#include "textwad_merger.h"

#include <saas/rtyserver/components/oxy/textwad/ki_normalizer.h>
#include <saas/rtyserver/components/oxy/config.h> // for CheckPossibleModes (temporary)
#include <saas/rtyserver/config/realm_config.h>
#include <saas/rtyserver/model/component.h>
#include <robot/library/oxygen/indexer/library/wrangle/wrangle.h>
#include <kernel/doom/offroad_wad/offroad_ann_wad_io.h>
#include <kernel/doom/yandex/yandex_io.h>

//-------------------------------------------------------------------------------------------------
// TOxyKeyInvWadMerger : implementation #2 (via textwad/ki_normalizer.h)

struct TOxyKeyInvWadPossibleModes {
    bool WrangleMode = false;
    bool WrangleAttrsMode = false;
};

namespace {
    static const NOxygen::TOxygenOptions* GetOxygenOptions(const TString& realmName,  const TRTYServerConfig& config) {
        const NOxygen::TOxygenOptions* oxygenOptions = config.GetRealmListConfig().GetRealmConfigByConfigName(realmName).GetOxygenOptions();
        return oxygenOptions;
    }

    static TOxyKeyInvWadPossibleModes CheckPossibleModes(const NRTYServer::TMergeContext& context, const TRTYServerConfig& config) {
        const TRTYOxyConfig* oxyConfig = config.ComponentsConfig.Get<TRTYOxyConfig>(OXY_COMPONENT_NAME);
        CHECK_WITH_LOG(!!oxyConfig);
        const TRTYOxyConfig::TDbgOxyFlags& dbgFlags = oxyConfig->GetDbgOxyFlags();

        TFsPath dir = TFsPath(context.Context.Dests[0]);
        CHECK_WITH_LOG(context.Context.Dests.size() == 1);
        TOXYKeyInvNormalizer::TIndexTraits current = TOXYKeyInvNormalizer::GetIndexTraits(dir);
        TOxyKeyInvWadPossibleModes possible;
        if (current.RenamedGlobalKeyInv || current.GlobalKeyInv) {
            possible.WrangleMode = !dbgFlags.contains("newwad_external_merger");
        }
        if (current.RenamedGlobalKeyInv || current.GlobalKeyInv || current.IndexAttrsOld) {
            possible.WrangleAttrsMode = !dbgFlags.contains("newattrs_external_merger");
        }

        return possible;
    }

}

void TOxyKeyInvWadMerger::RebuildIndex(const TFsPath& dir, const TString& realmName, const TRTYServerConfig& config, const TOxyKeyInvWadPossibleModes& possible) {
    const NOxygen::TOxygenOptions* oxygenOptions = GetOxygenOptions(realmName, config);
    AssertCorrectConfig(oxygenOptions, "OxygenOptions are required for TOXYKeyInvNormalizer normalizer");

    TOXYKeyInvNormalizer::TIndexTraits current = TOXYKeyInvNormalizer::GetIndexTraits(dir);
    // Check whether the segment has either the expected indexes or the source data for wrangling
    CHECK_WITH_LOG(!possible.WrangleMode || (current.GlobalKeyInv || current.RenamedGlobalKeyInv || current.TextWad)); // A => B or C or D
    CHECK_WITH_LOG(!possible.WrangleAttrsMode || (current.IndexAttrsOld || current.GlobalKeyInv || current.RenamedGlobalKeyInv || current.IndexAttrs));

    TOXYKeyInvNormalizer::TIndexTraits expected = TOXYKeyInvNormalizer::GetIndexTraits(oxygenOptions);
    expected.GlobalKeyInv = current.GlobalKeyInv;
    expected.RenamedGlobalKeyInv = current.RenamedGlobalKeyInv;
    expected.IndexAttrs = possible.WrangleAttrsMode || current.IndexAttrs;
    expected.TextWad = possible.WrangleMode || current.TextWad;

    TOXYKeyInvNormalizer normalizer(config);
    bool res = normalizer.DoFix(expected, current, dir);
    if (!res)
        ythrow yexception() << "failed to rebuild TextWad";
}

