#include "process_stat.h"

#include <balancer/kernel/cpu/cpu_usage.h>
#include <balancer/kernel/dns/resolver_face.h>

#include <util/system/rusage.h>

namespace NSrvKernel {

    namespace {
        template <typename T>
        T AbsDiff(T a, T b) noexcept {
            return a > b ? a - b : b - a;
        }

        // Coroutine cycle duration in microseconds
        static const TVector<ui64> ContsCycleHistIntervals = {
            0, 100, 200, 300, 400, 500, 600, 700, 800, 900,          // [0..1ms]
            1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000,    // [1..10ms)
            10000, 15000, 20000, 25000, 30000, 35000, 40000, 45000,  // [10..50ms)
            50000, 60000, 70000, 80000, 90000,                       // [50..100ms)
            100000, 125000, 150000, 175000, 200000, 225000,          // [100..250ms)
            250000, 300000, 350000, 400000, 450000,                  // [250..500ms)
            500000, 600000, 700000, 800000, 900000, 1000000          // [500..1000ms]
        };

        static const TVector<ui64> CpuUsageHistIntervals = {
            0,
            5,
            10,
            15,
            20,
            25,
            30,
            35,
            45,
            50,
            60,
            70,
            80,
            90,
            100
        };

        static TWorkerCpuStat::TStats MakeStats(TSharedStatsManager& statsManager, const TString& processName) {
            return {
                .ContsReady = statsManager.MakeGauge(TString::Join(processName, "-conts_ready")).Build(),
                .ContsScheduled = statsManager.MakeGauge(TString::Join(processName, "-conts_scheduled")).Build(),
                .ContsWaiting = statsManager.MakeGauge(TString::Join(processName, "-conts_waiting")).Build(),
                .ContsAllocReleasedSize = statsManager.MakeGauge(TString::Join(processName, "-conts_alloc_released")).Build(),
                .ContsAllocNotReleasedSize = statsManager.MakeGauge(TString::Join(processName, "-conts_alloc_not_released")).Build(),
                .ContsAllocated = statsManager.MakeGauge(TString::Join(processName, "-conts_allocated")).Build(),
                .ContsCycleCounter = statsManager.MakeCounter(TString::Join(processName, "-conts_cycle")).Build(),
                .DesyncCounter = statsManager.MakeCounter(TString::Join(processName, "-cpu_usage_time_desync")).Build(),
                .CpuUsageHist = statsManager.MakeHistogram(TString::Join(processName, "-cpu_usage"), CpuUsageHistIntervals).Build(),
                .ContsCycleHist = statsManager.MakeHistogram(TString::Join(processName, "-conts_cycle_duration"), ContsCycleHistIntervals).Build(),
            };
        }

        static TWorkerCpuStat::TStats MakeWorkerStats(TWorkerCpuStat::TStats& stats, size_t workerId) {
            return {
                .ContsReady = TSharedCounter(stats.ContsReady, workerId),
                .ContsScheduled = TSharedCounter(stats.ContsScheduled, workerId),
                .ContsWaiting = TSharedCounter(stats.ContsWaiting, workerId),
                .ContsAllocReleasedSize = TSharedCounter(stats.ContsAllocReleasedSize, workerId),
                .ContsAllocNotReleasedSize = TSharedCounter(stats.ContsAllocNotReleasedSize, workerId),
                .ContsAllocated = TSharedCounter(stats.ContsAllocated, workerId),
                .ContsCycleCounter = TSharedCounter(stats.ContsCycleCounter, workerId),
                .DesyncCounter = TSharedCounter(stats.DesyncCounter, workerId),
                .CpuUsageHist = TSharedHistogram(stats.CpuUsageHist, workerId),
                .ContsCycleHist = TSharedHistogram(stats.ContsCycleHist, workerId),
            };
        }
    }

    TProcessStat::TProcessStat(TSharedStatsManager& statsManager, TVector<TThreadStatus>& statuses, TString processType)
        : ProcessName_(std::move(processType))
        , Stats_(MakeStats(statsManager, ProcessName_))
        , DnsCounters_(MakeHolder<NDns::TStatsCounters>(statsManager, ProcessName_))
        , ThreadsStatuses_(statuses)
    {}

    THolder<TWorkerCpuStat> TProcessStat::GetWorkerCpuStat(size_t workerId) {
        Y_VERIFY(workerId < ThreadsStatuses_.size());
        return MakeHolder<TWorkerCpuStat>(
            MakeWorkerStats(Stats_, workerId),
            ProcessName_ == "worker" ? &ThreadsStatuses_[workerId] : nullptr
        );
    }

    THolder<NDns::TStatsCounters> TProcessStat::GetDnsCounters(size_t workerId) {
        return MakeHolder<NDns::TStatsCounters>(*DnsCounters_, workerId);
    }
}
