#include "async_simple_resolver.h"

#include <balancer/kernel/dns/async_simple/async_simple_wrapper.h>
#include <balancer/kernel/net/addr.h>


namespace NSrvKernel::NDns {

TAsyncSimpleResolver::TAsyncSimpleResolver(TDuration resolveTimeout)
    : Resolver_(MakeHolder<TAsyncSimpleWrapper>(resolveTimeout))
{
}

TAsyncSimpleResolver::~TAsyncSimpleResolver() = default;

void TAsyncSimpleResolver::StartResolve(THosts& hostToIp, int family) noexcept
{
    HostToIp_ = &hostToIp;

    TSockAddr numericAddr;
    for (auto& i : hostToIp) {
        auto numericError = TSockAddr::FromIpPort(i.first, 0).AssignTo(numericAddr);
        if (!numericError) { // already numeric format - nothing to resolve
            if ((family == AF_INET && numericAddr.V4()) || (family == AF_INET6 && numericAddr.V6())) {
                i.second = numericAddr.Ip().ToString();
            }
            continue;
        }

        Resolver_->AsyncResolve(i.first.c_str(), family, this);
        HasRequests_ = true;
    }
}

void TAsyncSimpleResolver::WaitForResults(TDuration timeout) noexcept
{
    if (HasRequests_) {
        Resolver_->WaitRequests(timeout);
        Resolver_->CancelRequests();
    }
}

void TAsyncSimpleResolver::OnComplete(int status, const hostent* he) noexcept
{
    if (status != ARES_SUCCESS || !he) {
        return;
    }

    char** addrList = he->h_addr_list;
    if (*addrList) { // one address is enough, don't cycle through all
        auto hostIter = HostToIp_->find(he->h_name);
        if (hostIter == HostToIp_->end()) {
            Y_ASSERT(false);
            return;
        }
        switch (he->h_addrtype) {
            case AF_INET6: {
                sockaddr_in6 sockAddr;
                Zero(sockAddr);
                sockAddr.sin6_family = AF_INET6;
                memcpy(&sockAddr.sin6_addr.s6_addr, *addrList, sizeof(sockAddr.sin6_addr.s6_addr));
                TSockAddr addr{sockAddr};
                hostIter->second = addr.ToString();
                break;
            }
            case AF_INET: {
                sockaddr_in sockAddr;
                Zero(sockAddr);
                sockAddr.sin_family = AF_INET;
                memcpy(&sockAddr.sin_addr, *addrList, sizeof(sockAddr.sin_addr));
                TSockAddr addr{sockAddr};
                hostIter->second = addr.ToString();
                break;
            }
            default:
                Y_ASSERT(false);
        }
    }
}

}