#include "rearrange.h"

#include <library/cpp/logger/global/global.h>
#include <util/string/split.h>

bool TCustomRearrangeParams::Init(const TString& name, const TString& configString) {
    Name = name;

    if (configString == "empty")
        return true;

    TVector<TString> splittedParameters;
    StringSplitter(configString).Split(';').SkipEmpty().Collect(&splittedParameters);
    for (const auto& keyVal : splittedParameters) {
        TVector<TString> vals;
        StringSplitter(keyVal).Split('=').SkipEmpty().Collect(&vals);
        if (vals.size() != 2) {
            ERROR_LOG << "Incorrect rearrange rule parametes" << keyVal << Endl;
            return false;
        }
        Parameters[vals[0]] = vals[1];
    }
    return true;
}

void ICustomRearrange::FormCgiParams(TCgiParameters& cgi, TSelfFlushLogFrame* eventLogFrame) {
    if (DoFormCgiParameters(cgi) && eventLogFrame) {
        eventLogFrame->LogEvent<NEvClass::DebugMessage>(
                GetName() + "\tcgi after: " + cgi.Print());
    }

}

bool ICustomRearrange::ProcessSharding(IReplyContext& context,
                                       const NSaas::TShardsDispatcher& shardDispatcher,
                                       const NSaas::TShardIntervals& shardIntervals,
                                       const TVector<THolder<NScatter::ISource>>& sources,
                                       NProxyMeta::TKeysForSource& keysForSources,
                                       TSelfFlushLogFrame* eventLogFrame) const
{
    size_t before = keysForSources.size();
    bool processed = DoProcessSharding(context, shardDispatcher, shardIntervals, sources, keysForSources);
    if (eventLogFrame && processed) {
        size_t after = keysForSources.size();
        eventLogFrame->LogEvent<NEvClass::DebugMessage>(
                GetName() + "\tsources before: " + ToString(before) + "\tsources after: " + ToString(after));
    }
    return processed;
}

void ICustomRearrange::ProcessReplies(TVector<NProxyMeta::TSourceReply>& replies, ICustomReportBuilder& builder,
                                      IReplyContext& context, TSelfFlushLogFrame* eventLogFrame) const
{
    if (eventLogFrame) {
        eventLogFrame->LogEvent(NEvClass::TRearrangeInfo(2, GetName(), replies.size()));
    }
    DoProcessReplies(replies, builder, context);
    if (eventLogFrame) {
        eventLogFrame->LogEvent(NEvClass::TRearrangeInfo(3, GetName(), replies.size()));
    }
}

TRearrangeEngine::TRearrangeEngine(TVector<ICustomRearrange::TPtr>&& rearranges, TSelfFlushLogFrame* eventLogFrame)
    : Rearranges(std::move(rearranges))
    , EventLogFrame(eventLogFrame)
{
}

void TRearrangeEngine::FormCgiParams(TCgiParameters& cgi) {
    for (const auto& rearrangePtr: Rearranges) {
        rearrangePtr->FormCgiParams(cgi, EventLogFrame);
    }
}

bool TRearrangeEngine::ProcessSharding(IReplyContext& context,
    const NSaas::TShardsDispatcher& shardDispatcher,
    const NSaas::TShardIntervals& shardIntervals,
    const TVector<THolder<NScatter::ISource>>& sources,
    NProxyMeta::TKeysForSource& keysForSource
    ) const
{
    for (const auto& rearrangePtr: Rearranges) {
        if (rearrangePtr->ProcessSharding(context, shardDispatcher,
            shardIntervals, sources, keysForSource, EventLogFrame))
        {
            return true;
        }
    }
    return false;
}

void TRearrangeEngine::ProcessReplies(TVector<NProxyMeta::TSourceReply>& replies,
    ICustomReportBuilder& builder, IReplyContext& context) const
{
    for (auto it = Rearranges.crbegin(); it != Rearranges.crend(); ++it) {
        (*it)->ProcessReplies(replies, builder, context, EventLogFrame);
    }
}

TRearrangeEngineFactory::TRearrangeEngineFactory(const TVector<TCustomRearrangeParams>& params) {
    Factories.reserve(params.size());
    for (const auto& rearr: params) {
        ICustomRearrangeFactory::TPtr rule = THolder(ICustomRearrangeFactory::TFactory::Construct(rearr.Name, rearr));
        CHECK_WITH_LOG(!!rule.Get());
        Factories.emplace_back(rearr.Name + "_off", std::move(rule));
    }
}

TRearrangeEngine TRearrangeEngineFactory::CreateEngine(const TCgiParameters& parameters, TSelfFlushLogFrame* eventLogFrame) const {
    TVector<ICustomRearrange::TPtr> rearranges;
    rearranges.reserve(Factories.size());
    for (const auto& entry: Factories) {
        if (!parameters.Has("rearr", entry.first))
            rearranges.push_back(entry.second->CreateRerrangeInstance());
    }

    return TRearrangeEngine(std::move(rearranges), eventLogFrame);
}

void TRearrangeSignals::CreateServiceSignals(const TString& service, const TVector<TCustomRearrangeParams>& params) {
    auto &instances = Instances[service];
    instances.reserve(params.size());
    for (const auto& rearr: params) {
        if (ICustomRearrangeSignals::TFactory::Has(rearr.Name)) {
            ICustomRearrangeSignals::TPtr signals = THolder(ICustomRearrangeSignals::TFactory::Construct(rearr.Name, rearr));
            CHECK_WITH_LOG(!!signals.Get());
            instances.emplace_back(std::move(signals));
        }
    }
}

void TRearrangeSignals::DrillServiceSignals(const TString& service, TUnistat& unistat) const {
    if (!Instances.contains(service))
        return;

    const auto &instances = Instances.at(service);
    for (const auto& rearrangePtr: instances) {
        rearrangePtr->DrillCustomSignals(service, unistat);
    }
}
