#include "sd.h"

#include <balancer/kernel/ctl/ctl.h>
#include <library/cpp/logger/log.h>

namespace NSrvKernel {

    namespace {
        TMaybe<TSharedCounter> MakeSharedCounter(TStringBuf name, TSharedStatsManager& statsManager) {
            TStringBuf shortName, suffix;
            name.RSplit('_', shortName, suffix);
            if (suffix == "dmmm" || suffix == "summ") {
                return statsManager.MakeCounter(TString{shortName}).Build();
            } else if (suffix == "ammv") {
                return statsManager.MakeGauge(TString{shortName}).Build();
            } else if (suffix == "axxx") {
                return statsManager.MakeGauge(TString{shortName}).Aggregation(TWorkerAggregation::Max).AggregateMaster(true).Build();
            } else if (suffix == "annn") {
                return statsManager.MakeGauge(TString{shortName}).Aggregation(TWorkerAggregation::Min).AggregateMaster(true).Default(Max<ui64>()).Build();
            } else {
                return Nothing();
            }
        }
    }

    class TSDStatCounterImpl
        : public NYP::NServiceDiscovery::IStatCounterImpl
    {
    public:
        TSDStatCounterImpl(TSharedCounter counter)
            : Counter_(std::move(counter))
        {
        }

        void Inc() override {
            ++Counter_;
        }

        void Set(TAtomicBase v) override {
            Counter_.Set(v);
        }

        TAtomicBase Get() const override {
            return Counter_.Value();
        }

        void SwitchToWorker(size_t workerId) {
            Counter_ = TSharedCounter(Counter_, workerId);
        }
    private:
        TSharedCounter Counter_;
    };

    NYP::NServiceDiscovery::TCounterFactory MakeCounterFactory(TSharedStatsManager& statsManager, TCounters& counters) {
        return [&statsManager, &counters](const TString& name) -> TAtomicSharedPtr<NYP::NServiceDiscovery::IStatCounterImpl> {
            if (auto shared = MakeSharedCounter(name, statsManager)) {
                auto counter = MakeAtomicShared<TSDStatCounterImpl>(std::move(*shared));
                counters.push_back(counter);
                return counter;
            } else {
                return NYP::NServiceDiscovery::StandaloneCounterFactory(name);
            }
        };
    }

    NYP::NServiceDiscovery::IRemoteRequesterPtr MakeRequester(const TSDConfig& config) {
        return MakeAtomicShared<NYP::NServiceDiscovery::THttpResolver>(config, FromString<TDuration>(config.GetConnectTimeout()), FromString<TDuration>(config.GetRequestTimeout()));
    }

    TSDManager::TSDManager(NConfig::IConfig& config, TSharedStatsManager& statsManager)
        : TSDManager(TSDConfig{config}, statsManager)
    {
    }


    TSDManager::TSDManager(const NYP::NServiceDiscovery::TSDConfig &config, TSharedStatsManager &statsManager)
        : TSDConfig(config)
        , NYP::NServiceDiscovery::TSDClient(*this, MakeCounterFactory(statsManager, *this), /*externalLog=*/true)
    {
        if (const TString& logPath = TSDConfig::GetLog()) {
            Log_ = MakeHolder<TLog>(logPath);
            AssignLog(Log_.Get());
        }
    }

    TSDManager::~TSDManager() {
        Stop();
    }

    void TSDManager::Start() {
        NYP::NServiceDiscovery::TEndpointSetManager::Start(FromString<TDuration>(GetUpdateFrequency()));
    }

    void TSDManager::SwitchStat(size_t workerId) {
        TCounters& counters = *this;
        for (auto& counter : counters) {
            counter->SwitchToWorker(workerId);
        }

        counters.clear();
        counters.shrink_to_fit();
    }

    void TSDManager::ReopenLog() {
        if (Log_) {
            Log_->ReopenLog();
        }
    }
}

