#include "http2_stats.h"

namespace NSrvKernel::NHTTP2 {

    TStatCounter::TStatCounter(const TStatCounter& other, size_t workerId) noexcept
        : ValueShared_(other.ValueShared_, workerId)
    {}

    TStatCounter::TStatCounter(
        TSharedStatsManager& statsManager,
        TString unistatName,
        TString unistatSigopt) noexcept
        : ValueShared_(
            unistatSigopt == "summ"
            ? statsManager.MakeCounter(TString::Join("http2-", unistatName)).AllowDuplicate().Build()
            : statsManager.MakeGauge(TString::Join("http2-", unistatName)).AllowDuplicate().Build()
        )
    {}

    TDeltaCounter::TDeltaCounter(TSharedStatsManager& statsManager, TString name) noexcept
        : TStatCounter(
            statsManager,
            name,
            "summ"
        )
    {}

    TErrorCounter::TErrorCounter(TSharedStatsManager& statsManager, TString name, TString err, bool attr) noexcept
        : TStatCounter(
            statsManager,
            TString::Join(name, attr ? "-" : "_", err),
            "summ"
        )
    {}

    TAbsCounter::TAbsCounter(TSharedStatsManager& statsManager, TString name) noexcept
        : TStatCounter(
            statsManager,
            name,
            "ammv"
        )
    {}


    TConnCounters::TConnCounters(TSharedStatsManager& statsManager, TString prefix) noexcept
        : Open_(statsManager, prefix + "_conn_open")
        , OpenActive_(statsManager, prefix + "_conn_open_active")
        , Close_(statsManager, prefix + "_conn_close")
        , Abort_(statsManager, prefix + "_conn_abort")
        , InProg_(statsManager, prefix + "_conn_inprog")
        , Active_(statsManager, prefix + "_conn_active")
    {}

    TConnCounters::TConnCounters(const TConnCounters& other, size_t workerId) noexcept
        : Open_(other.Open_, workerId)
        , OpenActive_(other.OpenActive_, workerId)
        , Close_(other.Close_, workerId)
        , Abort_(other.Abort_, workerId)
        , InProg_(other.InProg_, workerId)
        , Active_(other.Active_, workerId)
    {}

    TStreamCounters::TStreamCounters(TSharedStatsManager& statsManager) noexcept
        : UnknownErrors_(statsManager, "stream_unknown_errors")
        , ClientOpen_(statsManager, "stream_client_open")
        , ServerOpen_(statsManager, "stream_server_open")
        , EndRecv_(statsManager, "end_stream_recv")
        , EndSend_(statsManager, "end_stream_send")
        , Success_(statsManager, "stream_success")
        , ConnAbort_(statsManager, "stream_conn_abort")
        , Dispose_(statsManager, "stream_dispose")
        , InProg_(statsManager, "stream_inprog")
    {}

    TStreamCounters::TStreamCounters(const TStreamCounters& other, size_t workerId) noexcept
        : UnknownErrors_(other.UnknownErrors_, workerId)
        , ClientOpen_(other.ClientOpen_, workerId)
        , ServerOpen_(other.ServerOpen_, workerId)
        , EndRecv_(other.EndRecv_, workerId)
        , EndSend_(other.EndSend_, workerId)
        , Success_(other.Success_, workerId)
        , ConnAbort_(other.ConnAbort_, workerId)
        , Dispose_(other.Dispose_, workerId)
        , InProg_(other.InProg_, workerId)
    {}

    TGoAwaySendCounters::TGoAwaySendCounters(const TGoAwaySendCounters& other, size_t workerId)
        : GoAwaySend_(other.GoAwaySend_, workerId)
        , UnknownGoAwaySend_(other.UnknownGoAwaySend_, workerId)
        , NoErrorSend_(other.NoErrorSend_, workerId)
        , ProtocolErrorSend_(other.ProtocolErrorSend_, workerId)
        , InternalErrorSend_(other.InternalErrorSend_, workerId)
        , FlowControlErrorSend_(other.FlowControlErrorSend_, workerId)
        , StreamClosedErrorSend_(other.StreamClosedErrorSend_, workerId)
        , CompressionErrorSend_(other.CompressionErrorSend_, workerId)
    {}

