#include "manager.h"

#include <saas/library/cgi/cgi.h>
#include <saas/library/proto_helper/proto_helper.h>
#include <saas/rtyserver/components/fullarchive/ddk_manager.h>
#include <saas/rtyserver/components/special_keys/component.h>
#include <saas/rtyserver/components/special_keys/manager.h>
#include <saas/rtyserver/components/special_keys/const.h>

#include <library/cpp/protobuf/json/string_transform.h>

IFAManager::IFAManager(const TRTYServerConfig& config)
        : TBaseGeneratorManager(config, FULL_ARCHIVE_COMPONENT_NAME)
        , DDKManager(new TDDKManager(*this))
        , SupportsSearch(TKeysComponent::IsUsedStatic(config))
{}

void IFAManager::CopyDocumentToReport(ui32 docId, const NRTYServer::TMessage::TDocument* docStored, const TCgiParameters& cgi, NMetaProtocol::TDocument& doc, const TFAExtraSearchParams* extraParams) const {
    const bool hrOut = (IsTrue(cgi.Get("laas_json")) || IsTrue(cgi.Get("unpack_json")) || IsTrue(cgi.Get("hr")));
    doc.SetDocId(ToString(docId));
    doc.SetUrl(docStored->GetUrl());
    doc.MutableArchiveInfo()->SetTitle(docStored->GetUrl());

    TSearchProtoHelper helper(doc);

    const TGtaFilter gta(cgi);
    const bool urlOnly = gta.Has("_UrlOnly");
    if (!urlOnly) {
        if (IsTrue(cgi.Get("normal_kv_report"))) {
            for (const auto& prop : docStored->GetDocumentProperties()) {
                const TString& attrName = prop.GetName();
                const TString& attrValue = prop.GetValue();

                if (gta.NeedProperty(attrName)) {
                    helper.AddProperty(attrName, attrValue);
                }
                UpdateCategAttr(extraParams, attrName, attrValue);
            }

            if (gta.NeedTimestamp()) {
                helper.AddProperty("_Timestamp", docStored->GetModificationTimestamp());
            }

            if (gta.NeedVersion()) {
                helper.AddProperty("_Version", docStored->GetVersion());
            }

            if (gta.NeedBody() && !docStored->GetBody().empty()) {
                helper.AddProperty("_Body", docStored->GetBody());
            }

            if (gta.NeedDeadline()) {
                helper.AddProperty("_Deadline", docStored->GetDeadlineMinutesUTC());
            }

            if (gta.NeedKeyPrefix()) {
                helper.AddProperty("_KeyPrefix", docStored->GetKeyPrefix());
            }

        } else {
            auto info = doc.MutableArchiveInfo()->AddGtaRelatedAttribute();
            info->SetKey("data");
            if (hrOut) {
                TStringStream data;
                NProtobufJson::Proto2Json(*docStored, data);
                info->SetValue(data.Str());
            } else {
                const TString& raw = docStored->SerializeAsString();
                info->SetValue(Base64Encode(raw));
            }
        }
    } else if (NeedGroupingProcessing(extraParams)) {
        for (ui32 i = 0; i < docStored->DocumentPropertiesSize(); ++i) {
            const auto& documentProperties = docStored->GetDocumentProperties(i);
            UpdateCategAttr(extraParams, documentProperties.GetName(), documentProperties.GetValue());
        }
    }

    if (cgi.Has("key_name") && !urlOnly) {
        for (ui32 i = 0; i < docStored->AdditionalKeysSize(); ++i) {
            helper.AddProperty("_SK_" + docStored->GetAdditionalKeys(i).GetName(),
                               docStored->GetAdditionalKeys(i).GetValue());
        }
    }
}

void IFAManager::SearchDocuments(const TVector<TDocIdCandidate>& docIds, const TCgiParameters& cgi, TVector<NMetaProtocol::TDocument>& documents, const TString& groupingAttr, TVector<TString>* categs) const {
    documents.clear();
    documents.reserve(docIds.size());

    TFAExtraSearchParams extraParams;
    TFAExtraSearchParams* extraParamsPtr = nullptr;
    if (categs) {
        extraParams.GroupingAttr = groupingAttr;
        extraParamsPtr = &extraParams;
        categs->clear();
        categs->reserve(docIds.size());
    }
    for (ui32 i = 0; i < docIds.size(); ++i) {
        TDocIdCandidate docId = docIds[i];
        if (!docId.IsDocIdSet())
            continue;

        NMetaProtocol::TDocument doc;
        TString categName;
        if (categs) {
            extraParams.CategName = &categName;
        }
        if (SearchDocumentCandidate(docId, cgi, doc, extraParamsPtr)) {
            documents.push_back(doc);
            if (categs) {
                categs->push_back(categName);
            }
        }
    }
}


ERTYSearchResult IFAManager::DoSearch(const TRTYSearchRequestContext& context, ICustomReportBuilder& reportBuilder, const IIndexController& controller) const {
    CHECK_WITH_LOG(SupportsSearch);
    const TCgiParameters& cgi = context.CgiParams();

    if (cgi.Has("haha") || cgi.Has("DF")) {
        reportBuilder.AddErrorMessage("Two step query request is incorrect for this component");
        return SR_NOT_FOUND;
    }

    const TString timeToWait = cgi.Get("sleep");
    ui64 waitingTime = 0;
    if (timeToWait && TryFromString(timeToWait, waitingTime)) {
        DEBUG_LOG << "Sleeping " << waitingTime << " microseconds" << Endl;
        Sleep(TDuration::MicroSeconds(waitingTime));
    }

    const TKeysIndexManager* manager = dynamic_cast<const TKeysIndexManager*>(controller.GetManager(NRTYServer::KeysComponentName));
    CHECK_WITH_LOG(manager);

    TMessagesCollector errors;
    TVector<TDocIdCandidate> docIds;
    if (!manager->SearchImpl(context, controller, docIds, errors)) {
        reportBuilder.AddErrorMessage(errors.GetStringReport());
        return SR_NOT_FOUND;
    }

    if (docIds.empty()) {
        return SR_NOT_FOUND;
    }

    TVector<NMetaProtocol::TDocument> documents;
    const TString groupingAttr = GetGropingAttr(cgi);
    if (groupingAttr) {
        TVector<TString> categs;
        SearchDocuments(docIds, cgi, documents, groupingAttr, &categs);
        CHECK_WITH_LOG(categs.size() == documents.size());
        for (ui32 i = 0; i < documents.size(); ++i) {
            reportBuilder.AddDocumentToGroup(documents[i], groupingAttr, categs[i]);
        }
    } else {
        SearchDocuments(docIds, cgi, documents);
        for (auto&& doc : documents) {
            reportBuilder.AddDocument(doc);
        }
    }

    return documents.size() == 0 ? SR_NOT_FOUND : SR_OK;
}

const IDDKManager& IFAManager::GetDDKManager() const {
    return *DDKManager;
}

bool IFAManager::GetDocInfo(const TString& layer, const NRTYServer::TParsedDoc& pd, NJson::TJsonValue& result) const {
    NProtobufJson::TProto2JsonConfig cfg;
    cfg.EnumMode = NProtobufJson::TProto2JsonConfig::EnumName;
    cfg.AddMissingFields = false;
    cfg.AddStringTransform(new TStringTransform);
    NProtobufJson::Proto2Json(pd, result.InsertValue(layer, NJson::TJsonValue(NJson::JSON_MAP)), cfg);
    return true;
}

