#pragma once

#include <saas/library/searchserver/replier.h>
#include <saas/library/metasearch/simple/config.h>
#include <saas/library/sharding/sharding.h>
#include <saas/util/queue.h>
#include <saas/searchproxy/configs/serviceconfig.h>
#include <saas/searchproxy/search_meta/cgi_corrector.h>

#include <search/session/comsearch.h>

#include <library/cpp/eventlog/eventlog_int.h>
#include <library/cpp/eventlog/threaded_eventlog.h>

#include <util/thread/pool.h>
#include <util/generic/ptr.h>
#include <util/system/rwlock.h>

namespace NSimpleMeta {
    class TBaseProxySearch {
    private:
        THolder<IThreadPool> RequestQueue;
        mutable TRWMutex Mutex;
        bool IsActive = false;

    protected:
        const TServiceConfig ServiceConfig;
        const NSimpleMeta::TConfig ProxyConfig;
        const THttpStatusManagerConfig HttpStatusManagerConfig;
        const NSaas::TShardsDispatcher::TPtr ShardsDispatcher;
        const ICgiCorrectorPtr CgiCorrector;
        mutable TEventLogPtr EventLog;
        TAtomic EventLogEnabled = 0;

    protected:
        THolder<IThreadPool> CreateRequestQueue(const TString& threadName) const {
            ui32 threadsCount = ProxyConfig.GetThreadsSenders();
            if (ServiceConfig.UseSmartQueue()) {
                return CreateRTYQueue(threadsCount, ProxyConfig.GetMaxQueueSize(), TRTYMtpQueue::TOptions().SetThreadName(threadName));
            } else {
                return THolder(TSearchHandlers::CreateHandler(threadsCount, ProxyConfig.GetMaxQueueSize(), ServiceConfig.UseElasticQueues(), threadName));
            }
        }
        virtual bool DoSearchContext(IReplyContext::TPtr context) const = 0;

    public:
        TBaseProxySearch(
            const TServiceConfig& serviceConfig,
            const NProxyMeta::TConfig& proxyConfig,
            const NSaas::TShardsDispatcher::TPtr sharding,
            ICgiCorrectorPtr cgiCorrector,
            TEventLogPtr eventlog
        )
            : ServiceConfig(serviceConfig)
            , ProxyConfig(proxyConfig)
            , HttpStatusManagerConfig(serviceConfig.GetHttpStatusManagerConfig())
            , ShardsDispatcher(sharding)
            , CgiCorrector(cgiCorrector)
            , EventLog(eventlog)
            , EventLogEnabled(proxyConfig.GetEventLogEnabled() ? 1 : 0)
        {
            if (!!ProxyConfig.GetEventLogPath() && (ProxyConfig.GetEventLogPath() != ServiceConfig.GetEventLogName())) {
                TMaybe<TEventLogFormat> eventLogFormat = ParseEventLogFormat(ProxyConfig.GetEventLogCompressionFormat());
                TEventLogPtr log = new TEventLog(ProxyConfig.GetEventLogPath(), NEvClass::Factory()->CurrentFormat(), {}, eventLogFormat);
                EventLog = new TThreadedEventLog(log);
            }
        }

        virtual ~TBaseProxySearch() {}

        const TServiceConfig& GetServiceConfig() const {
            return ServiceConfig;
        }

        const THttpStatusManagerConfig& GetHttpStatusManagerConfig() const {
            return HttpStatusManagerConfig;
        }

        bool Active() const {
            return IsActive;
        }

        virtual bool SearchContext(IReplyContext::TPtr context) const final {
            TReadGuard rg(Mutex);
            if (!IsActive)
                return false;
            CHECK_WITH_LOG(!!context);
            return DoSearchContext(context);
        }

        virtual void Stop() {
            TWriteGuard rg(Mutex);
            IsActive = false;
            RequestQueue.Destroy();
        }

        virtual void Start() {
            TWriteGuard wg(Mutex);
            IsActive = true;
            RequestQueue = CreateRequestQueue("BaseProxySearch");
        }

        bool LoggingEnabled() const {
            return ProxyConfig.GetLoggingEnabled();
        }

        inline bool IsEventLogEnabled() const noexcept {
            return AtomicGet(EventLogEnabled);
        }

        inline void EnableEventLog() {
            AtomicSet(EventLogEnabled, 1);
        }

        inline void DisableEventLog() {
            AtomicSet(EventLogEnabled, 0);
        }

        IThreadPool* GetSendHandler() const {
            Y_ASSERT(Active());
            Y_ASSERT(RequestQueue);
            return RequestQueue.Get();
        }
    };
}