    TRstStreamSendCounters::TRstStreamSendCounters(const TRstStreamSendCounters& other, size_t workerId)
        : RstStreamSend_(other.RstStreamSend_, workerId)
        , UnknownRstStreamSend(other.UnknownRstStreamSend, workerId)
        , NoErrorSend_(other.NoErrorSend_, workerId)
        , ProtocolErrorSend_(other.ProtocolErrorSend_, workerId)
        , InternalErrorSend_(other.InternalErrorSend_, workerId)
        , FlowControlErrorSend_(other.FlowControlErrorSend_, workerId)
        , StreamClosedErrorSend_(other.StreamClosedErrorSend_, workerId)
        , RefusedStreamErrorSend_(other.RefusedStreamErrorSend_, workerId)
    {}

    TGoAwayRecvCounters::TGoAwayRecvCounters(const TGoAwayRecvCounters& other, size_t workerId)
        : GoAwayRecv_(other.GoAwayRecv_, workerId)
        , FailedPing_(other.FailedPing_, workerId)
        , Error102ReadingFromSocket_(other.Error102ReadingFromSocket_, workerId)
        , ClosingCurrentSessions_(other.ClosingCurrentSessions_, workerId)
    {}

    void TGoAwayRecvCounters::OnGoAway(const TGoAway& goAway) noexcept {
        if (EErrorCode::PROTOCOL_ERROR == goAway.ErrorCode) {
            if ("Failed ping." == goAway.DebugData) {
                // BALANCER-1992
                // Chromium browsers would restart the connection if it seems unresponsive.
                // Happens often enough to deserve a separate counter.
                FailedPing_ += 1;
            } else if ("Error 102 reading from socket." == goAway.DebugData) {
                // BALANCER-1992
                // Chromium browsers would restart the connection on network errors (TODO(velavokr): find out when).
                // Happens often enough to deserve a separate counter.
                Error102ReadingFromSocket_ += 1;
            } else if ("Closing current sessions." == goAway.DebugData) {
                // BALANCER-2089
                // Chromium browsers reopen all the connections on ip and cert storage changes
                ClosingCurrentSessions_ += 1;
            } else {
                GoAwayRecv_.OnError(goAway.ErrorCode);
            }
        } else {
            GoAwayRecv_.OnError(goAway.ErrorCode);
        }
    }

    TRstStreamRecvCounters::TRstStreamRecvCounters(const TRstStreamRecvCounters& other, size_t workerId)
        : RstStreamRecv_(other.RstStreamRecv_, workerId)
        , ProtocolErrorAfterCancel_(other.ProtocolErrorAfterCancel_, workerId)
    {}

    TBeforePrefaceErrorCounters::TBeforePrefaceErrorCounters(const TBeforePrefaceErrorCounters& other, size_t workerId)
        : BeforePreface_(other.BeforePreface_, workerId)
        , ClientFin_(other.ClientFin_, workerId)
    {}

    TStats::TStats(const TStats& other, size_t workerId)
        : H1Conn(other.H1Conn, workerId)
        , H2Conn(other.H2Conn, workerId)
        , Stream(other.Stream, workerId)
        , GoAwaySend(other.GoAwaySend, workerId)
        , GoAwayRecv(other.GoAwayRecv, workerId)
        , RstStreamSend(other.RstStreamSend, workerId)
        , RstStreamRecv(other.RstStreamRecv, workerId)
        , BeforePreface(other.BeforePreface, workerId)
        , RecvFrame(other.RecvFrame, workerId)
        , SendFrames(other.SendFrames, workerId)
        , Unexpected(other.Unexpected, workerId)
        , ReqsWithHeadersDropped(other.ReqsWithHeadersDropped, workerId)
        , RespsWithHeadersDropped(other.RespsWithHeadersDropped, workerId)
    {}

    EIOError ErrnoToIOError(int err) noexcept {
        switch (err) {
        default:
            return EIOError::Other;
        case ECANCELED:
            return EIOError::Cancelled;
        case ECONNRESET:
            return EIOError::ConnReset;
        case EIO:
            return EIOError::IO;
        case EPIPE:
            return EIOError::Pipe;
        }
    }

    bool TStats::OnIOError(EIOError err, EErrorSource src) noexcept {
        switch (src) {
        case EErrorSource::BeforePreface:
            return BeforePreface.OnError(err);
        case EErrorSource::RecvFrame:
            return RecvFrame.OnError(err);
        case EErrorSource::SendFrames:
            return SendFrames.OnError(err);
        case EErrorSource::Other:
            return Unexpected.OnError(err);
        default:
            Y_FAIL();
        }
    }
}
