#pragma once

#include "toloka/platform.h"
#include <maps/wikimap/mapspro/services/mrc/libs/common/include/ecstatic_client.h>
#include <maps/wikimap/mapspro/services/mrc/libs/db/include/toloka/task_type_info.h>
#include <maps/wikimap/mapspro/libs/common/include/yandex/maps/wiki/common/pgpool3_helpers.h>

#include <maps/libs/cmdline/include/cmdline.h>
#include <maps/libs/enum_io/include/enum_io_fwd.h>
#include <maps/libs/http/include/http.h>
#include <maps/libs/mds-client/include/yandex/maps/mds/mds.h>
#include <maps/libs/pgpool/include/pgpool3.h>
#include <maps/libs/vault_boy/include/secrets.h>
#include <maps/libs/xml/include/xml.h>
#include <maps/libs/common/include/environment.h>

#include <mapreduce/yt/interface/client.h>

#include <optional>

namespace maps {
namespace mrc {
namespace common {

static const std::string CORE_DB_ID = "core";
static const std::string CORE_POOL_ID = "core";
static const std::string LONG_READ_DB_ID = "long-read";
static const std::string LONG_READ_POOL_ID = "long-read";

using SharedXmlDoc = std::shared_ptr<xml3::Doc>;
using SharedPool = std::shared_ptr<wiki::common::PoolHolder>;

class TolokaTaskConfig {
public:
    explicit TolokaTaskConfig(const xml3::Node& node);

    const std::string& templatePoolId() const { return templatePoolId_; }

    size_t poolSize() const { return poolSize_; }

    size_t suiteSize() const { return suiteSize_; }

    size_t goldenTasksCount() const { return goldenTasksCount_; }

private:
    std::string templatePoolId_;
    size_t poolSize_;
    size_t suiteSize_;
    size_t goldenTasksCount_;
};

class TolokaConfig {
public:
    explicit TolokaConfig(const xml3::Node& node);

    const std::string& host() const { return host_; }

    const std::string& authHeader() const { return authHeader_; }

    std::optional<TolokaTaskConfig> taskConfig(db::toloka::TaskType taskType) const;

private:
    std::string host_;
    std::string authHeader_;
    std::unordered_map<db::toloka::TaskType, TolokaTaskConfig> taskTypeToConfigMap_;
};

class SignalsUploaderConfig {
public:
    explicit SignalsUploaderConfig(const xml3::Node& node);

    const std::string& queuePath() const { return queuePath_; }

private:
    std::string queuePath_;
};

class YtConfig {
public:
    explicit YtConfig(const xml3::Node& xml);

    const std::string& token() const { return token_; }

    const std::string& cluster() const { return cluster_; }

    const std::string& path() const { return path_; }

    NYT::IClientPtr makeClient() const;

    class Panoramas {
    public:
        explicit Panoramas(const xml3::Node& xml);

        const std::string& exportDir() const;

    private:
        std::string exportDir_;
    };

    const Panoramas& panoramas() const;

private:
    std::string token_;
    std::string cluster_;
    std::string path_;
    Panoramas panoramas_;
};

class ClickHouseConfig {
public:
    explicit ClickHouseConfig(const xml3::Node& xml);

    const std::string& user() const { return user_; }
    const std::string& password() const { return password_; }

    const std::string& readHost() const { return readHost_; }
    const std::vector<std::string>& writeHosts() const { return writeHosts_; }

    const std::string database() const { return database_; }

private:
    std::string database_;
    std::string user_;
    std::string password_;
    std::string readHost_;
    std::vector<std::string> writeHosts_;
};

class TakeoutConfig {
public:
    explicit TakeoutConfig(const xml3::Node& xml);

    uint32_t ownTvmServiceId() const { return ownTvmServiceId_; }
    uint32_t dstTvmServiceId() const { return dstTvmServiceId_; }
    const std::string& tvmSecret() const { return tvmSecret_; }
    const http::URL& url() const { return url_; }

private:
    uint32_t ownTvmServiceId_;
    uint32_t dstTvmServiceId_;
    std::string tvmSecret_;
    http::URL url_;
};

class EyeTvmConfig {
public:
    explicit EyeTvmConfig(const xml3::Node& xml);

