#include "record_data.h"

#include <library/cpp/monlib/metrics/labels.h>
#include <util/generic/xrange.h>

namespace NReport {

    TBaseCounter::TBaseCounter(const TBaseCounter& other, size_t workerId)
        : TCombinedCounter(other, workerId)
        , Name_(other.Name_)
    {}

    TStatsCounter::TStatsCounter(TString name) {
        SetName(std::move(name));
    }

    TStatsCounter& TStatsCounter::SetName(TString name) {
        Name_ = std::move(name);
        return *this;
    }

    void TStatsCounter::Init(const TString& uuid, NMonitoring::TLabels labels, TSharedStatsManager& statsManager) {
        SetSharedCounter(statsManager.MakeCounter(TStringBuilder() << "report-" << uuid << "-" << Name_)
            .AllowDuplicate().Labels(std::move(labels)).Build());
    }

    TStatsGauge::TStatsGauge(TString name) {
        SetName(std::move(name));
    }

    TStatsGauge& TStatsGauge::SetName(TString name) {
        Name_ = std::move(name);
        return *this;
    }

    void TStatsGauge::Init(const TString& uuid, NMonitoring::TLabels labels, TSharedStatsManager& statsManager) {
        SetSharedCounter(statsManager.MakeGauge(TStringBuilder() << "report-" << uuid << "-" << Name_)
            .AllowDuplicate().Labels(std::move(labels)).Build());
    }

    TDimHistogram::TDimHistogram(TDimFilter filter)
        : DimFilter_(filter)
        , Histograms_(GetUnistatMatrixSize(DimFilter_))
    {}

    TDimHistogram::TDimHistogram(const TDimHistogram& other, size_t workerId)
        : DimFilter_(other.DimFilter_)
        , DisableRobotness_(other.DisableRobotness_)
    {
        Histograms_.reserve(other.Histograms_.size());
        for (const auto& histogram : other.Histograms_) {
            if (!!histogram) {
                Histograms_.emplace_back(TSharedHistogram(*histogram, workerId));
            } else {
                Histograms_.push_back(Nothing());
            }
        }
    }

    void TDimHistogram::Init(const TString& uuid, NMonitoring::TLabels labels,
                             TVector<ui64> intervals, TSharedStatsManager& statsManager,
                             TMaybe<bool> disableSslness, TMaybe<bool> disableRobotness, double scale) {

        if (statsManager.HideLegacySignals()) {
            return;
        }

        DisableRobotness_ = disableRobotness.GetOrElse(false);

        if (disableSslness.GetOrElse(false)) {
            // if sslness is disabled, we shouldn't create any signals with sslness
            if (DimFilter_.Mask & TDimFilter().Set(EDim::Ssl).Mask) {
                return;
            }
        }

        for (size_t index = 0; index < Histograms_.size(); ++index) {
            if (disableRobotness.GetOrElse(false)) {
                // if robotness is disabled, we should only store histograms with unknown robotness and internalness
                if (DimFilter_.Mask & TDimFilter{}.Set(EDim::Robot).Mask) {
                    if (GetDimParameter(DimFilter_, index, EDim::Robot) != "u") {
                        continue;
                    }
                }

                if (DimFilter_.Mask & TDimFilter{}.Set(EDim::Internal).Mask) {
                    if (GetDimParameter(DimFilter_, index, EDim::Internal) != "u") {
                        continue;
                    }
                }
            }

            Histograms_[index] = statsManager.MakeHistogram(
                TStringBuilder() << "report-" << uuid << GetUnistatSuffix(DimFilter_, index), intervals
            ).AllowDuplicate().Labels(labels).Scale(scale).Build();
        }
    }

    void TDimHistogram::Add(const TDimData& data, ui64 value) {
        size_t index = GetUnistatIndex(DimFilter_, data, DisableRobotness_);
        if (Histograms_[index]) {
            Histograms_[index]->AddValue(value);
        }
    }

