#include "async_resolver.h"

#include <balancer/kernel/dns/async_cache.h>
#include <balancer/kernel/dns/async_coro.h>
#include <balancer/kernel/dns/async_entry.h>
#include <balancer/kernel/dns/common.h>

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

#include <util/generic/scope.h>

namespace NSrvKernel::NDns {

constexpr size_t MaxRequests = 0xFFFFFFFF;

TAsyncResolver::TAsyncResolver(TContExecutor* executor, TStatsCounters& counters, const TResolverConfig& config)
    : Executor_(executor)
    , Config_(config)
    , Counters_(counters)
    , ResolveCounters_(MakeHolder<TCounters>(Counters_))
{
    auto resolver = MakeContAsyncResolver(executor, TOptions{MaxRequests, Config_.DnsTimeout}, *ResolveCounters_, Config_.DnsServer);
    auto builder = MakeHolder<TEntryBuilder>();
    Cache_ = MakeHolder<TAsyncCache>(executor, Config_, std::move(resolver), std::move(builder), *ResolveCounters_);
}

TAsyncResolver::~TAsyncResolver() = default;

TErrorOr<TSockAddrInfo> TAsyncResolver::DoResolve(const TResolveInfo& info, bool onlyCache, TInstant deadline)
{
    const auto start = TInstant::Now();

    if (deadline <= start) {
        Counters_.DnsAsyncNoTimeToResolve.Add(1);
        return Y_MAKE_ERROR(TNetworkResolutionError{EAI_AGAIN} << "No time to resolve: now "
                << start.ToString() << ", effective deadline " << deadline.ToString());
    }

    if (info.Host.empty()) {
        Counters_.DnsAsyncEmptyAddress.Add(1);
        return Y_MAKE_ERROR(TNetworkResolutionError{EAI_FAIL} << "Empty address");
    }

    TSockAddr numericAddr;
    auto numericError = TSockAddr::FromIpPort(info.Host, info.Port).AssignTo(numericAddr);
    if (!numericError) { // already numeric format - nothing to resolve
        Counters_.DnsAsyncAlreadyNumeric.Add(1);
        return TSockAddrInfo{{std::move(numericAddr)}};
    }

    Y_DEFER {
        const auto resolveTime = TInstant::Now() - start;
        Counters_.DnsAsyncResolveTime.AddValue(resolveTime.MicroSeconds());
    };
    return Cache_->LookupOrResolve(info, onlyCache, deadline);
}

void TAsyncResolver::DoResetCache()
{
    Counters_.DnsAsyncResetCache.Add(1);
    auto resolver = MakeContAsyncResolver(Executor_, TOptions{MaxRequests, Config_.DnsTimeout}, *ResolveCounters_, Config_.DnsServer);
    auto builder = MakeHolder<TEntryBuilder>();
    Cache_ = MakeHolder<TAsyncCache>(Executor_, Config_, std::move(resolver), std::move(builder), *ResolveCounters_);
}

}
