#include "timeout.h"

#include <balancer/kernel/balancer/algorithm.h>
#include <balancer/kernel/balancing/backend.h>
#include <balancer/kernel/fs/kv_file_consumer.h>
#include <balancer/kernel/log/errorlog.h>

#include <balancer/modules/balancer/policies/policy_factory.h>

namespace NSrvKernel::NTimeout {

struct TPolicyFactoryTls {
    TSharedFileReReader ParamsFileReader;
    TSharedFileReReader::TData ParamsFileData;
};

POLICY_FACTORY_WITH_TLS(timeout_policy, TPolicyFactoryTls), public TModuleParams {
    TPolicyFactory(const TModuleParams& mp)
        : TPolicyBase(mp.Control->GetCountOfChildren())
        , TModuleParams(mp)
    {
        Config->ForEach(this);
        CheckConfiguration();
    }

    THolder<IPolicy> ConstructPolicy(const TStepParams& params) noexcept override {
        UpdateParams(*params.Descr);
        return MakeHolder<TTimeoutPolicy>(TPolicyBase::ConstructPolicy(params), Timeout_);
    }

    THolder<TPolicyFactoryTls> InitTls(IWorkerCtl* process) override {
        auto tls = MakeHolder<TPolicyFactoryTls>();
        if (ParamsFilename_) {
            tls->ParamsFileReader = process->SharedFiles()->FileReReader(ParamsFilename_, TDuration::Seconds(1));
        }
        return tls;
    }

private:
    void DoUpdateParams(const TStringBuf contents, const TConnDescr& descr) noexcept {
        using TTimingFileReaction = NSrvKernel::TBaseFileReaction<TString, TDuration, true>;
        using TTimingFileConsumer = TKvFileConsumer<TTimingFileReaction>;

        TTimingFileConsumer consumer;
        try {
            ProcessKvData(consumer, contents);
            const auto& kv = consumer.Storage();
            if (const TDuration* timeout = MapFindPtr(kv, "timeout")) {
                Timeout_ = *timeout;
            }
        } catch (const yexception& e) {
            LOG_ERROR(TLOG_ERR, descr, "timeout_policy Error parsing params_file: " << e.what());
        }
    }

    void UpdateParams(const TConnDescr& descr) noexcept {
        auto& tls = GetTls(&descr.Process());
        const auto& data = tls.ParamsFileReader.Data();
        if (data.Id() != tls.ParamsFileData.Id()) {
            tls.ParamsFileData = data;
            DoUpdateParams(tls.ParamsFileData.Data(), descr);
        }
    }

private:
    START_PARSE {
        ON_KEY("timeout", Timeout_) {
            return;
        }

        ON_KEY("params_file", ParamsFilename_) {
            return;
        }

        if (Configure(key, Copy(value->AsSubConfig()))) {
            return;
        }
    } END_PARSE

private:
    TString ParamsFilename_;
    TDuration Timeout_;
};

INodeHandle<IPolicyFactory>* Handle() {
    return TPolicyFactory::Handle();
}

}  // namespace NSrvKernel::NTimeout
