#include "format.h"

#include <library/cpp/json/writer/json_value.h>
#include <library/cpp/json/json_writer.h>
#include <util/stream/str.h>


TJugglerCheck::TJugglerCheck(const TString& service, const TString& ctype, const TString& aKey,
    const bool isUsers, const TString& commonTag, const TString& nameSpace, const TString& golemPrefix)
    : SaasService(service)
    , Ctype(ctype)
    , AKey(aKey)
    , IsUsers(isUsers)
    , CommonTag(commonTag)
    , Namespace(nameSpace)
    , GolemPrefix(golemPrefix)
    , Flaps(0, 0, 0)
    , GolovanId("")
    , Notifications("", "", 0, TSet<TString>())
    { };

TString TJugglerCheck::GolemHost() const {
    return GolemPrefix + "_" + Ctype + "_" + SaasService
        + (Notifications.GetSkipGolem() ? GOLEM_NOCALL_HOST_POSTFIX : "")
        + (IsUsers ? GOLEM_USERS_HOST_POSTFIX : "");
}

NJson::TJsonValue TJugglerCheck::Tags() const {
    NJson::TJsonValue result(NJson::JSON_ARRAY);
    result.AppendValue("saas_service_" + SaasService);
    result.AppendValue("saas_ctype_" + Ctype);
    result.AppendValue("saas_skey_" + AKey);
    result.AppendValue(CommonTag);
    result.AppendValue(IsUsers ? "saas_group_users" : "saas_group_dev");
    result.AppendValue(Notifications.GetSkipGolem() ? "saas_golem_off" : "saas_golem_on");
    return result;
}

NJson::TJsonValue TJugglerCheck::ToJson() const {
    NJson::TJsonValue result(NJson::JSON_MAP);

    result["host"] = GolemHost();
    result["service"] = "saas_" + Ctype + "_" + SaasService + "_" + AKey;
    result["refresh_time"] = 5;
    result["aggregator"] = "logic_or";

    result["tags"] = Tags();
    result["namespace"] = Namespace;

    result["children"] = NJson::TJsonValue(NJson::JSON_ARRAY);
    NJson::TJsonValue child(NJson::JSON_MAP);
    child["host"] = "yasm_alert";
    child["type"] = "HOST";
    child["instance"] = "";
    child["service"] = GolovanId;
    result["children"].AppendValue(child);

    NJson::TJsonValue urlYasm(NJson::JSON_MAP);
    urlYasm["type"] = "yasm_alert";
    urlYasm["title"] = "Golovan";
    urlYasm["url"] = "https://yasm.yandex-team.ru/alert/" + GolovanId;

    NJson::TJsonValue urlDm(NJson::JSON_MAP);
    urlDm["type"] = "dm_link";
    urlDm["title"] = "DM";
    urlDm["url"] = "https://saas-mon.n.yandex-team.ru/deploy?ctype=" + Ctype + "&service=" + SaasService;

    result["meta"] = NJson::TJsonValue(NJson::JSON_MAP);
    result["meta"]["urls"] = NJson::TJsonValue(NJson::JSON_ARRAY);
    result["meta"]["urls"].AppendValue(urlYasm);
    result["meta"]["urls"].AppendValue(urlDm);

    result["notifications"] = Notifications.ToJson();

    if (Flaps.IsOn()) {
        result["flaps"] = Flaps.ToJson();
    }
    return result;
}

TString TJugglerCheck::ToString() const {
    NJson::TJsonValue result = ToJson();
    TStringStream ss;
    NJson::WriteJson(&ss, &result);
    return ss.Str();
}

TJugglerCheck& TJugglerCheck::SetFlaps(const ui32 criticalTime, const ui32 stableTime, const ui32 boostTime) {
    Flaps = TFlaps(criticalTime, stableTime, boostTime);
    return *this;
}

TJugglerCheck& TJugglerCheck::SetGolovanId(const TString& golovanId) {
    GolovanId = golovanId;
    return *this;
}

TJugglerCheck& TJugglerCheck::SetIsUsers(const bool isUsers) {
    IsUsers = isUsers;
    return *this;
}

TJugglerCheck& TJugglerCheck::SetNotifications(const TString& phoneTimeStart, const TString& phoneTimeEnd,
    const ui32 calendarId, const TSet<TString>& calendarMethods) {
    Notifications = TNotifications(phoneTimeStart, phoneTimeEnd, calendarId, calendarMethods);
    return *this;
}

TJugglerCheck& TJugglerCheck::SetGolemTimes(const TString& phoneTimeStart, const TString& phoneTimeEnd) {
    Notifications.SetGolemTimes(phoneTimeStart, phoneTimeEnd);
    return *this;
}