    uint32_t selfTvmServiceId() const { return selfTvmServiceId_; }
    uint32_t panoramaDesciptionTvmServiceId() const { return panoDescTvmServiceId_; }
    std::optional<std::string> tvmSecret() const { return tvmSecret_; }

private:
    uint32_t selfTvmServiceId_;
    uint32_t panoDescTvmServiceId_;
    std::optional<std::string> tvmSecret_;
};

class NirvanaConfig {
public:
    explicit NirvanaConfig(const xml3::Node& node);

    const std::string& oauthToken() const { return oauthToken_; }

private:
    std::string oauthToken_;
};

class NexarConfig {
public:
    explicit NexarConfig(const xml3::Node& node);
    const std::string& host() const { return host_; }
    const std::string& refreshToken() const { return refreshToken_; }
private:
    std::string host_;
    std::string refreshToken_;
};

class WikiAclConfig {
public:
    explicit WikiAclConfig(const xml3::Node& node);
    const std::string& host() const { return host_; }
    int tvmId() const { return tvmId_; }
private:
    std::string host_;
    int tvmId_;
};

class ExternalServicesConfig {
public:
    explicit ExternalServicesConfig(const xml3::Node& node);

    const http::URL& blackboxUrl() const { return blackboxUrl_; }

    const http::URL& nmapsUrl() const { return nmapsUrl_; }

    const http::URL& socialBackofficeUrl() const { return socialBackofficeUrl_; }

    const http::URL& mrcBrowserUrl() const { return mrcBrowserUrl_; }

    const http::URL& mrcBrowserProUrl() const { return mrcBrowserProUrl_; }

    const http::URL& s3MdsUrl() const { return s3MdsUrl_; }

    const std::string& s3MdsPublicReadHost() const { return s3MdsPublicReadHost_; }

    const http::URL& yavisionUrl() const { return yavisionUrl_; }

    const std::string& geoIdCoveragePath() const { return geoIdCoveragePath_; }

    const YtConfig& yt() const { return ytConfig_; }

    const ClickHouseConfig& clickHouseConfig() const { return clickHouseConfig_; }

    const TakeoutConfig& takeoutConfig() const { return takeoutConfig_; }

    const EyeTvmConfig& eyeTvmConfig() const {return eyeTvmConfig_; }

    const NirvanaConfig& nirvana() const { return nirvana_; }

    const http::URL& mapsCoreUgcBackofficeUrl() const { return mapsCoreUgcBackofficeUrl_; }

    const http::URL& mapsCoreNmapsMrcUgcBackUrl() const { return mapsCoreNmapsMrcUgcBackUrl_; }

    const std::string firmwareUpdaterHost() const { return firmwareUpdaterHost_; }

    const std::string wikiEditorHost() const { return wikiEditorHost_; }

    const std::optional<maps::common::Environment>& ecstaticEnvironmentOverride() const {
        return ecstaticEnvironmentOverride_;
    }

    const http::URL& stvdescrUrl() const { return stvdescrUrl_; }

    const NexarConfig& nexar() const { return nexar_; }

    const WikiAclConfig& wikiAcl() const { return wikiAcl_; }

    const http::URL& geosearchUrl() const { return geosearchUrl_; }

private:
    std::optional<maps::common::Environment> loadEcstaticEnvironmentEverride(const xml3::Node& node) const;

    http::URL blackboxUrl_;
    http::URL nmapsUrl_;
    http::URL socialBackofficeUrl_;
    http::URL mrcBrowserUrl_;
    http::URL mrcBrowserProUrl_;
    http::URL s3MdsUrl_;
    std::string s3MdsPublicReadHost_;
    http::URL yavisionUrl_;
    std::string geoIdCoveragePath_;
    YtConfig ytConfig_;
    ClickHouseConfig clickHouseConfig_;
    TakeoutConfig takeoutConfig_;
    EyeTvmConfig eyeTvmConfig_;
    NirvanaConfig nirvana_;
    http::URL mapsCoreUgcBackofficeUrl_;
    http::URL mapsCoreNmapsMrcUgcBackUrl_;
    std::optional<maps::common::Environment> ecstaticEnvironmentOverride_;
    http::URL stvdescrUrl_;
    std::string firmwareUpdaterHost_;
    std::string wikiEditorHost_;
    NexarConfig nexar_;
    WikiAclConfig wikiAcl_;
    http::URL geosearchUrl_;
};

class TaxiImportConfig {
public:
    explicit TaxiImportConfig(const xml3::Node& xml);

