#pragma once

#include "db_log.h"
#include "task_params.h"
#include "pg_helpers.h"

#include <yandex/maps/mds/mds.h>
#include <maps/libs/pgpool/include/pgpool3.h>
#include <yandex/maps/wiki/common/extended_xml_doc.h>
#include <yandex/maps/wiki/common/pgpool3_helpers.h>
#include <yandex/maps/wiki/common/string_utils.h>
#include <yandex/maps/wiki/revision/common.h>
#include <yandex/maps/wiki/tasks/database.h>

#include <filesystem>
#include <memory>
#include <set>
#include <string>
#include <unordered_map>
#include <vector>

namespace rev = maps::wiki::revision;
namespace fs = std::filesystem;

namespace maps::wiki::exporter {

const std::string DATASET_PATH_PREFIX = "export";

const std::string LOCK_NAME = "lock";

const std::string PUBLISHED_SUFFIX = "_pub";

const size_t TMP_SCHEMATA_TO_KEEP_DAYS = 1;

struct JsonPass
{
    std::string name;
    rev::DBID commitId;
    std::string fileNamePattern;
};

enum class RelatioType
{
    ToMaster,
    ToSlave
};

struct RelativeIdSource
{
    RelatioType relationType;
    std::string role;
    std::string relativeCategoryId;
};

struct ServiceCategoryAttrInfo
{
    std::string type;
    std::optional<std::string> defVal;
    std::optional<std::string> sourceAttrName;
    std::optional<RelativeIdSource> relativeIdSource;
};

using CategoryAttrNameToInfo =
    std::unordered_map<std::string, ServiceCategoryAttrInfo>;
using CategoriesMap = std::unordered_map<std::string, CategoryAttrNameToInfo>;

// Map from category to list of filters
// Each filter is a pair: attribute -> value
using CategoriesFiltersMap =
    std::unordered_map<std::string, std::vector<std::pair<std::string, std::string>>>;


using Region = mds_dataset::Region;
using Regions = mds_dataset::Regions;
extern const Region NO_REGION;


class ExportConfig {
public:
    ExportConfig(const TaskParams& taskParams, const common::ExtendedXmlDoc& configXml);

    const mds::Configuration& mdsConfig() const { return mdsConfig_; }
    mds::Configuration mdsConfig(const Region& region) const;
    Subset subset() const { return taskParams_.subset; }
    IsTested tested() const { return taskParams_.tested; }
    revision::DBID commitId() const { return taskParams_.commitId; }
    const std::string& branch() const { return taskParams_.branch; }
    uint64_t taskId() const { return taskParams_.taskId; }

    void checkCanceled() const { maps::wiki::exporter::checkCanceled(taskParams_); }

    pgpool3::Pool& mainPool() const { return mainPool_.pool(); }
    pgpool3::Pool& mdsPool() const { return mdsPool_ ? mdsPool_->pool() : mainPool(); }

    Logger& logger() const { return *logger_; }
    void setLogger(std::unique_ptr<Logger> logger);

    std::string schema(const Region& region) const;
    std::string datasetPath(const Region& region) const;

    const Regions& regions() const { return regions_; }
    void setRegions(const Regions& regions) { regions_ = regions; }

    const CategoriesMap& serviceCategories() const { return serviceCategories_; }
    const CategoriesMap& mrcCategories() const { return mrcCategories_; }
    const CategoriesFiltersMap& mrcFilters() const { return mrcFilters_; }
    const CategoriesMap& mrcPedestrianCategories() const { return mrcPedestrianCategories_; }
    const std::string& schemaPrefix() const { return schemaPrefix_; }
    const std::string& tag() const { return tag_; }

    void setJson2ymapsdfPath(const std::string& json2ymapsdfPath) { json2ymapsdfPath_ = json2ymapsdfPath; }
    void setJson2ymapsdfTransformXml(const std::string& transformCfg) { transformCfg_ = transformCfg; }
    const std::string& json2ymapsdfTransformXml() const { return transformCfg_; }
    std::string json2ymapsdfPath() const;

    bool keepJson() const { return keepJson_; }
    void keepJson(bool value) { keepJson_ = value; }

    bool mdsUploadEnabled() const { return mdsUploadEnabled_; }
    void disableMdsUpload() { mdsUploadEnabled_ = false; }

    const TaskParams& taskParams() const { return taskParams_; }

    std::string ymapsdfConnStr() const;

    void detectAndLockNearestExportDatabase();

    pgpool3::Pool& ymapsdfPool() const;

private:
    const TaskParams& taskParams_;
    const common::ExtendedXmlDoc& configXml_;
    bool keepJson_;

    std::string tag_;
    const mds::Configuration mdsConfig_;
    bool mdsUploadEnabled_ = true;
    std::string transformCfg_;

    std::optional<std::string> json2ymapsdfPath_;

    std::string schemaPrefix_;

    mutable common::PoolHolder mainPool_;
    std::unique_ptr<common::PoolHolder> mdsPool_;
    mutable std::unique_ptr<pgpool3::Pool> ymapsdfPool_;

    Regions regions_;
    const CategoriesMap serviceCategories_;
    const CategoriesMap mrcCategories_;
    const CategoriesFiltersMap mrcFilters_;
    const CategoriesMap mrcPedestrianCategories_;

    std::unique_ptr<Logger> logger_;
    std::unique_ptr<tasks::DatabaseLock> dbLock_;

    friend std::ostream& operator<<(std::ostream& out, const ExportConfig& cfg);
};


std::ostream& operator<<(std::ostream& out, const ExportConfig& cfg);


mds::Configuration makeMdsConfig(const common::ExtendedXmlDoc& configXml);
Regions readRegions(const common::ExtendedXmlDoc& configXml);
void updateMdsPath(mds::Configuration& mdsConfig, const Region& region);

} // namespace maps::wiki::exporter
