#pragma once

#include <balancer/kernel/module/ssl_props.h>

#include <util/string/builder.h>
#include <util/generic/hash_set.h>
#include <util/generic/string.h>

namespace NSrvKernel {
    /**
     * Implementation of Ja3 TLS fingerprinting.
     *
     * For more information please see https://github.com/salesforce/ja3
     */
    class TSslJa3 {
    private:
        const TSslJa3Data* Data_;

        // List of reserved values https://tools.ietf.org/html/draft-ietf-tls-grease-01
        const THashSet<ui16> Grease_ {
            0x0a0a, 0x1a1a, 0x2a2a, 0x3a3a, 0x4a4a, 0x5a5a, 0x6a6a, 0x7a7a,
            0x8a8a, 0x9a9a, 0xaaaa, 0xbaba, 0xcaca, 0xdada, 0xeaea, 0xfafa
        };

        // Grease values for PskKeyExchangeModes
        const THashSet<ui8> PskKeyExchangeModesGrease_ {
            0x0b, 0x2a, 0x49, 0x68, 0x87, 0xa6, 0xc5, 0xe4
        };

        // All ALPN identifiers beginning with this prefix are reserved as grease
        const TString ALPNGreasePrefix_ = "ignore/";

    public:
        explicit TSslJa3(const TSslJa3Data* props)
            : Data_(props)
        {};

        /**
         * Generate Ja3 string formatted as SSLVersion,Cipher,SSLExtension,EllipticCurve,EllipticCurvePointFormat
         * in case of Data_ is nullptr(no sni module) than return correct empty value "0,,,,"
         *
         * @return                           Ja3 string
         */
        TString Ja3HeaderValue() const noexcept;

        /**
         * Generate Ja4 string formatted as
         * SignatureAlgorithms,SignatureAlgorithmsCert, SupportedVersions,ApplicationLayerProtocolNegotiation,KeyShare,PskKeyExchangeModes
         * in case of Data_ is nullptr(no sni module) than return correct empty value ",,,,,"
         *
         * @return                           Ja4 string
         */
        TString Ja4HeaderValue() const noexcept;

    private:
        void SerializeVector16bit(const TVector<ui8>& v, size_t startPos, TStringBuilder& dest) const noexcept;
        void SerializeVectorInt(const TVector<int>& v, TStringBuilder& dest) const noexcept;
        void SerializeProtocols(TStringBuilder& dest) const noexcept;
        void SerializeKeyShare(TStringBuilder& dest) const noexcept;
        bool IsALPNGrease(const TVector<ui8>& v, size_t begin, size_t end) const noexcept;

    private:
        static void SerializeVector8bit(const TVector<ui8>& v, size_t startPos, TStringBuilder& dest) noexcept;
        static void SerializeVector8bit(const TVector<ui8>& v, size_t startPos, const THashSet<ui8>& grease, TStringBuilder& dest) noexcept;
    };
}
