#pragma once

#include <balancer/kernel/net/sockops.h>

#include <util/datetime/base.h>
#include <util/generic/array_ref.h>
#include <util/generic/variant.h>
#include <util/generic/vector.h>
#include <util/generic/xrange.h>
#include <util/random/fast.h>
#include <util/stream/input.h>
#include <util/system/condvar.h>
#include <util/system/thread.h>

#include <array>

namespace NSrvKernel::NNetUt {

    // The only reason to make this is to prevent unwanted symmetries in test inputs
    class TStableRandomInput : public IInputStream {
    public:
        TString GenString(size_t len);

    private:
        size_t DoRead(void* buf, size_t len) override;

    private:
        // Making the test input readable
        std::array<ui8, 65> Data_ {"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890+/"};

        // A random but fixed seed
        TFastRng64 Rng_ {0xe70f61c4873a6373, 0x3c503d67, 0x407b5059cf5fb547, 0x18d9f661,};
    };


    enum class ETcpFinish {
        Fin, Rst
    };

    using TAction = std::variant<TString, TDuration>;
    using TActions = TVector<TAction>;

    TSockBufSize ConsumeSockBufSize(TArrayRef<ui8>& data);

    TActions ConsumeActions(TArrayRef<ui8>& data);

    ETcpFinish ConsumeTcpFinish(TArrayRef<ui8>& data);


    std::pair<TString, TDuration> CombineActions(const TActions& acts);


    class TWriteThread : public ISimpleThread {
    public:
        TWriteThread(TSocket s, TActions a, ETcpFinish f);

        TError Error() {
            return std::move(Err_);
        }

        void Cancel() {
            Sock_.Close();
        }

    private:
        void* ThreadProc() noexcept override;

    private:
        TError Err_;
        TSocket Sock_;
        const TActions Acts_;
    };


    class TReadThread : public ISimpleThread {
    public:
        TReadThread(TSocket s, size_t buf, TDuration tot);

        TError Error() {
            return std::move(Err_);
        }

        TString Result() {
            return std::move(Res_);
        }

        void Cancel() {
            Sock_.Close();
        }

    private:
        void* ThreadProc() noexcept override;

    private:
        TError Err_;
        TString Res_;
        TSocket Sock_;
        const size_t Buf_;
    };


    std::pair<TSocketHolder, TSocket> CreateTcpSocketPair(TSockBufSize sz = {});

}
