#include "weights_file.h"

#include <balancer/kernel/ctl/ctl.h>
#include <balancer/kernel/log/errorlog.h>
#include <util/generic/strbuf.h>
#include <util/generic/maybe.h>

namespace NSrvKernel::NModBalancer {

static constexpr TStringBuf WeigthsFileTagPrefix = "__weights_file_tag_";

static TMaybe<TString> ExtractWeightsFileTagFromKey(const TString& key) noexcept {
    if (!key.StartsWith(WeigthsFileTagPrefix)) {
        return Nothing();
    }

    return key.substr(WeigthsFileTagPrefix.size());
}

// ExtractWeightsFileTag returns true, if multiple tags were found in given consumer.
static bool ExtractWeightsFileTag(TExternalWeightsFileConsumer& consumer, TMaybe<TString>* tag) noexcept {
    TMaybe<TString> foundTag;
    for (auto& entry : consumer.Storage()) {
        auto tag = ExtractWeightsFileTagFromKey(entry.first);
        if (tag.Defined()) {
            if (foundTag.Defined()) {
                return true;
            }
            foundTag = std::move(tag);
        }
    }

    *tag = foundTag;
    return false;
}

TExternalWeightsFileReReader::TExternalWeightsFileReReader(IWorkerCtl& process, TString weightsFile, TDuration interval, TString name) noexcept
    : FileName_(std::move(weightsFile))
    , ReReader_(process.SharedFiles()->FileReReader(FileName_, interval))
    , Name_(std::move(name))
{
}

bool TExternalWeightsFileReReader::UpdateWeights(const TConnDescr& descr) noexcept {
    const auto& data = ReReader_.Data();
    TStringBuf NAME{Name_};
    if (data.Id() != Id_) {
        Id_ = data.Id();
        TStringBuf contents = data.Data();
        Entries_.clear();
        TExternalWeightsFileConsumer consumer;
        try {
            ProcessKvData(consumer, contents);
            if (ExtractWeightsFileTag(consumer, &Tag_)) {
                LOG_ERROR(TLOG_ERR, descr, "refusing weights_file: multiple tags found");
                return false;
            }
            Entries_ = consumer.Storage();
            return true;
        } catch (const yexception& e) {
            LOG_ERROR(TLOG_ERR, descr, "UpdateWeights Error parsing weight for weights_file: " << e.what());
        }
    }
    return false;
}

void TExternalWeightsFileReReader::WriteWeightsFileTag(NJson::TJsonWriter& out) const noexcept {
    if (FileName_.Empty()) {
        return;
    }

    out.OpenMap();
    out.Write("weights_file", FileName_);
    if (Tag_.Defined()) {
        out.Write("tag", *Tag_);
    }
    out.CloseMap();
}

} // namespace NModBalancer
