#include "tuples_list_and_filters.h"

#include <saas/rtyserver/config/const.h>
#include <saas/rtyserver/config/layers.h>
#include <saas/rtyserver/components/fullarchive/config.h>

#include <library/cpp/logger/global/global.h>

TTuplesListsAndFilters::TTupleNameSet TTuplesListsAndFilters::ITuplesListsAndFiltersInitializer::BuildFilterTuples(TRTYOxyConfig::TFilterType filterType) const {
    TTupleNameSet result;
    switch (filterType) {
    case TRTYOxyConfig::NO:
        break;
    case TRTYOxyConfig::FOR_SAVE:
    case TRTYOxyConfig::FOR_INDEX: {
        const auto fullSet = BuildTuplesInfo(false).GetFullTuplesSet();
        const auto& additionalRequired = GetAdditionalRequiredTuples();
        const auto& additionalMerge = GetAdditionalRequiredMergeTuples();
        result.insert(fullSet.begin(), fullSet.end());
        result.insert(additionalRequired.begin(), additionalRequired.end());
        result.insert(additionalMerge.begin(), additionalMerge.end());
        break;
    }
    case TRTYOxyConfig::FOR_MERGE: {
        const auto fullSet = BuildTuplesInfo(true).GetFullTuplesSet();
        const auto& additionalMerge = GetAdditionalRequiredMergeTuples();
        result.insert(fullSet.begin(), fullSet.end());
        result.insert(additionalMerge.begin(), additionalMerge.end());
        break;
    }
    case TRTYOxyConfig::FOR_MERGE_COMPLEMENT: {
        TTupleNameSet necessaryTuplesForIndex = BuildFilterTuples(TRTYOxyConfig::FOR_INDEX);
        TTupleNameSet necessaryTuplesForMerge = BuildFilterTuples(TRTYOxyConfig::FOR_MERGE);
        for (const auto& layer : necessaryTuplesForIndex) {
            if (!necessaryTuplesForMerge.contains(layer))
                result.insert(layer);
        }
        break;
    }
    default:
        FAIL_LOG("Incorrect filter type: %d", (int)filterType);
    }
    return result;
}

TTuplesListsAndFilters::TTuplesListsAndFilters(const ITuplesListsAndFiltersInitializer& initializer)
    : FilterTuples(TRTYOxyConfig::COUNT_FILTER_TYPES)
    , HasFullCoverage(false)
{

    TuplesForMergeInfo = initializer.BuildTuplesInfo(true);
    TuplesForIndexInfo = initializer.BuildTuplesInfo(false);

    FilterTuples[TRTYOxyConfig::FOR_INDEX] = initializer.BuildFilterTuples(TRTYOxyConfig::FOR_INDEX);
    FilterTuples[TRTYOxyConfig::FOR_MERGE] = initializer.BuildFilterTuples(TRTYOxyConfig::FOR_MERGE);
    FilterTuples[TRTYOxyConfig::FOR_SAVE] = initializer.BuildFilterTuples(TRTYOxyConfig::FOR_SAVE);
    Layers = initializer.GetConfig().ComponentsConfig.Get<TRTYFullArchiveConfig>(FULL_ARCHIVE_COMPONENT_NAME)->GetActiveLayersFinal();
    Layers.insert(NRTYServer::NFullArchive::FullLayer);

    TLayersSetByFilterType layersListByFilterType;
    for (const auto& ftLayers : initializer.GetConfig().ComponentsConfig.Get<TRTYOxyConfig>(OXY_COMPONENT_NAME)->GetLayers())
        for (const auto& layer : ftLayers.second)
            if (Layers.contains(layer))
                layersListByFilterType[ftLayers.first].insert(layer);
            else
                WARNING_LOG << "layer " << layer << " configured as " << ftLayers.first << " but not active, will ignore";

    FilterTuples[TRTYOxyConfig::FOR_MERGE_COMPLEMENT] = initializer.BuildFilterTuples(TRTYOxyConfig::FOR_MERGE_COMPLEMENT);

    for (const auto& layersList : layersListByFilterType) {
        for (const auto& layer : layersList.second)
            FilteredTuplesByLayer[layer] = FilterTuples[layersList.first];
    }

    for (i32 filterType = TRTYOxyConfig::FOR_MERGE; filterType >= TRTYOxyConfig::NO && (!LayerForMerge || !HasFullCoverage); --filterType) {
        TRTYOxyConfig::TFilterType ft = (TRTYOxyConfig::TFilterType)filterType;
        TLayersSetByFilterType::const_iterator i = layersListByFilterType.find(ft);
        if (i == layersListByFilterType.end() || i->second.empty())
            continue;
        switch (ft) {
            case TRTYOxyConfig::FOR_MERGE:
                LayerForMerge = *i->second.begin();
                break;
            case TRTYOxyConfig::FOR_MERGE_COMPLEMENT:
                HasFullCoverage = true;
                break;
            case TRTYOxyConfig::FOR_INDEX:
            case TRTYOxyConfig::NO:
                HasFullCoverage = true;
                if (!LayerForMerge)
                    LayerForMerge = *i->second.begin();
                break;
            default:
                break;
        }
    }
    VERIFY_WITH_LOG(!!LayerForMerge, "there is no layer for merge");

    for (ui32 i = 0; i < FilterTuples.size(); ++i) {
        const auto& tuples = FilterTuples[i];
        INFO_LOG << TRTYOxyConfig::TFilterType(i) << ": " << JoinStrings(tuples.begin(), tuples.end(), " ") << Endl;
    }
}

const NOxygen::TTuplesUsageInfo& TTuplesListsAndFilters::GetRequiredTuples(bool forMerge) const {
    if (forMerge)
        return TuplesForMergeInfo;

    return TuplesForIndexInfo;
}

const TTuplesListsAndFilters::TTupleNameSet& TTuplesListsAndFilters::GetFilterTuples() const {
    return FilterTuples[TRTYOxyConfig::FOR_SAVE];
}

const TTuplesListsAndFilters::TTupleNameSet* TTuplesListsAndFilters::GetFilterTuples(const TString& layer) const {
    TFilteredTuplesByLayer::const_iterator i = FilteredTuplesByLayer.find(layer);
    if (i != FilteredTuplesByLayer.end())
        return &i->second;
    return &EmptyTupleList;
}

const TSet<TString>& TTuplesListsAndFilters::GetLayersSet() const {
    return Layers;
}


const TString& TTuplesListsAndFilters::GetLayerForMerge() const {
    return LayerForMerge;
}

bool TTuplesListsAndFilters::GetHasFullCoverage() const {
    return HasFullCoverage;
}

const TTuplesListsAndFilters::TTupleNameSet TTuplesListsAndFilters::EmptyTupleList;