    TRecordRanges::TRecordRanges(
        const TMaybe<TDurationRanges>& legacyTimes,
        const TMaybe<TDurationRanges>& backendTimes,
        const TMaybe<TDurationRanges>& clientFailTimeRanges,
        const TMaybe<TDurationRanges>& firstByteTimeRanges,
        const TMaybe<TSizeRanges>& inputSizeRanges,
        const TMaybe<TSizeRanges>& outputSizeRanges,
        const TMaybe<TSizeRanges>& inputHeadersSizeRanges,
        const TMaybe<TSizeRanges>& failedInputHeadersSizeRanges,
        NLegacyRange::TDimFilter filter,
        TMaybe<bool> disableSslness,
        TMaybe<bool> disableRobotness
    ) noexcept
        : DimFilter_(filter)
        , DisableSslness_(disableSslness)
        , DisableRobotness_(disableRobotness)
    {
        DimHistograms_.emplace_back(CodeFilter_);
        DimHistograms_.emplace_back(CodeIRFilter_);
        DimHistograms_.emplace_back(CodeIRSlFilter_);

        if (legacyTimes) {
            LegacyTimes_.ConstructInPlace(*legacyTimes);
            std::generate(LegacyTimes_->ValuesBegin(), LegacyTimes_->ValuesEnd(), [&filter] {
                return NLegacyRange::TStorage(filter);
            });
        }

        if (backendTimes) {
            BackendTimes_.ConstructInPlace(*backendTimes);
            Fill(BackendTimes_->ValuesBegin(), BackendTimes_->ValuesEnd(), 0);
        }

        if (clientFailTimeRanges) {
            ClientFailTimes_.ConstructInPlace(*clientFailTimeRanges);
            Fill(ClientFailTimes_->ValuesBegin(), ClientFailTimes_->ValuesEnd(), 0);
        }

        if (firstByteTimeRanges) {
            FirstByte_.ConstructInPlace(*firstByteTimeRanges);
            Fill(FirstByte_->ValuesBegin(), FirstByte_->ValuesEnd(), 0);
        }

        if (inputSizeRanges) {
            InputSizes_.ConstructInPlace(*inputSizeRanges);
            Fill(InputSizes_->ValuesBegin(), InputSizes_->ValuesEnd(), 0);
        }

        if (outputSizeRanges) {
            OutputSizes_.ConstructInPlace(*outputSizeRanges);
            Fill(OutputSizes_->ValuesBegin(), OutputSizes_->ValuesEnd(), 0);
        }

        if (inputHeadersSizeRanges) {
            InputHeadersSizes_.ConstructInPlace(*inputHeadersSizeRanges);
            Fill(InputHeadersSizes_->ValuesBegin(), InputHeadersSizes_->ValuesEnd(), 0);
        }

        if (failedInputHeadersSizeRanges) {
            FailedInputHeadersSizes_.ConstructInPlace(*failedInputHeadersSizeRanges);
            Fill(FailedInputHeadersSizes_->ValuesBegin(), FailedInputHeadersSizes_->ValuesEnd(), 0);
        }
    }

    TRecordRanges::TRecordRanges(const TRecordRanges& other, size_t workerId)
        : LegacyTimes_(other.LegacyTimes_)
        , BackendTimes_(other.BackendTimes_)
        , ClientFailTimes_(other.ClientFailTimes_)
        , FirstByte_(other.FirstByte_)
        , InputSizes_(other.InputSizes_)
        , OutputSizes_(other.OutputSizes_)
        , InputHeadersSizes_(other.InputHeadersSizes_)
        , FailedInputHeadersSizes_(other.FailedInputHeadersSizes_)

