#include "domain_info.h"
#include "gdpr_controls.h"
#include "utils.h"

#include <balancer/kernel/cookie/domain/domain.h>
#include <balancer/kernel/cookie/obfuscation.h>

#include <util/generic/xrange.h>

namespace NModCookiePolicy {
    namespace {
        bool MatchDomainsRe(const TMaybe<TFsm>& domainsRe, const TDomainInfo& domainInfo) noexcept {
            if (!domainsRe || !domainInfo.Domain) {
                return false;
            }
            return Match(*domainsRe, *domainInfo.Domain)
                || domainInfo.DomainWcard && Match(*domainsRe, *domainInfo.DomainWcard);
        }

        bool IsUserConnSecure(const TConnDescr& descr) noexcept {
            if (descr.Properties->UserConnIsSsl) {
                return true;
            }
            for (const auto& value : descr.Request->Headers().GetValuesRef("X-Yandex-HTTPS")) {
                if (value.AsStringBuf() == "yes") {
                    return true;
                }
            }
            for (const auto& value : descr.Request->Headers().GetValuesRef("X-HTTPS-Request")) {
                if (value.AsStringBuf() == "yes") {
                    return true;
                }
            }
            return false;
        }
    }


    TDomainFilter::TDomainFilter(const TDomainFilterCfg& cfg)
        : IncludeDomainsRe_(MaybeRe(
            cfg.include_domains_re(),
            cfg.has_include_domains_re()
        ))
        , ExcludeDomainsRe_(MaybeRe(
            cfg.exclude_domains_re(),
            cfg.has_exclude_domains_re()
        ))
    {}

    bool TDomainFilter::Included(const TDomainInfo& domainInfo) const noexcept {
        return MatchDomainsRe(IncludeDomainsRe_, domainInfo);
    }

    bool TDomainFilter::Excluded(const TDomainInfo& domainInfo) const noexcept {
        return MatchDomainsRe(ExcludeDomainsRe_, domainInfo);
    }


    TDomainInfo GetDomainInfo(const TConnDescr& descr) noexcept {
        TDomainInfo res;

        res.IsSecure = IsUserConnSecure(descr);

        auto hostHeader = descr.Request->Headers().GetFirstValue("host");
        if (!hostHeader) {
            return res;
        }

        auto host = NCookie::StrToLower(NCookie::StripPort(hostHeader));

        if (!ValidDomainName(host)) {
            return res;
        }

        res.Domain = NCookie::RSkipDot(host);

        if (auto domainRoot = ShortestPrivateSuffix(*res.Domain); *domainRoot) {
            res.DomainRoot = std::string(*domainRoot);
            res.IsGdpr = GdprDomain(*domainRoot);
            res.DomainWcard = ReplacePublicSuffix(*res.Domain);
        } else {
            res.DomainWcard = *res.Domain;
        }

        return res;
    }
}

template <>
void Out<NModCookiePolicy::TDomainInfo>(IOutputStream& out, const NModCookiePolicy::TDomainInfo& di) {
    using namespace NSrvKernel::NCookie;
    out << "{.Domain="sv;
    PrintTo(out, di.Domain);
    out << ",.DomainRoot="sv;
    PrintTo(out, di.DomainRoot);
    out << ",.DomainWcard="sv;
    PrintTo(out, di.DomainWcard);
    out << ",.IsSecure="sv << di.IsSecure;
    out << ",.IsGdpr="sv << di.IsGdpr;
    out << "}"sv;
}
