#include "index.h"
#include "manager.h"
#include "component.h"

#include <saas/rtyserver/common/common_messages.h>
#include <saas/rtyserver/common/search_area_modifier.h>
#include <saas/rtyserver/search/context/rty_search_context.h>
#include <saas/rtyserver/search/processors/abstract/abstract.h>

#include <library/cpp/unistat/unistat.h>

ui32 IRemapperUrlDocId::DoRemapUrls2DocIds(const TVector<TDocSearchInfo>& docInfos, TVector<ui32>& docIds) const {
    ui32 result = 0;
    for (auto&& url : docInfos) {
        ui32 docId;
        if (RemapUrl2DocId(url, docId)) {
            docIds.push_back(docId);
            ++result;
        } else {
            docIds.push_back(Max<ui32>());
        }
    }
    return result;
}

// Copy-paste, maybe templates?

ui32 IRemapperUrlDocId::DoRemapUrls2DocIdCandidates(const TVector<TDocSearchInfo>& docInfos, TVector<TDocIdCandidate>& docIds) const {
    ui32 result = 0;
    for (auto&& url : docInfos) {
        TDocIdCandidate docId(url);
        if (RemapUrl2DocIdCandidate(url, docId)) {
            docIds.push_back(docId);
            ++result;
        } else {
            docId.UnsetDocId();
            docIds.push_back(docId);
        }
    }
    return result;
}

ui32 IRemapperUrlDocId::RemapUrls2DocIds(const TVector<TDocSearchInfo>& docInfos, TVector<ui32>& docIds) const {
    docIds.clear();
    docIds.reserve(docInfos.size());
    return DoRemapUrls2DocIds(docInfos, docIds);
}

ui32 IRemapperUrlDocId::RemapUrls2DocIdCandidates(const TVector<TDocSearchInfo>& docInfos, TVector<TDocIdCandidate>& docIds) const {
    docIds.clear();
    docIds.reserve(docInfos.size());
    return DoRemapUrls2DocIdCandidates(docInfos, docIds);
}


ui32 IIndexController::RemoveAll() {
    TVector<ui32> docIds;
    FillDocIds(docIds);
    return RemoveDocIds(docIds);
}

ui32 IIndexController::RemoveDocIdsUnsafe(const TVector<ui32>& docIds) {
    if (!docIds.size())
        return 0;
    {
        TReadGuard rg(MutexCallbacks);
        for (auto&& i : Callbacks) {
            i->OnBeforeRemove(docIds);
        }
    }
    if (IS_LOG_ACTIVE(TLOG_DEBUG) && GetDDKManager()) {
        for (auto&& docid : docIds) {
            DEBUG_LOG << "action=delete;hash=" << GetDDKManager()->GetIdentifier(docid).Quote() << ";docid=" << docid << ";index=" << Directory().BaseName() << Endl;
        }
    }
    return DoRemoveDocIdsUnsafe(docIds);
}

ui32 IIndexController::RemoveDocIds(const TVector<ui32>& docIds) {
    TGuardIndexModification gim(Self);
    return RemoveDocIdsUnsafe(docIds);
}

ui32 IIndexController::MarkForDelete(const TVector<ui32>& docIds, ui32 marker) {
    TGuardIndexModification gim(Self);
    {
        TReadGuard rg(MutexCallbacks);
        for (auto&& i : Callbacks) {
            i->OnBeforeMark(docIds, marker);
        }
    }
    if (IS_LOG_ACTIVE(TLOG_DEBUG) && GetDDKManager()) {
        for (auto&& docid : docIds) {
            DEBUG_LOG << "action=mark_for_delete;hash=" << GetDDKManager()->GetIdentifier(docid).Quote() << ";docid=" << docid << ";index=" << Directory().BaseName() << ";marker=" << marker << Endl;
        }
    }
    return MarkDocIdsForDeleteUnsafe(docIds, marker);
}