        , BackendTimesShared_(CreateHistogramOrNothing(other.BackendTimesShared_, workerId))
        , ClientFailShared_(CreateHistogramOrNothing(other.ClientFailShared_, workerId))
        , FirstByteShared_(CreateHistogramOrNothing(other.FirstByteShared_, workerId))
        , InputSizesShared_(CreateHistogramOrNothing(other.InputSizesShared_, workerId))
        , OutputSizesShared_(CreateHistogramOrNothing(other.OutputSizesShared_, workerId))
        , ProcessingTimesShared_(CreateHistogramOrNothing(other.ProcessingTimesShared_, workerId))
        , InputHeadersSizesShared_(CreateHistogramOrNothing(other.InputHeadersSizesShared_, workerId))
        , FailedInputHeadersSizesShared_(CreateHistogramOrNothing(other.FailedInputHeadersSizesShared_, workerId))
        , InternalrobotShared_(CreateHistogramOrNothing(other.InternalrobotShared_, workerId))
        , ExternalunknownShared_(CreateHistogramOrNothing(other.ExternalunknownShared_, workerId))
    {
        DimHistograms_.reserve(other.DimHistograms_.size());
        for (const auto& dimHistogram : other.DimHistograms_) {
            DimHistograms_.emplace_back(dimHistogram, workerId);
        }
    }

    void TRecordRanges::RegisterSharedSignals(const TString& uuid, const NMonitoring::TLabels& labels, TSharedStatsManager& statsManager, THashSet<TString>& disabledSignals) {
        constexpr double TIME_SCALE = 1e6;

        if (LegacyTimes_) {
            auto intervals = RangesToIntervals(LegacyTimes_->Ranges());
            if (intervals.size() > 0) {
                for (auto& hist : DimHistograms_) {
                    hist.Init(uuid, labels, intervals, statsManager, DisableSslness_, DisableRobotness_, TIME_SCALE);
                }

                ProcessingTimesShared_ = statsManager.MakeHistogram(
                    "report-" + uuid + "-processing_time", intervals
                ).AllowDuplicate().Labels(labels).Scale(TIME_SCALE).Build();

                if (!statsManager.HideLegacySignals()) {
                    InternalrobotShared_ = statsManager.MakeHistogram(
                        "report-" + uuid + "-internalrobot", intervals
                    ).AllowDuplicate().Labels(labels).Scale(TIME_SCALE).Build();

                    ExternalunknownShared_ = statsManager.MakeHistogram(
                        "report-" + uuid + "-externalunknown", intervals
                    ).AllowDuplicate().Labels(labels).Scale(TIME_SCALE).Build();
                }
            }
        }

        RegisterGenericHistogram(statsManager, BackendTimesShared_, BackendTimes_, uuid, "backend_time", labels, disabledSignals);
        RegisterGenericHistogram(statsManager, ClientFailShared_, ClientFailTimes_, uuid, "client_fail_time", labels, disabledSignals);
        RegisterGenericHistogram(statsManager, FirstByteShared_, FirstByte_, uuid, "backend_ttfb", labels, disabledSignals);
        RegisterGenericHistogram(statsManager, InputSizesShared_, InputSizes_, uuid, "input_size", labels, disabledSignals);
        RegisterGenericHistogram(statsManager, OutputSizesShared_, OutputSizes_, uuid, "output_size", labels, disabledSignals);
        RegisterGenericHistogram(statsManager, InputHeadersSizesShared_, InputHeadersSizes_, uuid, "input_headers_size", labels, disabledSignals);
        RegisterGenericHistogram(statsManager, FailedInputHeadersSizesShared_, FailedInputHeadersSizes_, uuid, "backend_fail_input_headers_size", labels, disabledSignals);
    }

    void TRecordRanges::RegisterLegacyTimes(const TDuration& duration, const NLegacyRange::TDimData& data) noexcept {
        if (LegacyTimes_) {
            (*LegacyTimes_)[duration].Add(data);
        }

        if (ProcessingTimesShared_) {
            ProcessingTimesShared_->AddValue(duration.MicroSeconds());

            for (auto& hist : DimHistograms_) {
                hist.Add(data, duration.MicroSeconds());
            }

            if (data.Internal.GetOrElse(false) || data.Robot.GetOrElse(false)) {
                if (InternalrobotShared_) {
                    InternalrobotShared_->AddValue(duration.MicroSeconds());
                }
            } else {
                if (ExternalunknownShared_) {
                    ExternalunknownShared_->AddValue(duration.MicroSeconds());
                }
            }
        }
    }

