#include "p_persistent_lifetime.h"

#include <balancer/kernel/cookie/gdpr/lifetime.h>

namespace NModCookiePolicy {
    namespace {
        struct TExpiresRangeCacheTls final : public IPolicyCustomTls {
            TPolicyPersistentLifetimeCfg Cfg;
            TExpiresCache MaxAgeMin;
            TExpiresCache GdprMaxAgeMin;

        public:
            TExpiresRangeCacheTls(const TPolicyPersistentLifetimeCfg& cfg)
                : Cfg(cfg)
            {}

            TExpires Get(TInstant now, bool isGdpr) noexcept {
                if (isGdpr) {
                    return GdprMaxAgeMin.Get(now + TDuration::Seconds(Cfg.gdpr_max_age_min()));
                }
                return MaxAgeMin.Get(now + TDuration::Seconds(Cfg.max_age_min()));
            }

            THolder<IPolicyCustomTls> Clone(IWorkerCtl&) const noexcept override {
                return MakeHolder<TExpiresRangeCacheTls>(Cfg);
            }
        };

        TExpires ExpiresCache(IPolicyCustomTls* tls, TInstant now, bool isGdpr) noexcept {
            return static_cast<TExpiresRangeCacheTls*>(tls)->Get(now, isGdpr);
        }
    }

    TPolicyPersistentLifetime::TPolicyPersistentLifetime(TString name, const TPolicyPersistentLifetimeCfg& cfg)
        : TNameMatchPolicyBase(name, cfg.mode(), cfg.name_re(), false)
        , Cfg_(cfg)
    {
        Y_ENSURE_EX(Cfg_.max_age_min() > 0,
            TConfigParseError() << "max_age_min must be > 0");
        Y_ENSURE_EX(Cfg_.gdpr_max_age_min() > 0,
            TConfigParseError() << "gdpr_max_age_min must be > 0");
        Y_ENSURE_EX(Cfg_.gdpr_max_age_min() <= Cfg_.max_age_min(),
            TConfigParseError() << "gdpr_max_age_min must be <= max_age_min");
        Y_ENSURE_EX(Cfg_.gdpr_max_age_min() <= (NGdprCookie::MaxAllowedAge + TDuration::Days(2)).Seconds(), TConfigParseError() << "gdpr_max_age_min must be not greater than 2 years");
    }

    THolder<IPolicyCustomTls> TPolicyPersistentLifetime::NewTls(TString, TSharedStatsManager&) const noexcept {
        return MakeHolder<TExpiresRangeCacheTls>(Cfg_);
    }

    TMaybe<ICookiePolicy::TAction> TPolicyPersistentLifetime::ApplyToResponseCookie(
        TResponseCookieView c, const TPolicyArgs& args) const noexcept
    {
        const auto& cc = c.Const();
        const auto now = args.Descr.Properties->Start;
        const auto isGdpr = args.Descr.Properties->Gdpr.Value.IsGdpr;

        if (!cc.Expires && !cc.MaxAge) {
            return TAction{
                .Reasons={EResponseCookieReason::NotPersistentLifetime},
                .Action=ECookieAction::Drop,
            };
        }

        TAction errs;

        if (cc.MaxAge) {
            const auto minMaxAge = isGdpr ? Cfg_.gdpr_max_age_min() : Cfg_.max_age_min();

            if (*cc.MaxAge < minMaxAge) {
                errs.Reasons.Set(EResponseCookieReason::SmallLifetime);
                if (!args.DryRun) {
                    c.Mutable().MaxAge = minMaxAge;
                }
            }
        }

        if (cc.Expires) {
            auto expires = ExpiresCache(args.Tls, now, isGdpr);
            if (cc.Expires.Get() < expires) {
                errs.Reasons.Set(EResponseCookieReason::SmallLifetime);
                if (!args.DryRun) {
                    c.Mutable().Expires = expires;
                }
            }
        }

        if (errs.Reasons) {
            return errs;
        }

        return Nothing();
    }
}
