#include "final_index_normalizer.h"

#include <saas/library/daemon_base/daemon/messages.h>
#include <saas/rtyserver/components/generator/manager.h>
#include <saas/rtyserver/config/config.h>
#include <saas/rtyserver/indexer_core/index_component_storage.h>
#include <saas/util/logging/exception_process.h>

#include <kernel/groupattrs/docsattrs.h>

#include <library/cpp/balloc/optional/operators.h>

TFinalIndexNormalizer::TFinalIndexNormalizer(const TPathName& indexDir, const TRTYServerConfig& config, NRTYServer::EExecutionContext execCtx)
    : TFinalIndexProcessor(indexDir, config, false, execCtx)
{}

TFinalIndexNormalizer::~TFinalIndexNormalizer() {
}

TFinalIndexNormalizer* TFinalIndexNormalizer::Create(const TPathName& indexDir, const TRTYServerConfig& config, NRTYServer::EExecutionContext execCtx) {
    TFinalIndexNormalizer* result = new TFinalIndexNormalizer(indexDir, config, execCtx);
    result->Initialize();
    return result;
}

const IKeysAndPositions* TFinalIndexNormalizer::GetYndex() const {
    return Yndex.Get();
}

NGroupingAttrs::TDocsAttrs* TFinalIndexNormalizer::GetGroupsAttrs() const {
    return DocsAttrs.Get();
}

void TFinalIndexNormalizer::LinkIndexData() {
    VERIFY_WITH_LOG(!IsLinked(), "Secondary linking for %s", IndexDir.PathName().data());
    const TString& index = IndexDir.PathName() + "/index";
    TRY
        Yndex.Reset(Managers.GetIndexManager()->BuildYndex());
        if (NGroupingAttrs::TDocsAttrs::IndexExists(index)) {
            DocsAttrs.Reset(new NGroupingAttrs::TDocsAttrs(NGroupingAttrs::CommonMode));
        }
        TFinalIndexProcessor::LinkIndexData();
        return;
    CATCH("On LinkIndexData");
    AbortFromCorruptedIndex("Incorrect normalization: LinkIndexData");
}

void TFinalIndexNormalizer::UnlinkIndexData() {
    TRY
        Yndex.Reset(nullptr);
        DocsAttrs.Reset(nullptr);
        TFinalIndexProcessor::UnlinkIndexData();
        return;
    CATCH("On UnlinkIndexData");
    VERIFY_WITH_LOG(false, "Incorrect normalization: UnlinkIndexData");
}

void TFinalIndexNormalizer::CheckOrFail() {
    try {
        INFO_LOG << IndexDir.BaseName() << " check started" << Endl;
        bool allright = TIndexComponentsStorage::Instance().AllRight(NRTYServer::TNormalizerContext(this, IndexDir, Managers, Config, IndexType));
        AssertCorrectIndex(allright, "An index is corrupted and cannot be repaired in read-only mode: %s", IndexDir.BaseName().data());
        INFO_LOG << IndexDir.BaseName() << " check finished" << Endl;
    } catch (...) {
        TString errorMessage = CurrentExceptionMessage();
        AbortFromCorruptedIndex("TFinalIndexNormalier failed: %s", errorMessage.data());
    }
}

bool TFinalIndexNormalizer::CheckAndRepair() {
    try {
        INFO_LOG << IndexDir.BaseName() << " normalization started" << Endl;
        TIndexComponentsStorage::Instance().CheckAndFix(NRTYServer::TNormalizerContext(this, IndexDir, Managers, Config, IndexType));
        INFO_LOG << IndexDir.BaseName() << " normalization finished successfully" << Endl;
        return true;
    } catch (...) {
        TString errorMessage = CurrentExceptionMessage();
        AbortFromCorruptedIndex("TFinalIndexNormalier failed: %s", errorMessage.data());
    }
    ERROR_LOG << IndexDir.BaseName() << " normalization finished unsuccessfully" << Endl;
    return false;
}

void TFinalIndexNormalizer::Process(void* /*ThreadSpecificResource*/) {
    ThreadDisableBalloc();

    THolder<TFinalIndexNormalizer> suicide(this);
    if (Config.IsReadOnly) {
        CheckOrFail();
    } else {
        VERIFY_WITH_LOG(CheckAndRepair(), "Unknown error on destruct TFinalIndexNormalizer")
    }
}

bool TFinalIndexNormalizer::BuildServiceFiles(const TPathName& indexDir, const TRTYServerConfig& config, NRTYServer::EExecutionContext execCtx) {
    THolder<TFinalIndexNormalizer> worker(Create(indexDir, config, execCtx));
    return worker->CheckAndRepair();
}
