#pragma once

#include <balancer/kernel/log/errorlog.h>
#include <balancer/kernel/module/conn_descr.h>

#include <util/generic/scope.h>

#include <balancer/kernel/connection_manager_helpers/helpers.h>

namespace NModProxy {
    class TTransfer {
    public:
        TTransfer(
            const NSrvKernel::TConnDescr& descr,
            NSrvKernel::IIoInput* i,
            NSrvKernel::IIoOutput* o,
            TDuration readTimeout,
            TDuration writeTimeout,
            TInstant sessionDeadline,
            NSrvKernel::TCoroSingleCondVar* finishCV,
            NSrvKernel::TSocketIo* readSockIo,
            TString logPrefix,
            TCont* mainCont,
            bool watchReadSocket,
            bool isRequest
        ) noexcept;

        void Run(TContExecutor* const exec);

        void Start(const char* name, TContExecutor* const exec) noexcept;

        void Cancel() noexcept {
            if (Running() && !Eof() && !AnyError()) {
                Cancelled_ = true;
            }
            TransferTask_.Cancel();
        }

        void Join() noexcept {
            TransferTask_.Join();
        }

        bool Running() const noexcept {
            return TransferTask_.Running();
        }

        TError& ClientError() noexcept {
            return ClientError_;
        }

        TError& BackendError() noexcept {
            return BackendError_;
        }

        TError& WatchReadSocketError() noexcept {
            return WatchReadSocketError_;
        }

        bool AnyError() const noexcept {
            return ClientError_ || BackendError_;
        }

        bool Eof() const noexcept {
            return Eof_;
        }

        void Finish() noexcept;

        void SetSessionDeadline(TInstant deadline) noexcept {
            SessionDeadline_ = deadline;
        }

        void FromBackendTransferFinished() noexcept {
            WatchReadSocket_ = false;
        }

        bool Cancelled() const noexcept {
            return Cancelled_;
        }

    private:
        TError DoRecv(NSrvKernel::TChunkList& lst) noexcept;

        TError DoSend(NSrvKernel::TChunkList lst) noexcept;

        void SetWriteDeadline(TInstant writeDeadline) noexcept;

        void WatchReadSocket() noexcept;

    private:
        const NSrvKernel::TConnDescr& Descr_;
        NSrvKernel::IIoInput* const I_ = nullptr;
        NSrvKernel::IIoOutput* const O_ = nullptr;
        TDuration ReadTimeout_;
        TDuration WriteTimeout_;
        TInstant SessionDeadline_;
        NSrvKernel::TCoroSingleCondVar* FinishCV_ = nullptr;
        NSrvKernel::TSocketIo* ReadSocketIo_ = nullptr;
        const TString LogPrefix_;
        TCont* MainCont_ = nullptr;

        NSrvKernel::TError ClientError_;
        NSrvKernel::TError BackendError_;
        NSrvKernel::TError WatchReadSocketError_;

        bool Eof_ = false;
        bool WatchReadSocket_ = false;
        bool Cancelled_ = false;
        bool IsRequest_;

        NSrvKernel::TCoroutine TransferTask_;
    };
}
