#pragma once

#include "extendedcontext.h"

#include <saas/searchproxy/common/abstract.h>
#include <saas/searchproxy/common/replier.h>
#include <saas/searchproxy/configs/serviceconfig.h>

#include <search/meta/metasearch.h>

#include <library/cpp/eventlog/eventlog.h>
#include <library/cpp/mediator/messenger.h>

#include <util/generic/ptr.h>

#include <atomic>

class IPageCallback;
class IRequestWizard;
class ITemplateCorrector;
using ITemplateCorrectorPtr = TIntrusivePtr<ITemplateCorrector>;
class TServiceConfig;
struct IHttpStatusManager;
struct IMisspellCorrector;
using IMisspellCorrectorPtr = TIntrusivePtr<IMisspellCorrector>;

class TServiceMetric;
typedef TAtomicSharedPtr<TServiceMetric> TServiceMetricPtr;

namespace NSearchReport {
    class IFactory;
}

namespace NMetaSearch {
    class TReplier
        : public TCommonSearchReplier
        , public NSearchProxy::TCommonReplierFeatures
    {
    protected:
        virtual TDuration GetDefaultTimeout() const override;
        virtual void OnRequestExpired(const int httpCode) override {
            NSearchProxy::TCommonReplierFeatures::OnRequestExpired(httpCode, NSearchProxy::PROXY_TYPE_META, Context);
        }
        virtual void OnQueueFailure() override {
            NSearchProxy::TCommonReplierFeatures::OnQueueFailure(Context, HttpStatusConfig.UnavailableStatus);
        }
        virtual void OnAccessDenied(const NSaas::TNotAuthorizedInfo& auth) override {
            NSearchProxy::TCommonReplierFeatures::OnAccessDenied(auth, NSearchProxy::PROXY_TYPE_META, Context);
        }

    public:
        TReplier(const TString& service, IReplyContext::TPtr context, const TCommonSearch* commonSearcher, const THttpStatusManagerConfig* httpStatusConfig);
    };

    const TString UpperSearchName = "uppersearch";
};

class TRaiiSearchParams: public TMetaSearch::TParams {
public:
    virtual THolder<IMetaRearrange> ConstructRearrange(const TConstructRearrangeEnv& env) override;
};

class TRaiiSearch: public TMetaSearch {
public:
    using TPtr = TAtomicSharedPtr<TRaiiSearch>;

    class TReportFactorySetter {
        const THolder<NSearchReport::IFactory> ReportFactory;

        static IPageCallback* CreateSearchProxyPageCallback(
            const TSearchContextFactory* ys, IPageCallback* backupCallback)
        {
            return dynamic_cast<const TRaiiSearch&>(*ys).
                CreatePageCallback(backupCallback);
        }

    public:
        TReportFactorySetter();
    };

    typedef void (*TInitFunc)(const TServiceConfig&, TRaiiSearch&);
public:
    TRaiiSearch(const TServiceConfig& serviceConfig, const TSourcesConfig& searchConfig,
        TRaiiSearchParams* params, TEventLogPtr eventlog);
    virtual ~TRaiiSearch();

    static TString GetClassName() {
        return "meta";
    }

    void RaiiSearchOpen();
    void RaiiSearchClose();

    void ReopenLog() const;
    virtual IPageCallback* CreatePageCallback(IPageCallback* backupCallback) const;
    virtual IThreadPool* CreateThreadPool(size_t treadCount, size_t queueSize, const TString& threadName = {});

    virtual TEventLogPtr GetEventLog() const {
        return EventLogEnabled ? EventLog : NullEventLog;
    }
    bool IsEventLogEnabled() const {
        return EventLogEnabled;
    }

    const THttpStatusManagerConfig* GetHttpStatusManagerConfig() const {
        return &ServiceConfig.GetHttpStatusManagerConfig();
    }

    void EnableEventLog();
    void DisableEventLog();

public:
    static TRequestHash GetTextKpsHash(const TCgiParameters& cgi);

protected:
    static TReportFactorySetter ReportFactorySetter;

    const TServiceConfig ServiceConfig;

    const THolder<TSearchConfig> SearchConfig;
    const THolder<IHttpStatusManager> HttpStatusManager;
    const IMisspellCorrectorPtr MisspellCorrector;
    const ITemplateCorrectorPtr TemplateCorrector;
    const TEventLogPtr EventLog;
    const TEventLogPtr NullEventLog;

    ICgiCorrectorPtr CgiCorrector;
    TServiceMetricPtr Metric;

    std::atomic<bool> EventLogEnabled;
};

namespace NMetaSearch {

    class TProxy
        : public IProxy
        , public IMessageProcessor
    {
    private:
        TMap<TString, TRaiiSearch::TPtr> Searchers;
        TMap<TString, TString> SearchersNotStarted;

        static TFactory::TRegistrator<TProxy> Registrator;

    public:
        TProxy() {
            RegisterGlobalMessageProcessor(this);
        }
        ~TProxy() {
            UnregisterGlobalMessageProcessor(this);
        }

        virtual TString Name() const override;
        virtual bool Process(IMessage* message) override;

        ISearchReplier::TPtr BuildReplier(const TString& service, IReplyContext::TPtr context) override;

        virtual void Start() override {
            for (auto&& i : Searchers) {
                try {
                    i.second->RaiiSearchOpen();
                } catch (const yexception& e) {
                    SearchersNotStarted[i.first] = e.what();
                    ERROR_LOG << "Searcher failed for " << i.first << ": " << e.what() << Endl;
                }
            }
        }
        virtual void Stop() override {
            for (auto&& i : Searchers) {
                i.second->RaiiSearchClose();
            }
        }

        virtual void ReopenLogs() override {
            for (auto&& i : Searchers) {
                i.second->ReopenLog();
            }
        }

        virtual TMap<TString, TString>& GetFailedSearchers() override {
            return SearchersNotStarted;
        }

        virtual bool IsCommonSearch() const override {
            return true;
        }

        virtual TCommonSearch* GetCommonSearch(const TString& service) override {
            auto it = Searchers.find(service);
            if (it == Searchers.end())
                return nullptr;
            return it->second.Get();
        }

        virtual void RegisterService(const NSearchMapParser::TSearchMapService& serviceInfo, const TServiceConfig& serviceConfig, const TSourcesConfig& sourcesConfig, ICgiCorrectorPtr cc, TServiceMetricPtr metric, TEventLogPtr eventlog) override;
        virtual void RegisterMetaService(const NSearchMapParser::TMetaService& serviceInfo, const TServiceConfig& serviceConfig, const TSourcesConfig& sourcesConfig, ICgiCorrectorPtr cc, TServiceMetricPtr metric, TEventLogPtr eventlog) override;
        virtual void RegisterUpperService(const TServiceConfig& serviceConfig, const TSourcesConfig& sourcesConfig, ICgiCorrectorPtr cc, TServiceMetricPtr metric, TEventLogPtr eventlog) override;
    };
}
