#include "is_gdpr_b.h"

#include <balancer/kernel/cookie/gdpr/is_gdpr_b.pb.h>
#include <balancer/kernel/cookie/utils/utils.h>

#include <library/cpp/string_utils/base64/base64.h>
#include <library/cpp/svnversion/svnversion.h>

#include <util/digest/city.h>
#include <util/generic/algorithm.h>

#include <string>

namespace NGdprCookie {
    TString TIsGdprB::Render() const noexcept {
        NIsGdprB::TIsGdprB res;
        res.SetBalancerVerHash(BalancerVerHash);
        res.SetTstampHours((std::max(Tstamp, IsGdprBStart) - IsGdprBStart).Hours());
        res.SetIsGdpr(IsGdpr);
        if (IsGdprNoVpn) {
            res.SetIsGdprNoVpn(*IsGdprNoVpn ? NIsGdprB::MB_TRUE : NIsGdprB::MB_FALSE);
        }
        return Base64Encode(res.SerializeAsString());
    }

    TMaybe<TIsGdprB> TIsGdprB::Parse(TStringBuf rawBase64) noexcept {
        if (rawBase64.length() % 4) {
            return Nothing();
        }
        TString raw;
        try {
            raw = Base64Decode(rawBase64);
        } catch (...) {
            // the implementation does not throw but the function signature does not guarantee that.
            return Nothing();
        }
        NIsGdprB::TIsGdprB ints;
        if (!ints.ParseFromString(raw)) {
            return Nothing();
        }
        TIsGdprB res;
        res.BalancerVerHash = ints.GetBalancerVerHash();
        res.Tstamp = IsGdprBStart + TDuration::Hours(ints.GetTstampHours());
        res.IsGdpr = ints.GetIsGdpr();
        if (ints.GetIsGdprNoVpn() != NIsGdprB::MB_NONE) {
            res.IsGdprNoVpn = ints.GetIsGdprNoVpn() == NIsGdprB::MB_TRUE;
        }
        return res;
    }

    TIsGdprB TIsGdprB::Merge(TIsGdprB prev, TIsGdprB next) noexcept {
        if (prev.Tstamp > next.Tstamp) {
            return prev;
        }
        return next;
    }

    ui32 BalancerVerHash(TStringBuf branch, TStringBuf lastChange) noexcept {
        auto res = (CityHash64(std::string().append(branch).append(TStringBuf("@")).append(lastChange)) & 0x1FFFFF);
        return res ? res : 1;
    }

    ui32 BalancerVerHash() noexcept {
        static const auto res = BalancerVerHash(GetBranch(), GetArcadiaLastChange());
        return res;
    }
}

using namespace NGdprCookie;

template <>
void Out<TIsGdprB>(IOutputStream& out, const TIsGdprB& val) {
    out << "{.BalancerVerHash="sv << val.BalancerVerHash;
    out << ",.Tstamp=TInstant::Seconds("sv << val.Tstamp.Seconds() << ")";
    out << ",.IsGdpr="sv << val.IsGdpr;
    out << ",.IsGdprNoVpn="sv;
    NSrvKernel::NCookie::PrintTo(out, val.IsGdprNoVpn);
    out << "}"sv;
}
