#include "module.h"

#include <balancer/modules/cookie_policy/common/combined_policy.h>
#include <balancer/modules/cookie_policy/common/cookie_policy.cfgproto.pb.h>
#include <balancer/modules/cookie_policy/common/cookie_policy.h>
#include <balancer/modules/cookie_policy/common/controls_reader.h>
#include <balancer/modules/cookie_policy/common/mode_controls.h>
#include <balancer/modules/cookie_policy/common/parse_render.h>
#include <balancer/modules/cookie_policy/policies/default_yandex_policies.h>
#include <balancer/modules/cookie_policy/policies/policy_traits.h>

#include <balancer/kernel/client_hints/impl/client_hints.h>
#include <balancer/kernel/cookie/domain/domain.h>
#include <balancer/kernel/module/iface.h>
#include <balancer/kernel/module/module.h>

#include <util/generic/ptr.h>

using namespace NConfig;
using namespace NModCookiePolicy;

Y_TLS(cookie_policy) {
    TTls(const TCombinedPolicyTls& pTls, IWorkerCtl& ctl)
        : ModeControls_(pTls.Policy().Config.file_switch(), ctl)
        , GdprControls_(pTls.Policy().Config.gdpr_file_switch(), ctl)
        , PolicyTls(pTls, ctl)
    {
    }

    void ReReadControls(const TConnDescr& descr) {
        PolicyTls.SetModeControls(ModeControls_.ReRead(NAME, descr));
        PolicyTls.SetGdprControls(GdprControls_.ReRead(NAME, descr));
    }

public:
    TControlsReader<TModeControls, TModeControlsCfg> ModeControls_;
    TControlsReader<TGdprControls, TGdprControlsCfg> GdprControls_;
    TCombinedPolicyTls PolicyTls;
};

MODULE_WITH_TLS_BASE(cookie_policy, TModuleWithSubModule) {
    TModule(const TModuleParams& mp)
        : TModuleBase(mp)
    {
        InitDomains();

        GetCachedUATraits();

        auto cfg = ParseProtoConfig<TModuleConfig>(
            [&](const TString& key, NConfig::IConfig::IValue* value) {
                Submodule_.Reset(Loader->MustLoad(key, mp.Copy(value->AsSubConfig())).Release());
            }
        );

        Y_ENSURE_EX(Submodule_, TConfigParseError() << "no submodule configured");

        AddDefaultYandexPolicies(cfg);
        Policy_ = InitCombinedPolicy(cfg);
        PolicyTlsTemplate_.ConstructInPlace(Policy_, mp.Control->SharedStatsManager());
    }

private:
    bool DoExtraAccessLog() const override {
        return true;
    }

    THolder<TTls> DoInitTls(IWorkerCtl* ctl) override {
        return MakeHolder<TTls>(*PolicyTlsTemplate_, *ctl);
    }

    TError DoRun(const TConnDescr& descr, TTls& tls) const override {
        tls.ReReadControls(descr);
        descr.ExtraAccessLog << " u:" << Policy_.Config.uuid();

        if (auto ctx = tls.PolicyTls.ApplyToRequest(descr)) {
            TString log;
            Y_DEFER {
                if (log) {
                    descr.ExtraAccessLog << log;
                }
            };

            auto output = MakeHttpOutput([&](TResponse&& resp, const bool forceClose, TInstant deadline) {
                log = tls.PolicyTls.ApplyToResponse(*ctx, descr, resp);
                return descr.Output->SendHead(std::move(resp), forceClose, deadline);
            }, [&](TChunkList lst, TInstant deadline) {
                return descr.Output->Send(std::move(lst), deadline);
            }, [&](THeaders&& trailers, TInstant deadline) {
                return descr.Output->SendTrailers(std::move(trailers), deadline);
            });

            return Submodule_->Run(descr.CopyOut(output));
        } else {
            return Submodule_->Run(descr);
        }
    }

private:
    TCombinedPolicy Policy_;
    TMaybe<TCombinedPolicyTls> PolicyTlsTemplate_;
};


IModuleHandle* NModCookiePolicy::Handle() {
    return TModule::Handle();
}
