#include "detach_task.h"

#include <saas/rtyserver/common/sharding.h>
#include <saas/rtyserver/indexer_core/index_dir.h>
#include <util/folder/filelist.h>

// TDeatchTask
const TVector<TString>& TDetachTask::GetSourceSegments() const {
    CHECK_WITH_LOG(IsCorrect);
    return SourceSegments;
}

const TDetachTask::TSegmentsForCopy& TDetachTask::GetSegmentsForCopy() const {
    CHECK_WITH_LOG(IsCorrect);
    return SegmentsForCopy;
}

const TVector<TString>& TDetachTask::GetDestSegments() const {
    CHECK_WITH_LOG(IsCorrect);
    return DestSegments;
}

bool TDetachTask::NeedToMerge() const {
    CHECK_WITH_LOG(IsCorrect);
    return !DestSegments.empty();
}

bool TDetachTask::DocumentIsAccepted(const TDocPlaceInfo& doc, ui32& interval) const {
    for (ui32 i = 0; i < Context.ShardIntervals.size(); ++i)
        if (Context.ShardIntervals[i].Check(doc.Shard)) {
            interval = i;
            return true;
        }
    return false;
}

bool TDetachTask::ShortProcessing(const TDocPlaceMap& shardsMap, TIndexControllerPtr index) {
    bool hasDenaed = false;
    bool hasAccepted = false;
    ui32 prevInterval = Max<ui32>();
    for (ui32 i = 0; i < shardsMap.size(); ++i) {
        if (!index->IsRemoved(i)) {
            ui32 interval;
            if (DocumentIsAccepted(shardsMap[i], interval)) {
                hasAccepted = true;
                if (prevInterval != Max<ui32>() && prevInterval != interval)
                    return false;
                prevInterval = interval;
            } else
                hasDenaed = true;
        }
    }
    if (!hasAccepted) {
        INFO_LOG << "segment not needed for detach: " << index->Directory().BaseName() << Endl;
        return true;
    } else if (hasDenaed)
        return false;
    SegmentsForCopy[prevInterval].push_back(index->Directory().BaseName());
    INFO_LOG << "segment full copy to interval " << prevInterval << ": " << index->Directory().BaseName() << Endl;
    return true;
}

void TDetachTask::DoBuildDecoder(IIndexStorage& storage) {
    TDirsList dl;
    //sort is a hack, really need to place prep_ after index_ segments SAAS-4773
    dl.Fill(Context.Config.GetRealmListConfig().GetMainRealmConfig().RealmDirectory, /*sort=*/true);

    const TRTYServerConfig& config = Context.Config;
    TM2NDecoder::TOptions options;
    options.SegmentSize = Context.DestSegmentSize;
    options.SizeDeviation = Context.DestSegmentSizeDeviation;
    options.MaxDeadlineDocs = config.GetMergerConfig().MaxDeadlineDocs;
    options.Pruning = config.Pruning && config.Pruning->PruningOn();

    TM2NDecoder* decoder = new TM2NDecoder(options);
    Decoder.Reset(decoder);

    const char* dirName;
    TDocumentsExtractor docsExtractor(Context.Sharding);
    while ((dirName = dl.Next()) != nullptr) {
        TIndexControllerPtr index = storage.GetIndexController(dirName);
        if (!!index) {
            SourceSegments.push_back(dirName);
            ui32 shard = NRTYServer::GetShard(dirName);
            DEBUG_LOG << "add source " << dirName << Endl;
            TDocPlaceMap shardsMap;
            docsExtractor.GetDocPlaceMap(*index, shardsMap);
            decoder->AddInfo(index->GetDDKManager());
            if (!ShortProcessing(shardsMap, index)) {
                for (ui32 i = 0; i < shardsMap.size(); ++i) {
                    ui32 interval = 0;
                    if (!index->IsRemoved(i) && DocumentIsAccepted(shardsMap[i], interval))
                        decoder->AddInfo(i, interval, shardsMap[i], shard);
                    else
                        decoder->AddInfo(i, -1, shardsMap[i], shard);
                }
            }
        }
    }
    decoder->Finalize();
    DestSegments.clear();
    DestSegments.resize(decoder->GetNewClustersCount());
    decoder->Print("Det ");
    for (ui32 interval = 0; interval < Context.ShardIntervals.size(); ++interval) {
        TVector<TClusterInfo> info = GetClusters(interval);
        for (ui32 i = 0; i < info.size(); ++i) {
            const TString newDir = info[i].FormatName(DIRPREFIX_INDEX);
            DEBUG_LOG << info[i].Id << "->>>>-" << newDir << Endl;
            DestSegments[info[i].Id] = newDir;
        }
    }
    for (ui32 i = 0; i < DestSegments.size(); ++i) {
        CHECK_WITH_LOG(!!DestSegments[i]);
    }
    IsCorrect = true;
}

TVector<TClusterInfo> TDetachTask::GetClusters(ui32 destId) const {
    return (dynamic_cast<TM2NDecoder*>(Decoder.Get()))->GetClusters(destId);
}

TString TDetachTask::DoBuildTempDest(IIndexStorage& /*storage*/, const TString& segment) const {
    return Context.DetachDirectory + "/" + GetName() + "_in_progress/" + segment;
}

TString TDetachTask::DoBuildFullDest(IIndexStorage& /*storage*/, const TString& segment) const {
    return Context.DetachDirectory + "/" + GetName() + "/" + segment;
}
