#include "storage_gc_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 TStorageGCProcess::Execute() {
        Executor_->Executor().Execute();
    }

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

    void TStorageGCProcess::CleanupLoop() {
        TCont* cont = Executor().Running();

        // Get lowest timeout
        TDuration minTimeout = TDuration::Max();
        for (auto& st: MainTask_.Storages_) {
            if (minTimeout > st->CacheTTL_) {
                minTimeout = st->CacheTTL_;
            }
        }

        // Randomize initial sleep
        TDuration startTimeout = TDuration::Seconds(RandomNumber(minTimeout.Seconds()));
        TInstant sleepTime = TInstant::Now() + startTimeout;

        cont->SleepD(sleepTime);
        if (cont->Cancelled()) {
            return;
        }

        while (!cont->Cancelled()) {
            // Wait for a full timeout
            sleepTime = TInstant::Now() + minTimeout;

            for (auto* st: MainTask_.Storages_) {
                // Or find the nearest occurrence of the cache expiration
                auto wakeupTime = st->Cleanup();
                if (wakeupTime.Defined() && sleepTime < *wakeupTime) {
                    sleepTime = *wakeupTime;
                }
            }

            cont->SleepD(sleepTime);
        }
    }

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

    TStorageGCProcess::TStorageGCProcess(TMainTask& task, const TMainOptions& options, TW2WChannel<TM2CMessage>& m2cChannel)
        : TBaseProcess(task.Config().NChildren + ChildProcessTypeToIndex(WorkerType()), task)
        , MainStats_(MakeHolder<TMainStats>(*task.MainStats_, WorkerId()))
        , M2CChannel_(m2cChannel)
    {
        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);

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

        StorageGCLog_ << "hello from storageGC" << Endl;

        StorageGCTask_ = TCoroutine(ECoroType::Service, "TStorageGCProcess::CleanupLoop", &Executor_->Executor(),
                &TStorageGCProcess::CleanupLoop, this);

        RecvLoop_ = TCoroutine(ECoroType::Service, "TStorageGCProcess::RecvLoop", &Executor_->Executor(),
                &TStorageGCProcess::RecvLoop, this);
    }

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

    TSharedFiles* TStorageGCProcess::SharedFiles() noexcept {
        Y_FAIL();
    }

    TThreadedQueue* TStorageGCProcess::ThreadedQueue(const TString&) noexcept {
        Y_FAIL();
    }

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

    TLog* TStorageGCProcess::GetLog(const TString&) {
        Y_FAIL();
    }

    NProcessCore::TChildProcessType TStorageGCProcess::WorkerType() const noexcept {
        return NProcessCore::TChildProcessType::StorageGC;
    }

    NDns::IResolver& TStorageGCProcess::Resolver() noexcept {
        Y_FAIL();
    }

    void TStorageGCProcess::DoDispose() noexcept {
        StorageGCLog_.DisposeBackend();

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

    TLog* TStorageGCProcess::GetDynamicBalancingLog() {
        Y_FAIL();
    }
}
