#include "updater_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 {
    TUpdaterProcess::TUpdaterProcess(TMainTask& task, const TMainOptions& options, TW2WChannel<TM2CMessage>& m2cChannel)
        : TBaseProcess(task.Config().NChildren + ChildProcessTypeToIndex(WorkerType()), task)
        , MainStats_(MakeHolder<TMainStats>(*task.MainStats_, WorkerId()))
        , Updater_(*task.UpdaterManager_)
        , 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);

        Log_ = task.Log_.GenerateThreadedLog(&Executor(), WorkerId());
        DynamicBalancingLog_ = task.DynamicBalancingLog_.GenerateThreadedLog(&Executor(), WorkerId());

        RecvLoopTask_ = TCoroutine("updater_recv_loop", &Executor_->Executor(),
                &TUpdaterProcess::RecvLoop, this);

        SharedFiles_ = MakeHolder<TSharedFiles>();

        Updater_.SetSharedFiles(SharedFiles());

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

        MainTask_.Trees.Init(this);

        FuncExecutionCont_ = TCoroutine(ECoroType::Service, "func_execution_cont", &Executor_->Executor(),
                                        &TUpdaterProcess::FuncExecution, this);
    }

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

    void TUpdaterProcess::Execute() {
        Updater_.Start(this);
        SharedFiles_->Start();
        Executor_->Executor().Execute();
    }

    void TUpdaterProcess::Stop() {
        Dispose();
    }

    void TUpdaterProcess::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);
        }
    }

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

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

    NSrvKernel::TPingerManager& TUpdaterProcess::SharedPingerManager() noexcept {
        Y_FAIL();
    }

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

    NProcessCore::TChildProcessType TUpdaterProcess::WorkerType() const noexcept {
        return NProcessCore::TChildProcessType::Updater;
    }

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

    void TUpdaterProcess::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();
        }

        Log_.DisposeBackend();
        DynamicBalancingLog_.DisposeBackend();

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

    TLog* TUpdaterProcess::GetDynamicBalancingLog() {
        return &DynamicBalancingLog_;
    }

    void TUpdaterProcess::FuncExecution() {
        auto& channel = *MainTask_.ChildrenChannels_.back();
        auto* runningCont = Executor_->Executor().Running();
        auto waker = ThreadLocalEventWaker(runningCont);
        auto* wakerPtr = waker.Get();
        std::function<void(IWorkerCtl*)> func;

        while (channel.Receive(func, TInstant::Max(), runningCont, wakerPtr) != EChannelStatus::Canceled) {
            func(this);
        }
    }
}
