#include "script.h"
#include <saas/deploy_manager/server/client/client.h>
#include <saas/deploy_manager/scripts/common/deploy/deploy_builder.h>
#include <saas/deploy_manager/scripts/common/get_slots_info/get_slot_info.h>
#include <saas/deploy_manager/scripts/cluster/cluster_task.h>
#include <saas/deploy_manager/modules/resources/resources.h>
#include <saas/util/external/dc.h>
#include <saas/util/json/json.h>
#include <library/cpp/json/json_writer.h>

#include <util/string/type.h>

namespace {
    // todo: duplicated in nanny module
    TString DcFromTags(const TString& tagsStr) {
        TVector<TString> tags = StringSplitter(tagsStr).SplitBySet(", ").SkipEmpty();
        TString dc = "";
        for (auto& tag : tags) {
            if (tag == "a_geo_sas") {
                dc = NRTYCluster::TDatacenter::SASMarker;
            } else if (tag == "a_geo_msk") {
                dc = NRTYCluster::TDatacenter::MSKMarker;
            } else if (tag == "a_geo_man") {
                dc = NRTYCluster::TDatacenter::MANMarker;
            } else if (tag == "a_geo_vla") {
                dc = NRTYCluster::TDatacenter::VLAMarker;
            }
        }
        return dc;
    }
}

namespace NRTYDeploy {
    bool TScriptListConf::Process(IDeployInfoRequest& request) {
        TFileContentGenerator::TContext context;
        const TCgiParameters& cgi = request.GetRD().CgiParam;
        if (cgi.Has("slot_info")) {
            if(!context.SlotInfo.DeserializeFromJson(NUtil::JsonFromString(cgi.Get("slot_info"))))
                ythrow yexception() << "invalid slot_info";
        } else {
            context.SlotInfo.CType = cgi.Get("ctype");
            context.SlotInfo.Service = cgi.Get("service");
            context.SlotInfo.ConfigType = "default";
            context.SlotInfo.ServiceType = cgi.Get("service_type");
            context.SlotInfo.Slot = request.GetRD().CgiParam.Get("slot");
            if (!!context.SlotInfo.Slot) {
                NSaas::TSlotInfo si = GetSlotInfo(context.SlotInfo.Slot, context.SlotInfo, request);

                bool siRewritten = false;
                if (cgi.Get("service") != "unused" && si.Service == "unused"
                    && context.SlotInfo.Slot.find(".yp") == TString::npos) {
                    TString contHostname = SplitString(context.SlotInfo.Slot, ":")[0];
                    const TString alternativeHost = TDatacenterUtil::Instance().GetRealHost(contHostname);

                    if (!!alternativeHost && alternativeHost != HOST_NOT_DEFINED
                        && alternativeHost != SplitString(context.SlotInfo.Slot, ":").front()) {
                        const TString alternativeSlot = alternativeHost + ":" + SplitString(context.SlotInfo.Slot, ":").back();
                        NSaas::TSlotInfo altSi = GetSlotInfo(alternativeSlot, context.SlotInfo, request);
                        if (altSi.Service != "unused") {
                            context.SlotInfo = altSi;
                            siRewritten = true;
                        }
                    }
                }
                if (!siRewritten) {
                    context.SlotInfo = si;
                }
            }
            if (!context.SlotInfo.Slot && (!context.SlotInfo.Service || !context.SlotInfo.CType)) {
                request.Output() << "HTTP/1.1 400 \r\n\r\n";
                request.Output() << "slot or service and ctype must be set";
                return false;
            }
        }
        TStringStream errors;
        NRTYDeployInfo::IDeployComponentInfo::TPtr component(NRTYDeployInfo::IDeployComponentInfo::TFactory::Construct(context.SlotInfo.ServiceType));
        if (!component) {
            request.Output() << "HTTP/1.1 400 \r\n\r\n";
            request.Output() << "Incorrect service type for slot: " << context.SlotInfo.ServiceType;
            return false;
        }
        if (context.SlotInfo.DC == "" || context.SlotInfo.DC == DC_NOT_DEFINED) {
            if (cgi.Has("tags")) {
                context.SlotInfo.DC = DcFromTags(cgi.Get("tags"));
            }
        }

        component->SetInfo(&request, context.SlotInfo.CType);

        NRTYDeployInfo::IDeployServiceInfo::TPtr builder = component->BuildServiceInfo(context.SlotInfo.Service);

        TString forceVersion = request.GetRD().CgiParam.Get("version");
        if (!forceVersion)
            forceVersion = IsTrue(request.GetRD().CgiParam.Get("last_versions")) ? "NEW" : "CURRENT";
        if (!builder->BuildFilesInfo(context, errors, forceVersion)) {
            request.Output() << "HTTP/1.1 400 \r\n\r\n";
            request.Output() << errors.Str();
            return false;
        }
        TStringStream out;
        NJson::TJsonWriter json(&out, true);
        json.OpenMap();
        const NJson::TJsonValue& files = builder->GetNodes().SerializeWithContext(context);
        json.Write("files", &files);
        NJson::TJsonValue slotInfoJson = context.SlotInfo.SerializeToJson();
        json.Write("slot_info", &slotInfoJson);
        json.CloseMap();
        json.Flush();
        request.Output() << "HTTP/1.1 200 \r\n\r\n";
        request.Output() << out.Str();
        return true;
    };

    IScript::TFactory::TRegistrator<TScriptListConf> TScriptListConf::Registrator("list_conf");
}