    void TRecordRanges::RegisterBackendTime(TDuration duration) noexcept {
        if (BackendTimes_) {
            ++(*BackendTimes_)[duration];
        }

        if (BackendTimesShared_) {
            BackendTimesShared_->AddValue(duration.MicroSeconds());
        }
    }

    void TRecordRanges::RegisterInputSize(size_t inputSize) noexcept {
        if (InputSizes_) {
            ++(*InputSizes_)[inputSize];
        }

        if (InputSizesShared_) {
            InputSizesShared_->AddValue(inputSize);
        }
    }

    void TRecordRanges::RegisterOutputSize(size_t outputSize) noexcept {
        if (OutputSizes_) {
            ++(*OutputSizes_)[outputSize];
        }

        if (OutputSizesShared_) {
            OutputSizesShared_->AddValue(outputSize);
        }
    }

    void TRecordRanges::RegisterClientFail(TDuration duration) noexcept {
        if (ClientFailTimes_) {
            ++(*ClientFailTimes_)[duration];
        }

        if (ClientFailShared_) {
            ClientFailShared_->AddValue(duration.MicroSeconds());
        }
    }

    void TRecordRanges::RegisterFirstByte(TDuration duration) noexcept {
        if (FirstByte_) {
            ++(*FirstByte_)[duration];
        }

        if (FirstByteShared_) {
            FirstByteShared_->AddValue(duration.MicroSeconds());
        }
    }

    void TRecordRanges::RegisterInputHeadersSize(size_t headersSize) noexcept {
        if (InputHeadersSizes_) {
            ++(*InputHeadersSizes_)[headersSize];
        }

        if (InputHeadersSizesShared_) {
            InputHeadersSizesShared_->AddValue(headersSize);
        }
    }

    void TRecordRanges::RegisterFailedInputHeadersSize(size_t headersSize) noexcept {
        if (FailedInputHeadersSizes_) {
            ++(*FailedInputHeadersSizes_)[headersSize];
        }

        if (FailedInputHeadersSizesShared_) {
            FailedInputHeadersSizesShared_->AddValue(headersSize);
        }
    }

    bool TRecordRanges::UpdateRanges(const TRecordRanges& other) noexcept {
        if (LegacyTimes_ && other.LegacyTimes_) {
            if (LegacyTimes_->Ranges() != other.LegacyTimes_->Ranges()) {
                return false;
            }
        } else if (!LegacyTimes_) {
            LegacyTimes_ = other.LegacyTimes_;
        }

        if (BackendTimes_ && other.BackendTimes_) {
            if (BackendTimes_->Ranges() != other.BackendTimes_->Ranges()) {
                return false;
            }
        } else if (!BackendTimes_) {
            BackendTimes_ = other.BackendTimes_;
        }

        if (ClientFailTimes_ && other.ClientFailTimes_) {
            if (ClientFailTimes_->Ranges() != other.ClientFailTimes_->Ranges()) {
                return false;
            }
        } else if (!ClientFailTimes_) {
            ClientFailTimes_ = other.ClientFailTimes_;
        }

        if (FirstByte_ && other.FirstByte_) {
            if (FirstByte_->Ranges() != other.FirstByte_->Ranges()) {
                return false;
            }
        } else if (!FirstByte_) {
            FirstByte_ = other.FirstByte_;
        }

        if (InputSizes_ && other.InputSizes_) {
            if (InputSizes_->Ranges() != other.InputSizes_->Ranges()) {
                return false;
            }
        } else if (!InputSizes_) {
            InputSizes_ = other.InputSizes_;
        }

        if (OutputSizes_ && other.OutputSizes_) {
            if (OutputSizes_->Ranges() != other.OutputSizes_->Ranges()) {
                return false;
            }
        } else if (!OutputSizes_) {
            OutputSizes_ = other.OutputSizes_;
        }

        if (InputHeadersSizes_ && other.InputHeadersSizes_) {
            if (InputHeadersSizes_->Ranges() != other.InputHeadersSizes_->Ranges()) {
                return false;
            }
        } else if (!InputHeadersSizes_) {
            InputHeadersSizes_ = other.InputHeadersSizes_;
        }

        if (FailedInputHeadersSizes_ && other.FailedInputHeadersSizes_) {
            if (FailedInputHeadersSizes_->Ranges() != other.FailedInputHeadersSizes_->Ranges()) {
                return false;
            }
        } else if (!FailedInputHeadersSizes_) {
            FailedInputHeadersSizes_ = other.FailedInputHeadersSizes_;
        }

        if (DimFilter_.Mask != other.DimFilter_.Mask) {
            return false;
        }

        return true;
    }