bool TOxyKeyInvWadMerger::DoMerge(const NRTYServer::TMergeContext& context, const TRTYServerConfig& config) {
    try {
        auto possible = CheckPossibleModes(context, config);
        if (!possible.WrangleMode) {
            WARNING_LOG << "OXY TextWad merging disabled, expected from EXTBUILDER" << Endl;
        }

        if (!possible.WrangleAttrsMode) {
            WARNING_LOG << "OXY IndexAttrs merging(wrangling) disabled, expected from EXTBUILDER" << Endl;
        }

        if (possible.WrangleMode || possible.WrangleAttrsMode) {
            for (const TString& destination : context.Context.Dests) {
                RebuildIndex(TFsPath(destination), context.RealmName, config, possible);
            }
        }
    } catch (...) {
        FATAL_LOG << "TextWad merging failed: " << CurrentExceptionMessage() << Endl;
    }

    return true;
}

TOxyKeyInvWadMerger::TFactory::TRegistrator<TOxyKeyInvWadMerger> TOxyKeyInvWadMerger::KeyInvRegistrator("TKeyInvWadProcessor");
TOxyKeyInvWadMerger::TFactory::TRegistrator<TOxyKeyInvWadMerger> TOxyKeyInvWadMerger::AttrsRegistrator("TKeyInvAttrsWadProcessor");

//-------------------------------------------------------------------------------------------------
// TOxyKeyInvWrangleMerger : unused implementation (delete this)
//TODO:(yrum) remove class TOxyKeyInvWrangleMerger - not used in production

namespace {
    //Temporary implementation using Merge+Wrangle. Active only if "Wrangling" TextWad implementation is used.

    //TODO: (yrum) reimplement using NDoom::TOffroadAnnWadIoSortedMultiKeys reader

    using TKeyInvReader =  NDoom::THitTransformingIndexReader<NDoom::TIdentityHitTransformation,
            NDoom::TKeyFilteringIndexReader<NDoom::TEmptyPrefixExceptTelKeyFilter, NDoom::TYandexIo::TReader>>;
    using TWriterIo = NDoom::TOffroadAnnWadIoSortedMultiKeys;

    using TOxyKeyInvWadWrangler = NOxygen::TWrangler<TKeyInvReader,TWriterIo>;
}

void TOxyKeyInvWrangleMerger::WrangleTextWad(const TString& slaveOutputPrefix, const TString& wadOutputPrefix) {
    TOxyKeyInvWadWrangler wrangler("TOxyKeyInvWadWrangler", slaveOutputPrefix, wadOutputPrefix);
    wrangler.WrangleKeyInv();
}

bool TOxyKeyInvWrangleMerger::DoInitialize() {
    NOxygen::TWranglingProcessorBase* wrangler = dynamic_cast<NOxygen::TWranglingProcessorBase*>(Processor.Get());
    CHECK_WITH_LOG(wrangler);

    Processor = wrangler->GetSlave();
    CHECK_WITH_LOG(Processor);

    return TOxyKeyInvMerger::DoInitialize();
}

bool TOxyKeyInvWrangleMerger::DoMerge(const NRTYServer::TMergeContext& context, const TRTYServerConfig& config) {
    if (!TOxyKeyInvMerger::DoMerge(context, config)) {
        return false;
    }

    try {
        //We cannot reuse NOxygen::TWranglingProcessorBase::Finish() here, because of multiple dests
        for (const TString& destination : context.Context.Dests) {
            const TString prefix = TFsPath(destination) / "index";
            WrangleTextWad(prefix, prefix + "keyinv.");
        }
    } catch (...) {
        FATAL_LOG << "keyinvwad merging failed: " << CurrentExceptionMessage() << Endl;
        return false;
    }

    return true;
}

TOxyKeyInvWrangleMerger::TFactory::TRegistrator<TOxyKeyInvWrangleMerger> TOxyKeyInvWrangleMerger::Registrator("TKeyInvWrangleProcessor");
