#include "default_yandex_policies.h"
#include "policy_traits.h"

#include <balancer/modules/cookie_policy/common/cookie_policy.cfgproto.pb.h>
#include <balancer/modules/cookie_policy/common/cookie_policy.h>

#include <library/cpp/config/config.h>
#include <library/cpp/resource/resource.h>

namespace NModCookiePolicy {
    using NConfig::TConfigParseError;

    namespace {
        EPolicyMode StableFix(EYandexPolicyMode mode) {
            return IsIn({EYandexPolicyMode::StableFix, EYandexPolicyMode::Unstable, EYandexPolicyMode::FixAll}, mode)
                ? EPolicyMode::Fix : EPolicyMode::DryRun;
        }
        EPolicyMode UnstableFix(EYandexPolicyMode mode) {
            return IsIn({EYandexPolicyMode::Unstable, EYandexPolicyMode::FixAll}, mode)
                ? EPolicyMode::Fix : EPolicyMode::DryRun;
        }
        EPolicyMode DryRun(EYandexPolicyMode mode) {
            return (EYandexPolicyMode::FixAll == mode)
                ? EPolicyMode::Fix : EPolicyMode::DryRun;
        }
        EPolicyMode Off(EYandexPolicyMode mode) {
            return (EYandexPolicyMode::FixAll == mode)
                ? EPolicyMode::Fix : EPolicyMode::Off;
        }

        template <class TBuilder>
        void InsertYandexPolicy(TModuleConfig& cfg, TString name, TBuilder&& builder) {
            auto polCfg = builder(cfg.default_yandex_policies());
            if (!polCfg) {
                return;
            }
            auto&& m = GetPoliciesMap<decltype(*polCfg)>(cfg);
            if (!m.contains(name)) {
                m.emplace(name, *polCfg);
            }
        }
    }

