#include "final_index_processor.h"
#include <saas/rtyserver/common/sharding.h>
#include <saas/rtyserver/components/generator/manager.h>
#include <saas/rtyserver/config/config.h>
#include <saas/rtyserver/config/indexer_config.h>
#include <saas/rtyserver/config/realm_config.h>
#include <saas/rtyserver/indexer_core/index_component_storage.h>
#include <saas/rtyserver/indexer_core/index_dir.h>
#include <saas/rtyserver/search/factory/factory.h>

#include <util/stream/direct_io.h>
#include <util/system/rwlock.h>

TFinalIndexProcessor::~TFinalIndexProcessor() {
    UnlinkIndexData();
    VERIFY_WITH_LOG(!CounterLinks, "Incorrect usage Link/Unlink pairs for TFinalIndexProcessor %s", ToString(CounterLinks).data());
}

void TFinalIndexProcessor::LinkIndexData() {
    INFO_LOG << "LinkIndexData for " << IndexDir.BaseName() << Endl;
    VERIFY_WITH_LOG(!CounterLinks, "try to link already linked index: %s", IndexDir.BaseName().data())
    Managers.Open();
    CounterLinks++;
}

void TFinalIndexProcessor::UnlinkIndexData() {
    INFO_LOG << "UnlinkIndexData for " << IndexDir.BaseName() << Endl;
    VERIFY_WITH_LOG(CounterLinks == 1, "try to unlink already unlinked index: %s", IndexDir.BaseName().data())
    Managers.Close();
    CounterLinks--;
}

TFinalIndexProcessor::TFinalIndexProcessor(const TPathName& indexDir, const TRTYServerConfig& config, bool useGlobalFileMapping, NRTYServer::EExecutionContext execCtx)
    : IndexDir(indexDir)
    , Config(config)
    , RealmConfig(Config.GetRealmListConfig().GetRealmConfig(IndexDir.PathName()))
    , Shard(NRTYServer::GetShard(indexDir.BaseName()))
    , IndexType(GetIndexType(indexDir))
    , Managers(Config)
    , ExecutionContext(execCtx)
    , CounterLinks(0)
{
    VERIFY_WITH_LOG(NFs::Exists(IndexDir.PathName()), "Directory %s does not exist. Could not initialize TFinalIndexProcessor", IndexDir.PathName().data());

    TIndexComponentsStorage::Instance().Fill(&Managers, NRTYServer::TManagerConstructionContext(RealmConfig.GetIndexerConfigDisk(), IndexDir, *this, IndexType, useGlobalFileMapping, Config.IsReadOnly, ExecutionContext));
    IndexManager = Managers.GetIndexManager();
    DDKManager = Managers.GetDDKManager();
    UrlToDocIdManager = Managers.GetUrlToDocIdManager();
    if (config.VerificationPolicy != TRTYServerConfig::vpIndexingUniqueUrls) {
        AssertCorrectConfig(!DDKManager->IsReadOnly() || Config.IsReadOnly, "Can't use read-only component on writable index");
    }
}

ui32 TFinalIndexProcessor::GetDeletedDocumentsCount() const {
    VERIFY_WITH_LOG(IndexManager, "Incorrect IndexManager usage");
    return IndexManager->GetDeletedDocumentsCount();
}

ui32 TFinalIndexProcessor::GetDocumentsCountImpl(bool keyWithDeleted) const {
    VERIFY_WITH_LOG(IndexManager, "Incorrect IndexManager usage");
    return IndexManager->GetDocumentsCount() - (keyWithDeleted ? 0 : IndexManager->GetDeletedDocumentsCount());
}

ui32 TFinalIndexProcessor::GetSearchableDocumentsCountImpl() const {
    VERIFY_WITH_LOG(IndexManager, "Incorrect IndexManager usage");
    return IndexManager->GetSearchableDocumentsCount();
}

void TFinalIndexProcessor::Initialize() {
    LinkIndexData();
}

bool TFinalIndexProcessor::DocumentIsRemoved(const ui32 docid) const {
    return IndexManager->IsRemoved(docid);
}

NRTYServer::ERealm TFinalIndexProcessor::GetRealm() const {
    return RealmConfig.Type;
}

TString TFinalIndexProcessor::GetRealmName() const {
    return RealmConfig.ConfigName;
}

IIndexController::EIndexType TFinalIndexProcessor::GetIndexType(const TPathName& indexDir) {
    IIndexController::EIndexType indexType;
    if (NRTYServer::HasIndexNamePrefix(indexDir.BaseName(), DIRPREFIX_INDEX)) {
        indexType = IIndexController::FINAL;
    } else if (NRTYServer::HasIndexNamePrefix(indexDir.BaseName(), DIRPREFIX_PREP)) {
        indexType = IIndexController::PREPARED;
    } else {
        indexType = IIndexController::DISK;
    }
    return indexType;
}

const IUrlIdInfo* TFinalIndexProcessor::GetUrlIdInfo() const {
    if (!IndexManager) {
        return nullptr;
    }

    switch (IndexManager->GetUrlIdSource()) {
    case NRTYServer::EUrlIdSource::DDK:
        return DDKManager ? DDKManager->GetUrlIdInfo() : nullptr;
    default:
        return IndexManager->GetUrlIdInfo();
    }
}