    TMaybe<TSharedHistogram> TRecordRanges::CreateHistogramOrNothing(const TMaybe<TSharedHistogram>& histogram, size_t workerId) {
        if (!!histogram) {
            return TSharedHistogram(*histogram, workerId);
        }
        return Nothing();
    }

    TRecordData::TRecordData(
        TRecordRanges ranges,
        const TCodesSet& codesSet,
        TString uuid,
        NMonitoring::TLabels labels,
        TSharedStatsManager& statsManager,
        THashSet<TString> disabledSignals
    )
        : Ranges_(std::move(ranges))
        , Uuid_(std::move(uuid))
    {
        for (auto cat : xrange(CategoriesCount)) {
            TString name = (TStringBuilder() << (cat + 1) << "xx");
            AllCounters_.emplace_back(&StatusCodeCategs_.at(cat).SetName("sc_" + name));
            AllCounters_.emplace_back(&OutgoingStatusCodeCategs_.at(cat).SetName("outgoing_" + name));

            UnistatOnlyCounters_.emplace_back(&InternalRobotStatusCodeCategs_.at(cat).SetName(name + "-internalrobot"));
            UnistatOnlyCounters_.emplace_back(&ExternalUnknownStatusCodeCategs_.at(cat).SetName(name + "-externalunknown"));
        }

        for (auto code : codesSet) {
            AllCounters_.emplace_back(&OutgoingStatusCodes_[code].SetName("outgoing_" + ToString(code)));

            UnistatOnlyCounters_.emplace_back(&ExternalUnknownStatusCodes_[code].SetName(ToString(code) + "-externalunknown"));
        }

        if (!Uuid_.empty()) {
            for (auto* cnt : AllCounters_) {
                if (disabledSignals.contains(cnt->Name())) {
                    disabledSignals.erase(cnt->Name());
                } else {
                    cnt->Init(Uuid_, labels, statsManager);
                }
            }
            if (!statsManager.HideLegacySignals()) {
                for (auto* cnt : UnistatOnlyCounters_) {
                    cnt->Init(Uuid_, labels, statsManager);
                }
            }

            Ranges_.RegisterSharedSignals(Uuid_, std::move(labels), statsManager, disabledSignals);
        } else {
            disabledSignals.clear();
        }

        if (!disabledSignals.empty()) {
            TStringBuilder builder;
            bool first = false;
            for (const auto& s : disabledSignals) {
                if (!first) {
                    builder << ", ";
                    first = false;
                }
                builder << s;
            }
            ythrow NConfig::TConfigParseError{} << "got unknown signals to disabling: " << builder;
        }
    }

    TRecordData::TRecordData(const TRecordData& other, size_t workerId)
        : Ranges_(other.Ranges_, workerId)
        , Uuid_(other.Uuid_)
        , Labels_(other.Labels_)