TJugglerCheck& TJugglerCheck::SetCalendarTimes(const TString& phoneTimeStart, const TString& phoneTimeEnd) {
    Notifications.SetCalendarTimes(phoneTimeStart, phoneTimeEnd);
    return *this;
}

TJugglerCheck& TJugglerCheck::SetOwners(const TVector<TString>& owners) {
    Notifications.SetOwners(owners);
    return *this;
}

TJugglerCheck& TJugglerCheck::SetWarnUsers(const TVector<TString>& users) {
    Notifications.SetWarnUsers(users);
    return *this;
}

TJugglerCheck& TJugglerCheck::SetSkipGolem(const bool skipGolem) {
    Notifications.SetSkipGolem(skipGolem);
    return *this;
}

TJugglerCheck::TFlaps::TFlaps(const ui32 criticalTime, const ui32 stableTime, const ui32 boostTime)
    : CriticalTime(criticalTime)
    , StableTime(stableTime)
    , BoostTime(boostTime)
{};

NJson::TJsonValue TJugglerCheck::TFlaps::ToJson() const {
    NJson::TJsonValue result(NJson::JSON_MAP);
    result["critical_time"] = CriticalTime;
    result["stable_time"] = StableTime;
    result["boost_time"] = BoostTime;
    return result;
}

TJugglerCheck::TNotifications::TNotifications(const TString& phoneTimeStart, const TString& phoneTimeEnd,
    const ui32 calendarId, const TSet<TString>& calendarMethods)
    : PhoneGolemTimeStart(phoneTimeStart)
    , PhoneGolemTimeEnd(phoneTimeEnd)
    , CalendarId(calendarId)
    , CalendarMethods(calendarMethods)
    , PhoneCalendarTimeStart(phoneTimeStart)
    , PhoneCalendarTimeEnd(phoneTimeEnd)
    , SkipGolem(false)
{};

TJugglerCheck::TNotifications& TJugglerCheck::TNotifications::SetGolemTimes(
    const TString& phoneTimeStart, const TString& phoneTimeEnd) {
    PhoneGolemTimeStart = phoneTimeStart;
    PhoneGolemTimeEnd = phoneTimeEnd;
    return *this;
}

TJugglerCheck::TNotifications& TJugglerCheck::TNotifications::SetCalendarTimes(
    const TString& phoneTimeStart, const TString& phoneTimeEnd) {
    PhoneCalendarTimeStart = phoneTimeStart;
    PhoneCalendarTimeEnd = phoneTimeEnd;
    return *this;
}

TJugglerCheck::TNotifications& TJugglerCheck::TNotifications::SetOwners(const TVector<TString>& owners) {
    Owners = owners;
    return *this;
}

TJugglerCheck::TNotifications& TJugglerCheck::TNotifications::SetWarnUsers(const TVector<TString>& users) {
    WarnUsers = users;
    return *this;
}

TJugglerCheck::TNotifications& TJugglerCheck::TNotifications::SetSkipGolem(const bool skipGolem) {
    SkipGolem = skipGolem;
    return *this;
}

NJson::TJsonValue TJugglerCheck::TNotifications::ToJson() const {
    NJson::TJsonValue result(NJson::JSON_ARRAY);
    if (!SkipGolem) {
        NJson::TJsonValue golem(NJson::JSON_MAP);
        golem["template_name"] = "phone_escalation";
        golem["template_kwargs"] = NJson::TJsonValue(NJson::JSON_MAP);
        golem["description"] = "";
        golem["template_kwargs"]["logins"] = NJson::TJsonValue(NJson::JSON_ARRAY);
        for (auto& login : Owners) {
            golem["template_kwargs"]["logins"].AppendValue(login);
        }
        if (!!PhoneGolemTimeStart && !!PhoneGolemTimeEnd) {
            golem["template_kwargs"]["time_start"] = PhoneGolemTimeStart;
            golem["template_kwargs"]["time_end"] = PhoneGolemTimeEnd;
        }
        result.AppendValue(golem);
    }
    if (CalendarId > 0 && !!CalendarMethods) {
        const TString PHONE_METHOD = "phone";
        if (CalendarMethods.contains(PHONE_METHOD)) {
            NJson::TJsonValue calendar(NJson::JSON_MAP), kwargs(NJson::JSON_MAP);
            calendar["template_name"] = "on_status_change";
            kwargs["status"] = NJson::TJsonValue(NJson::JSON_ARRAY);
            kwargs["status"].AppendValue("CRIT");
            kwargs["calendar_id"] = CalendarId;
            kwargs["method"] =  NJson::TJsonValue(NJson::JSON_ARRAY);
            kwargs["method"].AppendValue(PHONE_METHOD);
            if (PhoneCalendarTimeStart != "" && PhoneCalendarTimeEnd != "") {
                kwargs["time_start"] = PhoneCalendarTimeStart;
                kwargs["time_end"] = PhoneCalendarTimeEnd;
            }
            calendar["template_kwargs"] = kwargs;
            result.AppendValue(calendar);
        }
        if (CalendarMethods.size() > 1 || !CalendarMethods.contains(PHONE_METHOD)) {
            NJson::TJsonValue calendar(NJson::JSON_MAP), kwargs(NJson::JSON_MAP);
            calendar["template_name"] = "on_status_change";
            kwargs["status"] = NJson::TJsonValue(NJson::JSON_ARRAY);
            kwargs["status"].AppendValue("CRIT");
            kwargs["calendar_id"] = CalendarId;
            kwargs["method"] =  NJson::TJsonValue(NJson::JSON_ARRAY);
            for (auto& method : CalendarMethods) {
                if (method != PHONE_METHOD) {
                    kwargs["method"].AppendValue(method);
                }
            }
            calendar["template_kwargs"] = kwargs;
            result.AppendValue(calendar);
        }
    }
    if (!!WarnUsers) {
        NJson::TJsonValue warn(NJson::JSON_MAP), kwargs(NJson::JSON_MAP);
        warn["template_name"] = "on_status_change";
        kwargs["status"] = NJson::TJsonValue(NJson::JSON_ARRAY);
        kwargs["status"].AppendValue("WARN");
        kwargs["method"] = NJson::TJsonValue(NJson::JSON_ARRAY);
        kwargs["method"].AppendValue("email");
        kwargs["login"] = NJson::TJsonValue(NJson::JSON_ARRAY);
        for (auto& login : WarnUsers) {
            kwargs["login"].AppendValue(login);
        }
        warn["template_kwargs"] = kwargs;
        result.AppendValue(warn);
    }
    return result;
}

