#pragma once

#include <yandex/maps/wiki/validator/common.h>
#include <yandex/maps/wiki/validator/result.h>
#include <yandex/maps/wiki/validator/validator_config.h>

#include <maps/wikimap/mapspro/libs/validator/check_meta.h>
#include <maps/wikimap/mapspro/libs/validator/loader/data_source.h>

#include <maps/libs/geolib/include/polygon.h>
#include <maps/libs/pgpool/include/pgpool3.h>
#include <yandex/maps/wiki/revision/common.h>

#include <memory>
#include <vector>
#include <list>
#include <set>

namespace maps {
namespace wiki {
namespace validator {

using CanceledChecker = std::function<bool(void)>;
using TCategories = std::set<TCategoryId>;

class ModuleInfo
{
public:
    ModuleInfo(
        std::string name,
        std::vector<TCheckId> checkIds,
        std::map<TCheckId, TCategories> checkToCategories);

    const std::string& name() const { return name_; }
    const std::vector<TCheckId>& checkIds() const { return checkIds_; }

    const std::set<TCategoryId>& categoriesByCheckId(
        const TCheckId& checkId) const;

private:
    std::string name_;
    std::vector<TCheckId> checkIds_;
    std::map<TCheckId, TCategories> checkToCategories_;
};

class Validator
{
public:
    Validator(const ValidatorConfig& validatorConfig);

    //TODO: enable by default and remove this option
    void enableCardinalityCheck()
    {
        checkCardinality_ = CheckCardinality::Yes;
    }

    //TODO: move these two functions to the ValidatorConfig class
    void initModules();
    const std::list<ModuleInfo>& modules() const { return moduleInfos_; }

    size_t checkThreadsCount() const { return checkThreadsCount_; }
    void setCheckThreadsCount(size_t value)
    {
        REQUIRE(value, "check workers count must be nonzero");
        checkThreadsCount_ = value;
    }

    void setIsCanceledChecker(CanceledChecker canceledChecker)
    {
        canceledChecker_ = std::move(canceledChecker);
    }

    ResultPtr run(
        const std::vector<TCheckId>& checks,
        pgpool3::Pool& pgPool,
        DBID branchId,
        DBID commitId);

    ResultPtr run(
        const std::vector<TCheckId>& checks,
        pgpool3::Pool& pgPool,
        DBID branchId,
        DBID commitId,
        geolib3::Polygon2 aoiGeom,
        std::string aoiCoverageDir,
        double aoiBuffer = ZERO_AOI_BUFFER);

    ResultPtr run(
        const std::vector<TCheckId>& checks,
        pgpool3::Pool& pgPool,
        DBID branchId,
        DBID commitId,
        const std::vector<DBID>& aoiObjectIds,
        std::string aoiCoverageDir,
        double aoiBuffer = ZERO_AOI_BUFFER);

    ResultPtr run(
        const std::vector<TCheckId>& checks,
        pgpool3::Pool& pgPool,
        DBID branchId,
        DBID commitId,
        const ObjectIdSet& objectIds);

private:
    const CheckMeta& check(const TCheckId& id) const;

    ResultPtr runImpl(
        const std::vector<TCheckId>& checks,
        pgpool3::Pool& pgPool,
        DBID branchId,
        DBID commitId,
        AreaOfInterest aoi);

    const ValidatorConfig& validatorConfig_;
    CheckCardinality checkCardinality_;
    std::list<ModuleInfo> moduleInfos_;
    std::map<TCheckId, CheckMeta> checksById_;
    size_t checkThreadsCount_;
    CanceledChecker canceledChecker_;
};

}
}
}