        , InProg_(other.InProg_, workerId)
        , Succ_(other.Succ_, workerId)
        , Fail_(other.Fail_, workerId)
        , Requests_(other.Requests_, workerId)
        , ConnFail_(other.ConnFail_, workerId)
        , BackendFail_(other.BackendFail_, workerId)
        , ClientFail_(other.ClientFail_, workerId)
        , OtherFail_(other.OtherFail_, workerId)
        , KeepAlive_(other.KeepAlive_, workerId)
        , NonKeepAlive_(other.NonKeepAlive_, workerId)
        , Reused_(other.Reused_, workerId)
        , NotReused_(other.NotReused_, workerId)
        , BackendKeepaliveReused_(other.BackendKeepaliveReused_, workerId)
        , ConnRefused_(other.ConnRefused_, workerId)
        , ConnTimeout_(other.ConnTimeout_, workerId)
        , ConnOtherError_(other.ConnOtherError_, workerId)
        , NoBackendsError_(other.NoBackendsError_, workerId)
        , BackendTimeout_(other.BackendTimeout_, workerId)
        , BackendAttempt_(other.BackendAttempt_, workerId)
        , LimitedBackendAttempt_(other.LimitedBackendAttempt_, workerId)
        , BackendError_(other.BackendError_, workerId)
        , ClientError_(other.ClientError_, workerId)
        , Code503_(other.Code503_, workerId)
        , InputSpeed_(other.InputSpeed_, workerId)
        , OutputSpeed_(other.OutputSpeed_, workerId)
        , BackendHedgedAttempts_(other.BackendHedgedAttempts_, workerId)
        , BackendHedgedSucc_(other.BackendHedgedSucc_, workerId)
        , BackendWriteError_(other.BackendWriteError_, workerId)
        , BackendShortReadAnswer_(other.BackendShortReadAnswer_, workerId)
        , AllCounters_{
            &InProg_,
            &Succ_,
            &Fail_,
            &Requests_,
            &ConnFail_,
            &BackendFail_,
            &ClientFail_,
            &OtherFail_,
            &KeepAlive_,
            &NonKeepAlive_,
            &Reused_,
            &NotReused_,
            &BackendKeepaliveReused_,
            &ConnRefused_,
            &ConnTimeout_,
            &ConnOtherError_,
            &NoBackendsError_,
            &BackendTimeout_,
            &BackendAttempt_,
            &LimitedBackendAttempt_,
            &BackendError_,
            &ClientError_,
            &Code503_,
            &InputSpeed_,
            &OutputSpeed_,
            &BackendHedgedAttempts_,
            &BackendHedgedSucc_,
            &BackendWriteError_,
            &BackendShortReadAnswer_,
        }
    {
        for (size_t i = 0; i < CategoriesCount; i++) {
            StatusCodeCategs_[i] = TStatsCounter(other.StatusCodeCategs_[i], workerId);
            OutgoingStatusCodeCategs_[i] = TStatsCounter(other.OutgoingStatusCodeCategs_[i], workerId);

            AllCounters_.push_back(&StatusCodeCategs_[i]);
            AllCounters_.push_back(&OutgoingStatusCodeCategs_[i]);

            InternalRobotStatusCodeCategs_[i] = TStatsCounter(other.InternalRobotStatusCodeCategs_[i], workerId);
            ExternalUnknownStatusCodeCategs_[i] = TStatsCounter(other.ExternalUnknownStatusCodeCategs_[i], workerId);

            UnistatOnlyCounters_.push_back(&InternalRobotStatusCodeCategs_[i]);
            UnistatOnlyCounters_.push_back(&ExternalUnknownStatusCodeCategs_[i]);
        }

        for (const auto& [key, value] : other.OutgoingStatusCodes_) {
            OutgoingStatusCodes_[key] = TStatsCounter(value, workerId);
            AllCounters_.push_back(&OutgoingStatusCodes_[key]);
        }

        for (const auto& [key, value] : other.ExternalUnknownStatusCodes_) {
            ExternalUnknownStatusCodes_[key] = TStatsCounter(value, workerId);
            UnistatOnlyCounters_.push_back(&ExternalUnknownStatusCodes_[key]);
        }
    }

