#include "pinger_process.h"

#include <balancer/kernel/balancing/updater.h>
#include <balancer/kernel/ctl/children_process_common.h>
#include <balancer/kernel/dns/dns_helper.h>
#include <balancer/kernel/process/sd/sd.h>


namespace NSrvKernel::NProcessCore {
    void TPingerProcess::Execute() {
        SharedFiles_->Start();
        Executor_->Executor().Execute();
    }

    void TPingerProcess::Stop() {
        PingerLog_ << "Pinger is about to stop working" << Endl;
        Dispose();
    }

    void TPingerProcess::RecvLoop() {
        auto runningCont = Executor_->Executor().Running();
        auto waker = ThreadLocalEventWaker(runningCont);
        auto* wakerPtr = waker.Get();

        bool shutdown = false;
        while (!runningCont->Cancelled() && !shutdown) {
            TM2CMessage message;
            auto status = M2CChannel_.Receive(message, TInstant::Max(), runningCont, wakerPtr);
            if (status != EChannelStatus::Success) {
                return;
            }

            std::visit(TOverloaded{
                [&](const TEvent& masterEvent) {
                    Y_UNUSED(masterEvent.OutputChannel->Send("", TInstant::Max(), runningCont, wakerPtr));
                },

                [&](const TListEvents& toMaster) {
                    Y_UNUSED(toMaster.OutputChannel->Send(TVector<TString>{}, TInstant::Max(), runningCont, wakerPtr));
                },

                [&](const TShutDown&) {
                    Stop();
                    shutdown = true;
                },

                [&](const TResetDnsCache&) {
                }
            }, message);
        }
    }

    TPingerProcess::TPingerProcess(TMainTask& task, const TMainOptions& options, TW2WChannel<TM2CMessage>& m2cChannel)
        : TBaseProcess(task.Config().NChildren + ChildProcessTypeToIndex(WorkerType()), task)
        , MainStats_(MakeHolder<TMainStats>(*task.MainStats_, WorkerId()))
        , M2CChannel_(m2cChannel)
        , DnsHelper_(MakeHolder<NDns::THelper>())
    {
        const auto& config = task.Config();

        WorkerCpuStat_ = task.ProcessStatOwner_->GetProcessStat(WorkerType())->GetWorkerCpuStat(WorkerId());

        Executor_ = MakeHolder<TOwnExecutor>(
            options.CoroStackSize(),
            options.Poller,
            options.CoroStackGuard,
            options.CoroPoolSettings,
            WorkerCpuStat_.Get()
        );
        Executor_->Executor().SetFailOnError(config.CoroFailOnError);

        CpuMeasureCoroutine_ = StartCpuAndTimeMeasurer(Executor_->Executor(), WorkerCpuStat_.Get(), nullptr);

        PingerLog_ = task.PingerLog_.GenerateThreadedLog(&Executor(), WorkerId());

        PingerStatsManager_ = MakeHolder<TPingerManager>(&Executor_->Executor(), task.PingerStatsCounters_.Get());

        PingerLog_ << "hello from pinger" << Endl;

        RecvLoopTask_ = TCoroutine(ECoroType::Service, "PingerProcess::RecvLoop", &Executor_->Executor(),
                &TPingerProcess::RecvLoop, this);

        SharedFiles_ = MakeHolder<TSharedFiles>();

        DnsCounters_ = task.ProcessStatOwner_->GetProcessStat(WorkerType())->GetDnsCounters(WorkerId());
        const bool useAsync = config.Dns.AsyncResolve;
        const auto dnsParams = DnsHelper_->CreateDnsResolver(&Executor_->Executor(), SharedFiles_.Get(), config.Dns,
                DnsCounters_.Get(), useAsync, ThreadedQueue("resolver_queue"));

        PingerLog_ << dnsParams << Endl;

        MainTask_.Trees.Init(this);
    }

    TPingerProcess::~TPingerProcess() {
        Dispose();
    }

    TSharedFiles* TPingerProcess::SharedFiles() noexcept {
        return SharedFiles_.Get();
    }

    TThreadedQueue* TPingerProcess::ThreadedQueue(const TString &name) noexcept {
        return ThreadedQueueList_.FindOrCreate(
            &Executor_->Executor(),
            ThreadPool_,
            name
        );
    }

    NSrvKernel::TPingerManager& TPingerProcess::SharedPingerManager() noexcept {
        return *PingerStatsManager_;
    }

    TLog* TPingerProcess::GetLog(const TString&) {
        return nullptr;
    }

    NProcessCore::TChildProcessType TPingerProcess::WorkerType() const noexcept {
        return NProcessCore::TChildProcessType::Pinger;
    }

    NDns::IResolver& TPingerProcess::Resolver() noexcept {
        return DnsHelper_->Resolver();
    }

    void TPingerProcess::DoDispose() noexcept {
        MainTask_.Trees.Dispose(this);

        for (auto i = ThreadedQueueList_.Begin(); i != ThreadedQueueList_.End(); ++i) {
            i->Dispose();
        }

        for (auto i = ThreadedQueueList_.Begin(); i != ThreadedQueueList_.End(); ++i) {
            i->Join();
        }

        PingerLog_.DisposeBackend();

        Executor_->Executor().Abort();
    }

    TLog* TPingerProcess::GetDynamicBalancingLog() {
        return nullptr;
    }
}
