#pragma once

#include <balancer/kernel/funcs/funcs.h>
#include <balancer/kernel/module/iface.h>
#include <balancer/kernel/http/parser/http.h>

#include <util/generic/ptr.h>
#include <util/generic/string.h>
#include <util/generic/variant.h>


namespace NModHeaders {
    using namespace NSrvKernel;

    enum class EHeaderFunc {
        ReqId       /* "reqid" */,
        MarketReqId /* "market_reqid" */,
        AdfoxReqId  /* "adfox_reqid" */,
        SearchReqId /* "search_reqid" */,
        RealIp      /* "realip" */,
        RealPort    /* "realport" */,
        Hostname    /* "hostname" */,
        LocalIp     /* "localip" */,
        LocalPort   /* "localport" */,
        Url         /* "url" */,
        Location    /* "location" */,
        Host        /* "host" */,
        Yuid        /* "yuid" */,
        Scheme      /* "scheme" */,
        Proto       /* "proto" */,
        SslClientCertCn             /* "ssl_client_cert_cn" */,
        SslClientCertSubject        /* "ssl_client_cert_subject" */,
        SslClientCertVerifyResult   /* "ssl_client_cert_verify_result" */,
        SslClientCertSerialNumber   /* "ssl_client_cert_serial_number" */,
        SslClientHandshakeInfo      /* "ssl_handshake_info" */,
        SslEarlyData                /* "ssl_early_data" */,
        SslTicketName               /* "ssl_ticket_name" */,
        SslTicketIV                 /* "ssl_ticket_iv" */,
        ExpStatic   /* "exp_static" */,
        TcpInfo     /* "tcp_info" */,
        Ja3         /* "ja3" */,
        Ja4         /* "ja4" */,
        Time        /* "time:x" */,
        P0f         /* "p0f" */,
        Traceparent /* "traceparent" */,
    };

    struct TSignedDuration {
        TDuration Value;
        bool Negative = false;

        TSignedDuration(TDuration value, bool negative)
            : Value(value)
            , Negative(negative)
        {};
    };

    struct TValueFromFile {
        TString FileName_;
    };

    using TCreateHeaderParameters = std::variant<TString, TSignedDuration>;
    using TCreateHeaderVal = std::variant<TString, EHeaderFunc, ECommonFunc, TValueFromFile>;

    struct TCreateHeader {
    private:
        const TString Name_;
        const TCreateHeaderVal Value_;
        const TCreateHeaderParameters Parameters_;
        TString Delimiter_;
    public:
        TCreateHeader(TString name, TCreateHeaderVal val, TCreateHeaderParameters params, TStringBuf delim)
            : Name_(name)
            , Value_(val)
            , Parameters_(std::move(params))
        {
            const auto* headerFunc = std::get_if<EHeaderFunc>(&Value_);
            if (delim.Empty() && headerFunc && *headerFunc == EHeaderFunc::MarketReqId) {
                Delimiter_ = "/";
            } else if (delim.Empty()) {
                Delimiter_ = ", ";
            } else {
                Delimiter_ = delim;
            }
        }

        const TString& GetName() const {
            return Name_;
        }

        TString GenValue(const NSrvKernel::TConnDescr& descrRequest, const THashMap<TString, TSharedFileReReader>& files) const noexcept;

        bool AllowEmpty() const noexcept;

        TString GenDelimiter() const noexcept {
            return Delimiter_;
        }
    };

    struct TCopyHeader {
        TString Src;
        TString Dst;
        bool Weak = false;
    };

    struct TCopyReverseHeader {
        bool Weak = false;
    };

    template <class TFunc>
    struct TFuncIdx {
        TFunc Func;
        size_t Index = -1;

        TFuncIdx(TFunc func, size_t idx)
            : Func(func)
            , Index(idx)
        {};
    };

    /**
     * Small wrapper for checking prefix
     *
     * @param[in] header                 header for check
     * @param[in] prefix                 prefix for check
     *
     * @return                           is matched
     */
    bool MatchHeaderPrefix(TStringBuf header, TStringBuf prefix);

    /**
     * Parser for construction of funcs from raw config values
     *
     * @param[in] value                  string with value from config
     * @param[in] delimiter              set custom delimiter
     *
     * @return                           class with func and parameters
     */
    TCreateHeader ParseHeaderFunc(const TString& key, TStringBuf value, TStringBuf delimiter);
    /**
     * Subparser for "time" function
     *
     * @param[in] value                  string with value from config
     * @param[in] delimiter              set custom delimiter
     *
     * @return                           class with func and parameters
     */
    TCreateHeader SubParserHeaderFuncTime(const TString& key, TStringBuf value, TStringBuf delimiter);

    using THeadersFuncMap = THashMap<TString, TFuncIdx<TCreateHeader> >;
    using THeadersCopyMap = THashMap<TString, TFuncIdx<TCopyHeader> >;
    using THeadersCopyReverseMap = THashMap<TString, TFuncIdx<TCopyReverseHeader> >;

    class THeadersHandler : public TMoveOnly {
    public:
        bool Delete(TString regexp) noexcept;

        void CopyValue(TString src, TString dst, bool weak) noexcept;

        void Append(TCreateHeader h, bool weak) noexcept;

        void Create(TCreateHeader h, bool weak) noexcept;

        void Apply(NSrvKernel::THeaders& headers, const NSrvKernel::TConnDescr& descr, const THashMap<TString, TSharedFileReReader>& files) const noexcept;

        void AddFile(TValueFromFile fromFile);

        void SetupFileReaders(THashMap<TString, TSharedFileReReader>& readers, IWorkerCtl* process);

    private:
        TFsm Delete_ {TFsm::False()};

        THeadersCopyMap Copy_;
        THeadersCopyReverseMap CopyReverse_;
        THeadersFuncMap CreateFuncs_;
        THeadersFuncMap CreateFuncsWeak_;
        THeadersFuncMap AppendFuncs_;
        THeadersFuncMap AppendFuncsWeak_;
        TVector<TValueFromFile> FromFiles_;
    };
}