    void TRecordData::RegisterResponseEnd(const TLocalStats& stats) noexcept {
        Ranges_.RegisterLegacyTimes(stats.TotalTime, stats.DimData);

        Ranges_.RegisterBackendTime(stats.BackendTime);

        Ranges_.RegisterInputSize(stats.InputSize);

        Ranges_.RegisterOutputSize(stats.OutputSize);

        if (stats.StatusCode >= 100 && stats.StatusCode <= 599) {
            OutgoingStatusCodeCategs_[stats.StatusCode / 100 - 1].Inc();

            if (!DisableRobotness_.GetOrElse(false)
                && (stats.DimData.Internal.GetOrElse(false) || stats.DimData.Robot.GetOrElse(false)))
            {
                InternalRobotStatusCodeCategs_[stats.StatusCode / 100 - 1].Inc();
            } else {
                ExternalUnknownStatusCodeCategs_[stats.StatusCode / 100 - 1].Inc();
                if (auto* code = ExternalUnknownStatusCodes_.FindPtr(stats.StatusCode)) {
                    code->Add(1);
                }
            }

            if (auto* code = OutgoingStatusCodes_.FindPtr(stats.StatusCode)) {
                code->Add(1);
            }
        }
    }

    void TRecordData::RegisterSucc(const TMaybe<size_t>& headersSize) noexcept {
        --InProg_;
        ++Succ_;
        if (headersSize) {
            Ranges_.RegisterInputHeadersSize(*headersSize);
        }
    }

    void TRecordData::RegisterFail(const TDuration duration, EFail fail, const TMaybe<size_t>& headersSize) noexcept {
        --InProg_;
        ++Fail_;

        switch (fail) {
        case EFail::Conn:
            ++BackendFail_;
            ++ConnFail_;
            break;
        case EFail::Backend:
            ++BackendFail_;
            break;
        case EFail::Client:
            ++ClientFail_;
            Ranges_.RegisterClientFail(duration);
            break;
        case EFail::Other:
            ++OtherFail_;
            break;
        }

        if (headersSize) {
            Ranges_.RegisterInputHeadersSize(*headersSize);
            if (fail == EFail::Backend) {
                Ranges_.RegisterFailedInputHeadersSize(*headersSize);
            }
        }
    }

    void TRecordData::RegisterConnStats(const TConnStats& delta) noexcept {
#define Y_UPDATE_STAT_VALUE(statName) statName##_ += delta.statName;

        Y_UPDATE_STAT_VALUE(ConnRefused);
        Y_UPDATE_STAT_VALUE(ConnTimeout);
        Y_UPDATE_STAT_VALUE(ConnOtherError);
        Y_UPDATE_STAT_VALUE(NoBackendsError);
        Y_UPDATE_STAT_VALUE(ClientTimeout);
        Y_UPDATE_STAT_VALUE(BackendTimeout);
        Y_UPDATE_STAT_VALUE(BackendAttempt);
        Y_UPDATE_STAT_VALUE(LimitedBackendAttempt);
        Y_UPDATE_STAT_VALUE(BackendError);
        Y_UPDATE_STAT_VALUE(ClientError);
        Y_UPDATE_STAT_VALUE(Code503);
        Y_UPDATE_STAT_VALUE(BackendKeepaliveReused);
        Y_UPDATE_STAT_VALUE(BackendHedgedAttempts);
        Y_UPDATE_STAT_VALUE(BackendHedgedSucc);
        Y_UPDATE_STAT_VALUE(BackendWriteError);
        Y_UPDATE_STAT_VALUE(BackendShortReadAnswer);

        for (auto code : xrange(StatusCodeCategs_.size())) {
            StatusCodeCategs_[code] += delta.StatusCodes[code];
        }
    }

    void TRecordData::DisableSslness(bool value) {
        if (DisableSslness_.Defined() && *DisableSslness_ != value) {
            ythrow NConfig::TConfigParseError{} <<
                                                "Inconsistent values of disable_sslness for uuid " << Uuid().Quote();
        }
        DisableSslness_ = value;
    }

    void TRecordData::DisableRobotness(bool value) {
        if (DisableRobotness_.Defined() && *DisableRobotness_ != value) {
            ythrow NConfig::TConfigParseError{} <<
                                                "Inconsistent values of disable_robotness for uuid " << Uuid().Quote();
        }
        DisableSslness_ = value;
    }
}
