#include "async_simple_wrapper.h"

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


namespace NSrvKernel::NDns {

TAsyncSimpleWrapper::TAsyncSimpleWrapper(TDuration timeout)
{
    TAresInit::InitOnce();

    ares_options aresOpts;
    Zero(aresOpts);
    int optMask = 0;

    optMask |= ARES_OPT_FLAGS;
    aresOpts.flags = ARES_FLAG_STAYOPEN;

    optMask |= ARES_OPT_TIMEOUTMS;
    aresOpts.timeout = timeout.MilliSeconds();

    optMask |= ARES_OPT_TRIES;
    aresOpts.tries = 1;

    if (const auto code = ares_init_options(&Channel_, &aresOpts, optMask); code) {
        ythrow TAresError(code) << "failed to init ares channel";
    }
}

TAsyncSimpleWrapper::~TAsyncSimpleWrapper()
{
    CancelRequests();
    ares_destroy(Channel_);
}

void TAsyncSimpleWrapper::AsyncResolve(const char* addr, int family, IHostResult* cb) noexcept
{
    ares_gethostbyname(Channel_, addr, family, ResolveHostCbInternal, cb);
}

void TAsyncSimpleWrapper::CancelRequests() noexcept
{
    ares_cancel(Channel_);
}

void TAsyncSimpleWrapper::WaitRequests(TDuration timeout) noexcept
{
    const auto start = TInstant().Now();
    int numberOfFds = 0;
    fd_set readers, writers;
    timeval tv, *tvp;

    while (start + timeout > TInstant().Now()) {
        FD_ZERO(&readers);
        FD_ZERO(&writers);
        numberOfFds = ares_fds(Channel_, &readers, &writers);
        if (!numberOfFds) {
            break;
        }
        tvp = ares_timeout(Channel_, NULL, &tv);
        select(numberOfFds, &readers, &writers, NULL, tvp);
        ares_process(Channel_, &readers, &writers);
    }
}

void TAsyncSimpleWrapper::ResolveHostCbInternal(void* arg, int status, int /*timeouts*/, hostent* he) noexcept
{
    auto* subscriber = static_cast<IHostResult*>(arg);
    Y_VERIFY(subscriber);
    subscriber->OnComplete(status, he);
}

}