#include "config.h"

#include <library/cpp/json/json_value.h>

#include <util/string/cast.h>

namespace NSaas {
    TRTYStorageOptions::TRTYStorageOptions() {
        SearchHost = "saas-searchproxy.search.yandex.net";
        SearchPort = 17000;
        IndexHost = "saas-indexerproxy.search.yandex.net";
        IndexPort = 80;
        SearchService = "dm_storage";
        Prefix = 1;
    }

    bool TMongoStorageOptions::DeserializeFromJson(const NJson::TJsonValue& value) {
        if (value.Has("db"))
            DB = value["db"].GetString();
        else
            return false;
        if (value.Has("uri"))
            Uri = value["uri"].GetString();
        else
            return false;
        if (value.Has("collection_prefix"))
            CollectionPrefix = value["collection_prefix"].GetString();
        else
            return false;
        return true;
    }

    NJson::TJsonValue TMongoStorageOptions::SerializeToJson() const {
        NJson::TJsonValue result;
        result.InsertValue("db", DB);
        result.InsertValue("uri", Uri);
        result.InsertValue("collection_prefix", CollectionPrefix);
        return result;
    }

    TString TMongoStorageOptions::GetActualCollection() const {
        return CollectionPrefix + "actual";
    }

    TString TMongoStorageOptions::GetHistoryCollection() const {
        return CollectionPrefix + "history";
    }

    void TMongoStorageOptions::Init(const TYandexConfig::Section& section) {
        const TYandexConfig::Directives& directives = section.GetDirectives();
        directives.GetValue("Uri", Uri);
        directives.GetValue("DB", DB);
        directives.GetValue("CollectionPrefix", CollectionPrefix);
    }

    TString TMongoStorageOptions::ToString() const {
        TStringStream ss;
        ss << "Uri: " << Uri << Endl;
        ss << "DB: " << DB << Endl;
        ss << "CollectionPrefix: " << CollectionPrefix << Endl;
        return ss.Str();
    }

    bool TStorageOptions::DeserializeFromJson(const NJson::TJsonValue& value) {
        if (value.Has("type")) {
            const NJson::TJsonValue& tValue = value["type"];
            if (tValue.IsInteger())
                Type = (TStorageType)tValue.GetInteger();
            else
                Type = FromString<TStorageType>(tValue.GetString());
        } else
            return false;

        if (value.Has("ct_lifetime"))
            ClusterTaskLifetimeSec = value["ct_lifetime"].GetInteger();
        else
            ClusterTaskLifetimeSec = 0;

        if (!value.Has("queue_name"))
            return false;
        QueueName = value["queue_name"].GetString();

        if (value.Has("rty") && !RTYStorage.DeserializeFromJson(value["rty"]))
            return false;
        if (value.Has("mongo") && !MongoOptions.DeserializeFromJson(value["mongo"]))
            return false;
        if (value.Has("locks") && !DistrLock.DeserializeFromJson(value["locks"]))
            return false;
        if (value.Has("local") && !LocalOptions.DeserializeFromJson(value["local"]))
            return false;
        if (value.Has("zoo") && !ZooOptions.DeserializeFromJson(value["zoo"]))
            return false;
        return true;
    }

    void TStorageOptions::Init(const TYandexConfig::Section& section) {
        const TYandexConfig::Directives& directives = section.GetDirectives();
        directives.GetValue("Type", Type);
        directives.GetValue("ClusterTaskLifetimeSec", ClusterTaskLifetimeSec);
        directives.GetValue("QueueName", QueueName);

        TYandexConfig::TSectionsMap sections = section.GetAllChildren();
        {
            TYandexConfig::TSectionsMap::const_iterator iter = sections.find("Zoo");
            if (iter != sections.end())
                ZooOptions.Init(*iter->second);
        }
        {
            TYandexConfig::TSectionsMap::const_iterator iter = sections.find("Local");
            if (iter != sections.end())
                LocalOptions.Init(*iter->second);
        }
        {
            TYandexConfig::TSectionsMap::const_iterator iter = sections.find("Mongo");
            if (iter != sections.end())
                MongoOptions.Init(*iter->second);
        }
        {
            TYandexConfig::TSectionsMap::const_iterator iter = sections.find("Saas");
            if (iter != sections.end())
                RTYStorage.Init(*iter->second);
        }
        {
            TYandexConfig::TSectionsMap::const_iterator iter = sections.find("DistrLocks");
            if (iter != sections.end())
                DistrLock.Init(*iter->second);
        }
    }