    void AddDefaultYandexPolicies(TModuleConfig& cfg) {
        if (cfg.default_yandex_policies() == EYandexPolicyMode::Off) {
            return;
        }

        if (!cfg.has_parser_mode()) {
            cfg.set_parser_mode(UnstableFix(cfg.default_yandex_policies()));
        }

        if (!cfg.has_file_switch()) {
            cfg.set_file_switch("./controls/cookie_policy_modes");
        }

        if (!cfg.has_gdpr_file_switch()) {
            cfg.set_gdpr_file_switch("./controls/cookie_policy_gdpr");
        }

        InsertYandexPolicy(cfg, "is_gdpr", [](EYandexPolicyMode mode) -> TMaybe<TPolicyIsGdprCookieCfg> {
            if (!IsIn({EYandexPolicyMode::FixAll, EYandexPolicyMode::Unstable}, mode)) {
                return Nothing();
            }
            TPolicyIsGdprCookieCfg pol;
            pol.set_mode(UnstableFix(mode));
            return pol;
        });

        InsertYandexPolicy(cfg, "is_gdpr_b", [](EYandexPolicyMode mode) -> TMaybe<TPolicyIsGdprBCookieCfg> {
            if (!IsIn({EYandexPolicyMode::FixAll, EYandexPolicyMode::Unstable}, mode)) {
                return Nothing();
            }
            TPolicyIsGdprBCookieCfg pol;
            pol.set_mode(UnstableFix(mode));
            return pol;
        });

        InsertYandexPolicy(cfg, "eprivacy_client", [](EYandexPolicyMode mode) -> TMaybe<TPolicyGdprClientCookieCfg> {
            TPolicyGdprClientCookieCfg pol;
            pol.set_mode(UnstableFix(mode));
            return pol;
        });

        InsertYandexPolicy(cfg, "eprivacy_server", [](EYandexPolicyMode mode) -> TMaybe<TPolicyGdprServerCookieCfg> {
            TPolicyGdprServerCookieCfg pol;
            pol.set_mode(UnstableFix(mode));
            return pol;
        });

        InsertYandexPolicy(cfg, "eprivacy_lifetime", [](EYandexPolicyMode mode) -> TMaybe<TPolicyGdprLifetimeCfg> {
            TPolicyGdprLifetimeCfg pol;
            pol.set_mode(StableFix(mode));
            return pol;
        });

        InsertYandexPolicy(cfg, "passport_protected", [](EYandexPolicyMode mode) -> TMaybe<TPolicyProtectedCookieCfg> {
            TPolicyProtectedCookieCfg pol;
            pol.set_name_re(JoinStrings({
                TString("sessguard"),
                TString("Session_id"),
                TString("mda_beacon"),
                TString("yandex_login"),
                TString("lah"),
                TString("ilahu"),
            }, "|"));
            pol.set_mode(StableFix(mode));

            TDomainFilterCfg dfCfg;
            dfCfg.set_include_domains_re("(.*[.])?yandex[.]TLD");
            dfCfg.set_exclude_domains_re("(.*[.])?passport[.]yandex[.]TLD");
            pol.set_domain_filter(dfCfg);
            return pol;
        });

        InsertYandexPolicy(cfg, "gdpr_protected", [](EYandexPolicyMode mode) -> TMaybe<TPolicyProtectedCookieCfg> {
            TPolicyProtectedCookieCfg pol;
            pol.set_name_re(JoinStrings({
                TString("gdpr"),
                TString("gdpr_popup"),
                TString("is_gdpr"),
                TString("is_gdpr_b")
            }, "|"));
            pol.set_mode(DryRun(mode));
            return pol;
        });

        InsertYandexPolicy(cfg, "yandex_ssn", [&](EYandexPolicyMode mode) -> TMaybe<TPolicySameSiteNoneCfg> {
            TPolicySameSiteNoneCfg pol;
            pol.set_name_re(JoinStrings({
                {"i"},
                {"my"},
                {"sc_[0-9]+"},
                {"yabs-frequency"},
                {"yandex_gid"},
                {"yandexuid"},
                {"yclid_[0-9]+"},
                {"yp"},
                {"yc"},
                {"ys"},
            }, "|"));
            pol.set_mode(StableFix(mode));
            return pol;
        });

        InsertYandexPolicy(cfg, "yandex_js", [&](EYandexPolicyMode mode) -> TMaybe<TPolicyHttpOnlyFalseCfg> {
            TPolicyHttpOnlyFalseCfg pol;
            pol.set_name_re(JoinStrings({
                {"my"},
                {"yandex_gid"},
                {"yandexuid"},
                {"yp"},
                {"yc"},
                {"ys"},
            }, "|"));
            pol.set_mode(StableFix(mode));
            return pol;
        });

        InsertYandexPolicy(cfg, "yandex_sec", [&](EYandexPolicyMode mode) -> TMaybe<TPolicySecureTrueCfg> {
            TPolicySecureTrueCfg pol;
            pol.set_name_re(JoinStrings({
                {"i"},
                {"yandexuid"},
            }, "|"));
            pol.set_mode(UnstableFix(mode));

            TDomainFilterCfg dfCfg;
            dfCfg.set_include_domains_re("(.*[.])?yandex[.]TLD");
            pol.set_domain_filter(dfCfg);
            return pol;
        });

        InsertYandexPolicy(cfg, "yandex_root", [&](EYandexPolicyMode mode) -> TMaybe<TPolicyGlobalScopeCfg> {
            TPolicyGlobalScopeCfg pol;
            pol.set_name_re(JoinStrings({
                {"i"},
                {"my"},
                {"yandex_gid"},
                {"yandexuid"},
                {"yp"},
                {"yc"},
                {"ys"},
            }, "|"));
            pol.set_mode(UnstableFix(mode));
            return pol;
        });

        InsertYandexPolicy(cfg, "yandexuid_life", [&](EYandexPolicyMode mode) -> TMaybe<TPolicyPersistentLifetimeCfg> {
            TPolicyPersistentLifetimeCfg pol;
            pol.set_name_re("yandexuid");
            pol.set_mode(DryRun(mode));
            pol.set_max_age_min(TDuration::Days(9 * 365).Seconds());
            pol.set_gdpr_max_age_min(TDuration::Days(350).Seconds());
            return pol;
        });

        InsertYandexPolicy(cfg, "yandexuid_val", [](EYandexPolicyMode mode) -> TMaybe<TPolicyYandexuidValueCfg> {
            TPolicyYandexuidValueCfg pol;
            pol.set_allow_change(false);
            pol.set_mode(DryRun(mode));
            return pol;
        });

        InsertYandexPolicy(cfg, "yandexuid_new", [](EYandexPolicyMode mode) -> TMaybe<TPolicyYandexuidCreateCfg> {
            TPolicyYandexuidCreateCfg pol;
            pol.set_mode(Off(mode));
            return pol;
        });
    }
}
