#include "server.h"

#include <saas/rtyserver/common/common_messages.h>
#include <saas/rtyserver/common/message_collect_server_info.h>
#include <saas/rtyserver/common/message_search_doc_ids.h>
#include <saas/rtyserver/config/searcher_config.h>
#include <saas/rtyserver/search/common_search_request.h>

#include <search/idl/meta.pb.h>

TString TMainSearchServer::Name() const {
    return "HttpMetaSearchServer";
}

void TMainSearchServer::OnFailRequest(int /*failstate*/) {
    SearchEngineManager.GetMainSearchServerMetrics()->DroppedRequests.Inc();
}

TMainSearchServer::TMainSearchServer(TSearchEnginesManager& sem, const NRTYServer::TSearcherConfig& config, const TDefaultKeyPrefix* defaultKeyPrefix)
   : TSearchServerBase(config.ServerOptions)
   , TMetaSearchServerFeatures(sem, config, defaultKeyPrefix)
{
    RegisterGlobalMessageProcessor(this);
}

TMainSearchServer::~TMainSearchServer() {
    UnregisterGlobalMessageProcessor(this);
}

ui32 TMainSearchServer::ProcessPageQuery(TString query, int page, TMessageSearchDocIds* message) {
    using ::google::protobuf::RepeatedPtrField;
    ui32 result = 0;
    TString pageInfo = "";
    if (page >= 0)
        pageInfo = "&p=" + ToString(page);

    message->AdditionalReserve(Config.PageScanSize);

    const TString& searcherHost = Config.ServerOptions.Host.empty() ? Config.LocalSearcherHost : Config.ServerOptions.Host;

    DEBUG_LOG << "Start processing request : " << query << pageInfo << " port: " << Config.ServerOptions.Port << " host: " << searcherHost << Endl;
    TSearchEnginesManager::TGuardedSearchPtr guard = SearchEngineManager.GetMetaSearch();
    const TCommonSearch* search = guard->GetIsMetaSearch() ? guard->GetMetaSearch() : nullptr;
    if (!search) {
        return result;
    }

    TCommonSearchRequest request(search, query + pageInfo, searcherHost, Config.ServerOptions.Port);
    const NMetaProtocol::TReport& report = request.GetReport();
    for (RepeatedPtrField<NMetaProtocol::TGrouping>::const_iterator iterGroupings = report.GetGrouping().begin(),
        endGroupings = report.GetGrouping().end();
        iterGroupings != endGroupings; ++ iterGroupings) {
            for (RepeatedPtrField<NMetaProtocol::TGroup>::const_iterator iterGroups = iterGroupings->GetGroup().begin(),
                endGroups = iterGroupings->GetGroup().end(); iterGroups != endGroups;
                ++iterGroups) {
                    for (RepeatedPtrField<NMetaProtocol::TDocument>::const_iterator iterDocuments = iterGroups->GetDocument().begin(),
                        endDocuments = iterGroups->GetDocument().end();
                        iterDocuments != endDocuments; ++iterDocuments) {
                            TString fullDocId = iterDocuments->GetDocId();
                            try {
                                // Assuming that DocID is <same num as in "mss">-<num doc in index>
                                const TDocHandle docHandle(fullDocId);
                                Y_ASSERT(docHandle.DocRoute);
                                size_t indexId = docHandle.ClientNum();
                                ui32 docId = SearchEngineManager.DecodeDocId(docHandle.ClientDocId(), indexId);
                                const TString& indexName = SearchEngineManager.GetIndexDirName(indexId);
                                message->AddResult(TDocSearchInfo(TString(), docId, 0, indexName));
                                result++;
                            } catch (yexception e) {
                                ERROR_LOG << "Attemption to delete document \"" << query << "\" failed. Id = \"" << fullDocId << "\". Exception: " << e.what() << Endl;
                            }
                    }
            }
    }
    return result;
}

void TMainSearchServer::ProcessSearchDocIds(TMessageSearchDocIds* message) {
    TString queryResult = message->GetQuery() + "&ms=proto&nocache=da&t=0&haha=da&relev=attr_limit%3D10000000";

    TCgiParameters cgiParams;
    cgiParams.Scan(queryResult);
    if (!cgiParams.Get("how"))
        queryResult += "&how=docid";
    if (!!cgiParams.Get("numdoc") || !!cgiParams.Get("p")) {
        ui32 numDocReserve = Config.PageScanSize;
        ui32 numPage = 0;
        try {
            numDocReserve = FromString(cgiParams.Get("numdoc"));
        } catch(...) {

        }
        try {
            numPage = FromString(cgiParams.Get("p"));
        } catch(...) {

        }
        message->AdditionalReserve(numDocReserve);
        if (ProcessPageQuery(queryResult, -1, message))
            message->FinishPage(numPage);
    } else {
        ui32 page = 0;
        ui32 docsCount = 0;
        while(docsCount = ProcessPageQuery(queryResult + "&numdoc=" + ToString(Config.PageScanSize), page, message)) {
            page += 1 + message->FinishPage(page);
            if (docsCount < Config.PageScanSize)
                break;
        }
    }
}

bool TMainSearchServer::Process(IMessage* message) {
    TMessageCollectServerInfo* messCollect = dynamic_cast<TMessageCollectServerInfo*>(message);
    if (messCollect) {
        HttpSearchRateMeter.AddRate(*messCollect->GetHttpSearchRps());
        return true;
    }
    TMessageSearchDocIds* messSearch = dynamic_cast<TMessageSearchDocIds*>(message);
    if (messSearch) {
        ProcessSearchDocIds(messSearch);
        return true;
    }

    return false;
}

TClientRequest* TMainSearchServer::CreateClient() {
    HttpSearchRateMeter.Hit();
    return new TMainSearchRequest(this, SearchEngineManager.GetMainSearchServerMetrics(), Config.Owner);
}