TExistingCheck::TExistingCheck(const NJson::TJsonValue& json)
    :Json(json)
    {};

TString TExistingCheck::GetTagPart(const TString& tagBeg) const {
    for (const auto& tag : Json["tags"].GetArray()) {
        if (tag.GetString().StartsWith(tagBeg)) {
            return tag.GetString().substr(tagBeg.length());
        }
    }
    return "";
}

TString TExistingCheck::GetService() const {
    return GetTagPart("saas_service_");
}

TString TExistingCheck::GetCtype() const {
    return GetTagPart("saas_ctype_");
}

TString TExistingCheck::GetAKey() const {
    return GetTagPart("saas_skey_");
}

TString TExistingCheck::GetIsUsersTag() const {
    return "saas_group_" + GetTagPart("saas_group_");
}

bool TExistingCheck::GetIsUsers() const {
    TString userTagPart = GetTagPart("saas_group_");
    return (userTagPart == "users");
}

bool TExistingCheck::GetHasGolem() const {
    TString golemTagPart = GetTagPart("saas_golem_");
    return (golemTagPart == "on" || golemTagPart == "");
}

void TExistingCheck::GetFlaps(ui32& criticalTime, ui32& stableTime, ui32& boostTime) const {
    criticalTime = Json.Has("critical_time") ? Json["critical_time"].GetUInteger() : 0;
    stableTime = Json.Has("stable_time") ? Json["stable_time"].GetUInteger() : 0;
    boostTime = Json.Has("boost_time") ? Json["boost_time"].GetUInteger() : 0;
}

bool TExistingCheck::GetGolemTimes(TString& timeStart, TString& timeEnd) const {
    if (!Json.Has("notifications")) {
        return false;
    }
    for (const auto& ntf : Json["notifications"].GetArray()) {
        if (ntf["template_name"].GetString() == "phone_escalation") {
            timeStart = ntf["template_kwargs"]["time_start"].GetString();
            timeEnd = ntf["template_kwargs"]["time_end"].GetString();
            break;
        }
    }
    return true;
}

bool TExistingCheck::GetOwners(TVector<TString>& owners) const {
    if (!Json.Has("notifications")) {
        return false;
    }
    for (const auto& ntf : Json["notifications"].GetArray()) {
        if (ntf["template_name"].GetString() == "phone_escalation") {
            if (ntf["template_kwargs"].Has("logins")) {
                for (auto& login : ntf["template_kwargs"]["logins"].GetArray()) {
                    owners.push_back(login.GetString());
                }
                break;
            }
        }
    }
    return true;
}

bool TExistingCheck::GetWarnUsers(TVector<TString>& users) const {
    if (!Json.Has("notifications")) {
        return false;
    }
    for (const auto& ntf : Json["notifications"].GetArray()) {
        if (ntf["template_name"].GetString() == "on_status_change") {
            if (ntf["template_kwargs"].Has("login")) {
                for (auto& login : ntf["template_kwargs"]["login"].GetArray()) {
                    users.push_back(login.GetString());
                }
                break;
            }
        }
    }
    return true;
}
