#include "tvm.h"

#include <util/system/env.h>
#include <util/folder/path.h>
#include <util/folder/dirut.h>
#include <util/stream/file.h>

#include <library/cpp/logger/global/global.h>

#include <contrib/libs/yaml-cpp/include/yaml-cpp/yaml.h>

namespace NSaas {

void TTvmSettings::Merge(const TTvmSettings& settings) {
    CHECK_WITH_LOG(ClientId == settings.ClientId);
    CHECK_WITH_LOG(Secret == settings.Secret);
    for (auto&& destClient : settings.DestinationClients) {
        auto it = DestinationClients.find(destClient.first);
        if (it != DestinationClients.end()) {
            CHECK_WITH_LOG(it->second.Id == destClient.second.Id);
        } else {
            DestinationClients.insert(destClient);
        }
    }
}

template<class T>
void TryFillFromEnv(T& result, const TString& key) {
    if (!result) {
        TString value = GetEnv(key);
        if (value) {
            result = FromString<T>(value);
        }
    }
}

template<class T>
void TryFillFromYamlConfig(T& result, const YAML::Node& config, const TString& key) {
    if (!result) {
        auto value = config[key.c_str()];
        if (value.IsDefined() && value.IsScalar()) {
            result = FromString<T>(value.Scalar());
        }
    }
}

void TTvmConfig::Init(const TYandexConfig::Section& configSection) {
    TYandexConfig::TSectionsMap sections = configSection.GetAllChildren();
    const TYandexConfig::Directives& dirs = configSection.GetDirectives();

    dirs.GetValue("Secret", Secret);
    dirs.GetValue("ClientId", ClientId);

    dirs.GetValue("EnvKeySecret", EnvKeySecret);
    dirs.GetValue("EnvKeyClientId", EnvKeyClientId);

    dirs.GetValue("ConfigKeySecret", ConfigKeySecret);
    dirs.GetValue("ConfigKeyClientId", ConfigKeyClientId);
    dirs.GetValue("ConfigFilename", ConfigFilename);

    dirs.GetValue("DestinationAlias", DestinationAlias);
    dirs.GetValue("DestinationClientId", DestinationClientId);

    InitSettings();
}

TString TTvmConfig::ToString(const char* sectionName) const {
    TStringStream ss;
    ss << "<" << sectionName << ">" << Endl;
    ss << "Secret : " << Secret << Endl;
    ss << "ClientId : " << ClientId << Endl;

    ss << "EnvKeySecret : " << EnvKeySecret << Endl;
    ss << "EnvKeyClientId : " << EnvKeyClientId << Endl;

    ss << "ConfigKeySecret : " << ConfigKeySecret << Endl;
    ss << "ConfigKeyClientId : " << ConfigKeyClientId << Endl;
    ss << "ConfigFilename : " << ConfigFilename << Endl;

    ss << "DestinationAlias : " << DestinationAlias << Endl;
    ss << "DestinationClientId : " << DestinationClientId << Endl;
    ss << "</" << sectionName << ">" << Endl;
    return ss.Str();
}

bool TTvmConfig::IsFilled() const {
    if (Settings.Secret.empty()) {
        ERROR_LOG << "tvm secret must be non-empty" << Endl;
        return false;
    }
    if (!Settings.ClientId) {
        ERROR_LOG << "tvm client id must be non-empty" << Endl;
        return false;
    }
    if (HasIncorrectDestinations) {
        ERROR_LOG << "Has incorrect destinations" << Endl;
        return false;
    }
    if (Settings.DestinationClients.empty()) {
        return false;
    }
    for (auto& dst : Settings.DestinationClients) {
        if (dst.first.empty()) {
            ERROR_LOG << "tvm destination alias must be non-empty" << Endl;
            return false;
        }
    }
    return true;
}

TTvmSettings TTvmConfig::GetSettings() const {
    CHECK_WITH_LOG(IsFilled()) << "some of tvm setting are not specified";
    return Settings;
}

void TTvmConfig::InitSettings() {
    Settings.Secret = Secret;
    Settings.ClientId = ClientId;

    TryFillFromEnv(Settings.Secret, EnvKeySecret);
    TryFillFromEnv(Settings.ClientId, EnvKeyClientId);

    if (!Settings.Secret || !Settings.ClientId) {
        TFsPath path(ConfigFilename ? ConfigFilename : (GetHomeDir() + "/.saas/tvm.yaml"));
        if (path.IsFile()) {
            YAML::Node yamlConfig = YAML::Load(TUnbufferedFileInput(path).ReadAll());
            TryFillFromYamlConfig(Settings.Secret, yamlConfig, ConfigKeySecret);
            TryFillFromYamlConfig(Settings.ClientId, yamlConfig, ConfigKeyClientId);
        }
    }

    if (DestinationClientId) {
        Settings.DestinationClients = {{DestinationAlias, DestinationClientId}};
    } else {
        HasIncorrectDestinations = true;
        ERROR_LOG << "Incorrect destination pair (" << DestinationAlias << ":" << DestinationClientId << ")" << Endl;
    }
}

TTvmClientPtr CreateTvmClient(const TTvmSettings& settings, NTvmAuth::TLoggerPtr tvmLogger) {
    NTvmAuth::NTvmApi::TClientSettings tvmSettings;
    tvmSettings.SetSelfTvmId(settings.ClientId);
    auto dest = settings.DestinationClients;
    tvmSettings.EnableServiceTicketsFetchOptions(settings.Secret, std::move(dest));

    return std::make_shared<NTvmAuth::TTvmClient>(tvmSettings, tvmLogger);
}

};