    const std::string& ytCluster() const { return ytCluster_; }
    const std::string& ytEventsPath() const { return ytEventsPath_; }
    const std::string& ytTracksPath() const { return ytTracksPath_; }
    const std::string& ytToken() const { return ytToken_; }

private:
   std::string ytCluster_;
   std::string ytEventsPath_;
   std::string ytTracksPath_;
   std::string ytToken_;
};

enum class ServiceRole {
    DataGenerator = 0,
    DataConsumer,
};

DECLARE_ENUM_IO(ServiceRole);

class Config {
public:
    /// Reads unittest config from embedded resource.
    static Config forTests();

    /// Reads config from file.
    explicit Config(const std::string& path);

    /// Reads config template from embedded resource and renders it.
    explicit Config(const vault_boy::Context& ctx, ServiceRole role = ServiceRole::DataGenerator);

    /// Reads config template from file and render it.
    explicit Config(const vault_boy::Context& ctx, const std::string& path);

    /// Constructs config from the given xml document.
    Config(SharedXmlDoc xml);

    /// Constructs config from the given string.
    static Config fromString(const std::string& config);

    std::string toString() const;

    wiki::common::PoolHolder makePoolHolder(
        const std::string& dbId = CORE_DB_ID, const std::string& poolId = CORE_POOL_ID) const;

    SharedPool makeSharedPool(
        const std::string& dbId = CORE_DB_ID, const std::string& poolId = CORE_POOL_ID) const;

    const mds::Configuration& makeMdsConfiguration() const { return mdsConfiguration_; }

    mds::Mds makeMdsClient() const { return mds::Mds{mdsConfiguration_}; }

    const mds::Configuration& makePublicMdsConfiguration() const { return publicMdsConfiguration_; }

    mds::Mds makePublicMdsClient() const { return mds::Mds{publicMdsConfiguration_}; }

    const TolokaConfig& crowdPlatformConfig(db::toloka::Platform platform) const
    {
        return crowdPlatformToConfig_.at(platform);
    }

    const SignalsUploaderConfig& signalsUploader() const { return signalsUploader_; }

    std::string rideReportsEmailAddress() const;

    const ExternalServicesConfig& externals() const { return externals_; }

    const TaxiImportConfig& taxiImportConfig() const { return taxiImportConfig_; }

    void enableTvmClient();

private:
    SharedXmlDoc xml_;
    mds::Configuration mdsConfiguration_;
    mds::Configuration publicMdsConfiguration_;
    std::map<db::toloka::Platform, TolokaConfig> crowdPlatformToConfig_;
    SignalsUploaderConfig signalsUploader_;
    ExternalServicesConfig externals_;
    TaxiImportConfig taxiImportConfig_;
};

/// Default directory with secrets for config templates.
/// `/etc/yandex/maps/maps-core-mrc-config/keys`
/// Keys are delivered by `yandex-maps-mrc-secrets` package.
const std::string KEYS_PATH = vault_boy::defaultPath("maps-core-mrc-config");

inline Config templateConfigFromCmdPath(
    const cmdline::Option<std::string>& configPath,
    ServiceRole role = ServiceRole::DataGenerator)
{
    vault_boy::DirectoryContext ctx{KEYS_PATH};
    if (configPath.defined()) {
        return Config{ctx, configPath};
    }
    return Config{ctx, role};
}

inline Config templateConfigFromCmdPath(const cmdline::Option<std::string>& secretVersion,
                                        const cmdline::Option<std::string>& configPath,
                                        ServiceRole role = ServiceRole::DataGenerator) {
    return secretVersion.defined()
        ? Config{maps::vault_boy::loadContextWithYaVault(secretVersion),
                 configPath}
        : templateConfigFromCmdPath(configPath, role);
}


inline Config templateConfig(ServiceRole role = ServiceRole::DataGenerator) {
    return Config{vault_boy::DirectoryContext{KEYS_PATH}, role};
}

} // namespace common
} // namespace mrc
} // namespace maps
