#pragma once

#include <balancer/kernel/dns/stats_counters.h>
#include <util/datetime/base.h>

#include <contrib/libs/c-ares/include/ares.h>


namespace NSrvKernel::NDns {

struct TOptions {
    TOptions()
        : MaxRequests(Max())
        , TimeOut(TDuration::MilliSeconds(1000 * 3))
    {
    }

    TOptions(size_t maxRequests, TDuration timeout)
        : MaxRequests(maxRequests)
        , TimeOut(timeout)
    {
    }

    TOptions& SetMaxRequests(size_t val) noexcept {
        MaxRequests = val;
        return *this;
    }

    TOptions& SetTimeOut(const TDuration& val) noexcept {
        TimeOut = val;
        return *this;
    }

    size_t MaxRequests;
    TDuration TimeOut; //!< Timeout to resolve request by c-ares
};

class ICounters {
public:
    virtual ~ICounters() = default;

    virtual void AddResolve() noexcept = 0;
    virtual void AddNoData() noexcept = 0;
    virtual void AddNxdomain() noexcept = 0;
    virtual void AddResolveError() noexcept = 0;
    virtual void AddCacheResolve() noexcept = 0;
    virtual void AddCacheError() noexcept = 0;
};

class TCounters final : public ICounters {
public:
    TCounters(TStatsCounters& counters) : Counters_(counters) {}

    void AddResolve() noexcept override { Counters_.DnsAsyncResolve.Add(1); }
    void AddNoData() noexcept override { Counters_.DnsAsyncNoDataResult.Add(1); }
    void AddNxdomain() noexcept override { Counters_.DnsAsyncNxdomain.Add(1); }
    void AddResolveError() noexcept override { Counters_.DnsAsyncResolveError.Add(1); }
    void AddCacheResolve() noexcept override { Counters_.DnsAsyncCacheResolve.Add(1); }
    void AddCacheError() noexcept override { Counters_.DnsAsyncCacheError.Add(1); }

private:
    TStatsCounters& Counters_; // not owned
};

class IHostResult {
public:
    virtual ~IHostResult() = default;

    virtual void OnComplete(int status, const hostent* he) noexcept = 0;
};

struct TAresError : public TDnsError {
    TAresError(int code) {
        (*this) << ares_strerror(code);
    }
};

class TAresInit final {
public:
    TAresInit() {
        if (const auto code = ares_library_init(ARES_LIB_INIT_ALL); code) {
            ythrow TAresError(code) << "failed to init ares engine";
        }
    }

    ~TAresInit() {
        ares_library_cleanup();
    }

    static TAresInit& InitOnce() {
        static TAresInit ares;
        return ares;
    }
};

}