    namespace {
        inline void ToStreamSection(IOutputStream& so, const char* name, const TString& content) {
            so << "<" << name << ">" << Endl;
            so << content << Endl;
            so << "</" << name << ">" << Endl;
        }
    }

    TString TStorageOptions::ToString() const {
        TStringStream ss;
        ss << "<Storage>" << Endl;
        ss << "Type: " << Type << Endl;
        ss << "ClusterTaskLifetimeSec: " << ClusterTaskLifetimeSec << Endl;
        ss << "QueueName: " << QueueName << Endl;
        ToStreamSection(ss, "Zoo", ZooOptions.ToString());
        ToStreamSection(ss, "Local", LocalOptions.ToString());
        ToStreamSection(ss, "Mongo", MongoOptions.ToString());
        ToStreamSection(ss, "Saas", RTYStorage.ToString());
        ToStreamSection(ss, "DistrLocks", DistrLock.ToString());
        ss << "</Storage>" << Endl;
        return ss.Str();
    }

    NJson::TJsonValue TStorageOptions::SerializeToJson() const
    {
        NJson::TJsonValue result;
        result.InsertValue("type", ::ToString(Type));
        result.InsertValue("queue_name", QueueName);
        result.InsertValue("ct_lifetime", ClusterTaskLifetimeSec);
        result.InsertValue("rty", RTYStorage.SerializeToJson());
        result.InsertValue("zoo", ZooOptions.SerializeToJson());
        result.InsertValue("local", LocalOptions.SerializeToJson());
        result.InsertValue("mongo", MongoOptions.SerializeToJson());
        result.InsertValue("locks", DistrLock.SerializeToJson());
        return result;
    }

    TStorageOptions::TStorageOptions()
        : Type(ZOO)
        , ClusterTaskLifetimeSec(0)
    {}

    NJson::TJsonValue TRTYStorageOptions::SerializeToJson() const {
        NJson::TJsonValue result;
        result.InsertValue("prefix", Prefix);
        result.InsertValue("search_host", SearchHost);
        result.InsertValue("index_host", IndexHost);
        result.InsertValue("search_port", SearchPort);
        result.InsertValue("index_port", IndexPort);
        result.InsertValue("index_url", IndexUrl);
        result.InsertValue("search_service", SearchService);
        return result;
    }

#define READ_FIELD(json_name, field_name) if (value.Has(#json_name)) field_name = value[#json_name].GetStringRobust(); else return false;
    bool TRTYStorageOptions::DeserializeFromJson(const NJson::TJsonValue& value) {
        NJson::TJsonValue result;
        READ_FIELD(search_host, SearchHost);
        READ_FIELD(index_host, IndexHost);
        READ_FIELD(search_service, SearchService);
        READ_FIELD(index_url, IndexUrl);
        if (value.Has("prefix"))
            Prefix = value["prefix"].GetInteger();
        else
            return false;
        if (value.Has("search_port"))
            SearchPort = value["search_port"].GetInteger();
        else
            return false;

        if (value.Has("index_port"))
            IndexPort = value["index_port"].GetInteger();
        else
            return false;
        return true;
    }

    void TRTYStorageOptions::Init(const TYandexConfig::Section& section) {
        const TYandexConfig::Directives& directives = section.GetDirectives();
        directives.GetValue("IndexHost", IndexHost);
        directives.GetValue("IndexPort", IndexPort);
        directives.GetValue("IndexUrl", IndexUrl);
        directives.GetValue("SearchHost", SearchHost);
        directives.GetValue("SearchPort", SearchPort);
        directives.GetValue("SearchService", SearchService);
        directives.GetValue("Prefix", Prefix);
    }

    TString TRTYStorageOptions::ToString() const {
        TStringStream ss;
        ss << "IndexHost: " << IndexHost << Endl;
        ss << "IndexPort: " << IndexPort << Endl;
        ss << "IndexUrl: " << IndexUrl << Endl;
        ss << "SearchHost: " << SearchHost << Endl;
        ss << "SearchPort: " << SearchPort << Endl;
        ss << "SearchService: " << SearchService << Endl;
        ss << "Prefix: " << Prefix << Endl;
        return ss.Str();
    }

