#pragma once

#include <balancer/kernel/balancing/backend.h>

#include <balancer/kernel/coro/coroutine.h>
#include <balancer/kernel/http/parser/http.h>

#include <library/cpp/config/sax.h>


namespace NSrvKernel {

class IWorkerCtl;

class IPingable {
public:
    virtual IModule* ActiveCheckModule() const noexcept = 0;
    virtual void SetEnabled(bool value) noexcept = 0;
};

struct TPingerConfigUnparsed {
    bool TryConsume(const TString& key, NConfig::IConfig::IValue* value) {
        ON_KEY("delay", Delay) {
            return true;
        }

        ON_KEY("tcp_check", TcpCheck) {
            return true;
        }

        ON_KEY("request", UnparsedRequest) {
            CheckRequest();
            return true;
        }

        ON_KEY("ping_after_conn_error", PingAfterConnError) {
            return true;
        }

        return false;
    }

    void CheckRequest() {
        TRequest request;
        TryRethrowError(request.Parse(UnparsedRequest));
    }

    TString UnparsedRequest;
    TDuration Delay = TDuration::Zero();
    bool TcpCheck = false;
    bool PingAfterConnError = false;
};

struct TPingerConfig {
    explicit TPingerConfig(const TPingerConfigUnparsed& config)
        : Delay(config.Delay)
        , PingAfterConnError(config.PingAfterConnError)
    {
        if (!config.UnparsedRequest.empty()) {
            PingRequest.ConstructInPlace();
            TryRethrowError(PingRequest->Parse(config.UnparsedRequest));
        }
    }

    TMaybe<TRequest> PingRequest;
    TDuration Delay = TDuration::Zero();
    bool PingAfterConnError = false;
};

class TPinger {
public:
    TPinger(IPingable& backend, const TInstant& lastRequestTime, TContExecutor* executor,
            const TPingerConfig& config, IWorkerCtl& process, bool steady);

    const IPingable& Backend() const noexcept {
        return Backend_;
    }

    IPingable& Backend() noexcept {
        return Backend_;
    }

    ui64 GetLastPingTime() const noexcept {
        return LastPingTime_;
    }

private:
    bool Ping() noexcept;

private:
    IPingable& Backend_;
    const TInstant& LastRequestTime_;
    const TPingerConfig& Config_;
    IWorkerCtl& Process_;
    bool Steady_ = false;
    ui64 LastPingTime_ = 0;

    TCoroutine PingerCont_;
};

}
