#include "action.h"

#include <library/cpp/json/json_reader.h>
#include <library/cpp/string_utils/quote/quote.h>

namespace {
    i32 ParseVersionFromDeployManagerFileUrl(const TStringBuf url) {
        const TString decodedUrl = CGIUnescapeRet(url);
        // Example:
        // {"generator":{"version":2950,"type":"plain","path":"\/configs\/ydo_search\/relev.conf-ydo_search"},"context":{"slot_info":{"slot":"saas-yp-cloud-ydo-search-12.vla.yp-c.yandex.net:80","disableindexing":false,"disablefetch":false,"servicetype":"rtyserver","disablesearch":false,"configtype":"default","isintsearch":false,"disablesearchfiltration":false,"shardmin":16383,"shardmax":32765,"service":"ydo_search","ctype":"stable"},"no_patch":false}}
        // {"generator":{"cluster_meta_version":20186,"default_config":{"version":81,"type":"plain","path":"/defaults/rtyserver.conf-default"},"rename":"rtyserver.conf-common","type":"rty_diff","diff":{"version":2778,"type":"plain","path":"/configs/ydo_search/rtyserver.diff-ydo_search"},"prefix":"Server."},"context":{"slot_info":{"slot":"saas-yp-cloud-ydo-search-12.vla.yp-c.yandex.net:80","disableindexing":false,"disablefetch":false,"servicetype":"rtyserver","disablesearch":false,"configtype":"default","isintsearch":false,"disablesearchfiltration":false,"shardmin":16383,"shardmax":32765,"service":"ydo_search","ctype":"stable"},"no_patch":false}}
        NJson::TJsonValue json;
        TStringInput si(decodedUrl);
        if (!NJson::ReadJsonTree(&si, &json)) {
            ERROR_LOG << "Failed to parse config as json:  " << decodedUrl << Endl;
            return -1;
        }
        const NJson::TJsonValue& generator = json["generator"];

        const TString& type = generator["type"].GetString();

        const NJson::TJsonValue* versionJson = nullptr;
        if (type == "plain") {
            versionJson = &generator["version"];
        } else if (type == "rty_diff") {
            versionJson = &generator["diff"]["version"];
        }

        if (versionJson == nullptr || !versionJson->IsInteger()) {
            ERROR_LOG << "Failed to find version: " << decodedUrl << Endl;
            return -1;
        }
        return versionJson->GetIntegerSafe();
    }
} // namespace

namespace NDaemonController {
    TListConfAction::TConfigFile::TConfigFile()
    {}

    TListConfAction::TConfigFile::TConfigFile(const NJson::TJsonValue& json)
        : Name(json["name"].GetString())
        , Url(json["url"].GetString())
        , Content(json["content"].GetString())
    {}

    void TListConfAction::TConfigFile::Serialize(NJson::TJsonValue& json) const {
        json.InsertValue("name", Name);
        json.InsertValue("url", Url);
        json.InsertValue("content", Content);
    }

    NJson::TJsonValue TListConfAction::DoSerializeToJson() const {
        NJson::TJsonValue result;
        result.InsertValue("slot", Slot);
        result.InsertValue("service", Service);
        result.InsertValue("service_type", ServiceType);
        result.InsertValue("ctype", CType);
        result.InsertValue("version", Version);
        result.InsertValue("tags", Tags);
        NJson::TJsonValue& files = result.InsertValue("files", NJson::JSON_ARRAY);
        for (const auto& f : Result)
            f.Serialize(files.AppendValue(NJson::JSON_MAP));
        return result;
    }

    void TListConfAction::DoDeserializeFromJson(const NJson::TJsonValue& json) {
        Slot = json["slot"].GetStringRobust();
        Service = json["service"].GetStringRobust();
        ServiceType = json["service_type"].GetStringRobust();
        CType = json["ctype"].GetStringRobust();
        Version = json["version"].GetStringRobust();
        Tags = json["tags"].GetStringRobust();
        Result.clear();
        for (const auto& f: json["files"].GetArray())
            Result.push_back(TConfigFile(f));
    }

    void TListConfAction::DoInterpretResult(const TString& result) {
        NJson::TJsonValue json;
        TStringInput si(result);
        if (!NJson::ReadJsonTree(&si, &json))
            Fail(result);
        else {
            for (const auto& f : json["files"].GetArray()) {
                TConfigFile cf;
                cf.Url = f["url"].GetString();
                cf.Name = f["rename"].GetString();
                cf.Version = ParseVersionFromDeployManagerFileUrl(cf.Url);
                Result.push_back(cf);
            }

            if (json.Has("slot_info")) {
                SlotInfoResult.Reset(new NSaas::TSlotInfo);
                if (!SlotInfoResult->DeserializeFromJson(json["slot_info"])) {
                    Fail(result);
                    return;
                }
            }

            Success("");
        }
    }

    TListConfAction::TFactory::TRegistrator<TListConfAction> TListConfAction::Registrator(LIST_CONF_ACTION_NAME);
}
