#pragma once

#include "http2_base.h"
#include "http2_flow.h"
#include "http2_headers.h"

#include <balancer/kernel/http2/server/utils/http2_queue.h>

namespace NSrvKernel::NHTTP2 {

    class TStreamOutput final : public IStreamOutput, public IFlowCallback {
    public:
        enum class EState {
            Open,
            Closing,
            Closed,
            Cancelled
        };

        explicit TStreamOutput(TStreamId, IConnection& conn, const TPrioTreeNode& prio) noexcept;

        [[nodiscard]] bool IsClosed() const noexcept {
            return EState::Closed == State_;
        }

        [[nodiscard]] bool IsOpen() const noexcept {
            return EState::Open == State_;
        }

        void PrintTo(IOutputStream& out) const;

        // TStream callbacks

        TError SendHeaders(THeadersList headers, std::optional<ui64> contentLength, bool eos) noexcept;

        TError SendData(TChunkList data, size_t sendBufferMax) noexcept;

        TError SendEnd() noexcept;

        TError MarkClosed() noexcept;

        TError FlushData() noexcept;

        void Cancel() noexcept;

        [[nodiscard]] TStreamSendFlow& GetFlow() noexcept {
            return Flow_;
        }

        // TClientOutput callbacks

        const TPrioTreeNode& GetPrioTreeNode() const noexcept override {
            return PrioTreeNode_;
        }

        TErrorOr<TData> DequeueNextDataFrame(ui32 frameSize) noexcept override;

        void OnConnFlowBlockedForever() noexcept override;

        // TStreamSendFlow callbacks

        void OnConnOutputClose() noexcept override;

        void OnFlowBlockedForever() noexcept override;

        TError OnFlowUnblocked() noexcept override;

    private:
        [[nodiscard]] bool MaySendData() const noexcept;

    private:
        TLogger& Logger_;
        TStats& Stats_;
        TConnOutput& Output_;
        const TPrioTreeNode& PrioTreeNode_;

        TPrioHandle PrioHandle_;

        // Response body
        TChunkList DataBuffer_;

        TContExecutor& Executor_;
        TCoroSingleCondVar SendDataCV_;
        TStreamSendFlow Flow_;
        std::optional<ui64> ContentLength_;

        EState State_ = EState::Open;
    };
}
