#pragma once

#include "addr.h"

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

#include <library/cpp/coroutine/engine/impl.h>

#include <util/network/socket.h>


namespace NSrvKernel {

    TErrorOr<TSocketHolder> TcpSocket(int af, bool makeNonblock) noexcept;

    TError Bind(SOCKET sock, const TSockAddr& addr) noexcept;

    TError Listen(SOCKET sock, unsigned backlog) noexcept;

    TError Shutdown(SOCKET sock) noexcept;

    TError Close(SOCKET sock) noexcept;


    TError EnableNonBlocking(SOCKET sock) noexcept;

    TError EnableNoDelay(SOCKET sock) noexcept;

    TError EnableReuseAddr(SOCKET sock) noexcept;

    TError EnableReusePort(SOCKET sock) noexcept;

    TError EnableRstOnClose(SOCKET sock) noexcept;

    TError EnableV6Only(SOCKET sock) noexcept;

    TErrorOr<TSockAddr> GetSockName(SOCKET sock) noexcept;


    struct TSockBufSize {
        TMaybe<unsigned> Rcv;
        TMaybe<unsigned> Snd;
    };

    TError ValidateSockBufSize(TSockBufSize sz) noexcept;

    TError SetSockBufSize(SOCKET sock, TSockBufSize sz) noexcept;


    struct TSockTimeout {
        TMaybe<TDuration> Rcv;
        TMaybe<TDuration> Snd;
    };

    TError SetSockTimeout(SOCKET sock, TSockTimeout tout) noexcept;


    struct TTcpKeepalive {
        TMaybe<unsigned> Cnt;
        TMaybe<unsigned> Idle;
        TMaybe<unsigned> Intvl;
    };


    TError ValidateKeepalive(TTcpKeepalive k) noexcept;

    TError EnableKeepalive(SOCKET sock, TTcpKeepalive k) noexcept;


    TError ValidateCongestionControl(const TString& algo) noexcept;

    TError SetCongestionControl(SOCKET sock, const TString& algo) noexcept;

    TError SetNotsentLowat(SOCKET sock, int size) noexcept;


    struct TTcpInfo {
        TDuration Rtt;
        TDuration RttVar;
        ui32 SndCwnd = 0;
        ui32 Unacked = 0;
        ui64 TotalRetrans = 0;
    };

    bool CanGetTcpInfo() noexcept;

    TErrorOr<TTcpInfo> GetTcpInfo(SOCKET sock) noexcept;


    [[nodiscard]] TMaybe<int> ErrNo(const TError& err);

    [[nodiscard]] bool HasBlocked(const TError& err);

    [[nodiscard]] bool HasTimedOut(const TError& err);


    TError Connect(SOCKET sock, const TSockAddr& addr);

    struct TAcceptResult {
        TSocketHolder Conn;
        TSockAddr Addr;
    };

    TErrorOr<TAcceptResult> Accept(SOCKET sock, bool makeNonblock) noexcept;

}