ui32 IIndexController::RemoveOldDocuments(ui32 currentTimeMinutes) {
    const IDDKManager* ddk = GetDDKManager();
    if (!ddk) {
        return Max<ui32>();
    }
    if (!ddk->IsDeadlineEnabled()) {
        INFO_LOG << Directory().BaseName() << " : old documents checker disabled" << Endl;
        return Max<ui32>();
    }

    INFO_LOG << Directory().BaseName() << " : old documents checker...(" << currentTimeMinutes << ")" << Endl;
    ui32 deleted = 0;
    ui32 nextDeadline = Max<ui32>();
    TVector<ui32> deadDocs;
    {
        TGuardIndexModification gim(Self);
        TVector<ui32> docIds;
        FillDocIds(docIds);
        for (const ui32 docId : docIds) {
            if (const ui32 deadline = ddk->GetDeadlineIfEnabled(docId)) {
                if (deadline <= currentTimeMinutes) {
                    deadDocs.push_back(docId);
                } else {
                    nextDeadline = Min(nextDeadline, deadline);
                }
            }
        }
        deleted = RemoveDocIdsUnsafe(deadDocs);
        TUnistat::Instance().PushSignalUnsafe("deadline-doc-count", deleted);
    }
    INFO_LOG << Directory().BaseName() << " : old documents checker...OK(" << deadDocs.size() << ")/" << nextDeadline << Endl;
    INFO_LOG << Directory().BaseName() << " : deleted " << deleted << " documents" << Endl;
    return nextDeadline;
}

void IIndexController::RegisterIndex() {
    SendGlobalMessage<TMessageRegisterIndex>(Self);
    RegisterSearcher();
}

void IIndexController::UnRegisterIndex() {
    UnregisterSearcher();
    SendGlobalMessage<TMessageUnregisterIndex>(Self);
}

void IIndexController::RegisterSearcher() {
    if (HasSearcher()) {
        TGuardOfTransactionSearchArea gtsa;
        SendGlobalMessage<TMessageRegisterSearcher>(Self);
    }
}

void IIndexController::UnregisterSearcher() {
    if (HasSearcher()) {
        TGuardOfTransactionSearchArea gtsa;
        SendGlobalMessage<TMessageUnregisterSearcher>(Self);
    }
}

ERTYSearchResult IIndexController::SearchCustom(ICustomReportBuilder& reportBuilder, const TRTYSearchRequestContext& searchContext) const {
    TString component = searchContext.GetComponentName();
    auto* manager = GetManager(component);
    if (manager) {
        reportBuilder.AddReportProperty("CustomSource", GetSearcherId());
        return manager->Search(searchContext, reportBuilder, *this);
    } else {
        reportBuilder.AddErrorMessage("Can't initialize search context: " + GetSearcherId());
    }
    return SR_NOT_FOUND;
}

TComponentSearcher::TPtr IIndexController::InitSpecialSearch(const TRTYSearchRequestContext& context, TMessagesCollector& errors) const {
    CHECK_WITH_LOG(context.UseFilters());

    TAtomicSharedPtr<TDocProcessors> searchFilters(new TDocProcessors());
    TSet<TString> components;
    Singleton<NRTYServer::IIndexComponent::TFactory>()->GetKeys(components);

    TComponentSearcher::TPtr result;
    for (const auto& componentName : components) {
        const NRTYServer::IIndexComponentManager* manager = GetManager(componentName);
        if (manager) {
            const ISearchFilter* filterManager = dynamic_cast<const ISearchFilter*>(manager);
            if (filterManager) {
                TComponentSearcher::TPtr searcher = filterManager->CreateSpecialSearcher();
                CHECK_WITH_LOG(searcher);

                if (!searcher->IsUsed(context)) {
                    continue;
                }

                if (!searcher->ParseContext(context, errors)) {
                    return nullptr;
                }

                if (result) {
                    errors.AddMessage(__LOCATION__, "comp_search mode is claimed by more than one component");
                    return nullptr;
                }

                result = std::move(searcher);
            }
        }
    }

    if (!result) {
        errors.AddMessage(__LOCATION__, "Can't initalize any filter");
        return nullptr;
    }
    return result;
}