    NJson::TJsonValue TDistributedLockOptions::SerializeToJson() const {
        NJson::TJsonValue result;
        result.InsertValue("enabled", Enabled);
        result.InsertValue("disconnect_timeout", ::ToString(DisconnectTimeout));
        result.InsertValue("retry_lock_timeout", ::ToString(RetryLockTimeout));
        result.InsertValue("connect_attemps", ConnectAttemps);
        return result;
    }

    bool TDistributedLockOptions::DeserializeFromJson(const NJson::TJsonValue& value) {
        Enabled = value["enabled"].GetBooleanRobust();
        DisconnectTimeout = FromString<TDuration>(value["disconnect_timeout"].GetStringRobust());
        RetryLockTimeout = FromString<TDuration>(value["retry_lock_timeout"].GetStringRobust());
        ConnectAttemps = value["connect_attemps"].GetUIntegerRobust();
        return true;
    }

    void TDistributedLockOptions::Init(const TYandexConfig::Section& section) {
        const TYandexConfig::Directives& directives = section.GetDirectives();
//        directives.GetValue("Enabled", Enabled);
        directives.GetValue("DisconnectTimeout", DisconnectTimeout);
        directives.GetValue("RetryLockTimeout", RetryLockTimeout);
        directives.GetValue("ConnectAttemps", ConnectAttemps);
    }

    TString TDistributedLockOptions::ToString() const {
        TStringStream ss;
        ss << "Enabled: " << Enabled << Endl;
        ss << "DisconnectTimeout: " << DisconnectTimeout << Endl;
        ss << "RetryLockTimeout: " << RetryLockTimeout << Endl;
        ss << "ConnectAttemps: " << ConnectAttemps << Endl;
        return ss.Str();
    }


    bool TLocalStorageOptions::DeserializeFromJson(const NJson::TJsonValue& value) {
        if (!value.Has("root"))
            return false;
        Root = value["root"].GetString();
        FlushOnWrite = value.Has("flush_on_write") && value["flush_on_write"].GetBooleanRobust();
        return true;
    }


    NJson::TJsonValue TLocalStorageOptions::SerializeToJson() const {
        NJson::TJsonValue result;
        result.InsertValue("root", Root.GetPath());
        result.InsertValue("flush_on_write", FlushOnWrite);
        return result;
    }


    void TLocalStorageOptions::Init(const TYandexConfig::Section& section) {
        const TYandexConfig::Directives& directives = section.GetDirectives();
        directives.GetValue("Root", Root);
        directives.GetValue("FlushOnWrite", FlushOnWrite);
        if (Root.IsDefined())
            Root.Fix();
    }


    TString TLocalStorageOptions::ToString() const {
        TStringStream ss;
        ss << "Root: " << Root << Endl;
        ss << "FlushOnWrite: " << FlushOnWrite << Endl;
        return ss.Str();
    }

    bool TZooStorageOptions::DeserializeFromJson(const NJson::TJsonValue& value) {
        if (!value.Has("root"))
            return false;
        Root = value["root"].GetString();

        if (!value.Has("zoo_address"))
            return false;
        Address = value["zoo_address"].GetString();

        if (!value.Has("zoo_log_level"))
            LogLevel = 3; // LL_INFO
        else
            LogLevel = value["zoo_log_level"].GetInteger();

        return true;
    }

    NJson::TJsonValue TZooStorageOptions::SerializeToJson() const  {
        NJson::TJsonValue result;
        result.InsertValue("root", Root);
        result.InsertValue("zoo_address", Address);
        result.InsertValue("zoo_log_level", LogLevel);
        return result;
    }

    void TZooStorageOptions::Init(const TYandexConfig::Section& section) {
        const TYandexConfig::Directives& directives = section.GetDirectives();
        directives.GetValue("Root", Root);
        directives.GetValue("Address", Address);
        directives.GetValue("LogLevel", LogLevel);
    }


    TString TZooStorageOptions::ToString() const {
        TStringStream ss;
        ss << "Root: " << Root << Endl;
        ss << "Address: " << Address << Endl;
        ss << "LogLevel: " << LogLevel << Endl;
        return ss.Str();
    }

}
