#pragma once

#include <kernel/search_daemon_iface/pagecb.h>
#include <kernel/search_daemon_iface/reqtypes.h>
#include <kernel/reqerror/reqerror.h>

#include <library/cpp/deprecated/atomic/atomic.h>
#include <library/cpp/http/misc/httpreqdata.h>
#include <library/cpp/http/misc/httpcodes.h>
#include <library/cpp/logger/global/global.h>

#include <saas/library/report_builder/abstract.h>
#include <saas/library/searchserver/auth.h>
#include <search/request/data/reqdata.h>
#include <search/output_context/interface.h>
#include <search/unistat_logger/unistat_logger.h>

#include <util/stream/output.h>
#include <util/thread/pool.h>
#include "http_status_config.h"

class TCommonSearch;
class TSearchHandlers;
class TSelfFlushLogFrame;
class TSearchRequestData;

using TUnistatFramePtr = TIntrusivePtr<TUnistatFrame>;
using TSelfFlushLogFramePtr = TIntrusivePtr<TSelfFlushLogFrame>;

enum class EDeadlineCorrectionResult {
    dcrOK,
    dcrNoDeadline,
    dcrRequestExpired,
    dcrIncorrectDeadline,
};

class IReplyContext: public IReportBuilderContext {
private:
    static TAtomic GlobalRequestsCounter;
    static TAtomic GlobalActiveObjects;
    TAtomic RequestId;
    TInstant RequestDeadline = TInstant::Max();
    TInstant CommonQueueTime;
    ui64 ActiveObjects = 0;
public:
    using TPtr = TAtomicSharedPtr<IReplyContext>;

public:
    IReplyContext() {
        RequestId = AtomicIncrement(GlobalRequestsCounter);
        ActiveObjects = AtomicIncrement(GlobalActiveObjects);
    }

    virtual ~IReplyContext() {
        AtomicDecrement(GlobalActiveObjects);
    }

    virtual void MakeAuth() {
    }

    static ui64 GetGlobalActiveObjects() {
        return AtomicGet(IReplyContext::GlobalActiveObjects);
    }

    virtual const TCgiParameters& GetCgiParameters() const override {
        return GetRequestData().CgiParam;
    }

    const TInstant& GetRequestDeadline() const {
        CHECK_WITH_LOG(RequestDeadline != TInstant::Max());
        return RequestDeadline;
    }

    virtual bool IsHttp() const = 0;
    virtual NSearch::IOutputContext& Output() = 0;

    virtual ui64 GetRequestId() const final {
        return RequestId;
    }

    virtual TBlob& GetBuf() = 0;
    virtual TSearchRequestData& MutableRequestData() = 0;

    EDeadlineCorrectionResult DeadlineCorrection(const TDuration& scatterTimeout);

    virtual const TRequestParams& GetRP() const override {
        return *GetRequestData().RP;
    }

    virtual long GetRequestedPage() const override {
        return 0;
    }

    virtual void Print(const TStringBuf& data) override {
        Output().Write(data);
    }

    ui64 GetActiveObjects() const {
        return ActiveObjects;
    }
    void SetCommonQueueTime(TInstant time) {
        CommonQueueTime = time;
    }
    TDuration GetCommonQueueDuration() const {
        return CommonQueueTime - TInstant::MicroSeconds(GetRequestData().RequestBeginTime());
    }
};

class ISearchReplier: public IObjectInQueue {
private:
    static TAtomic GlobalActiveObjects;
protected:
    THttpStatusManagerConfig HttpStatusConfig;
protected:
    virtual TDuration GetDefaultTimeout() const = 0;
    virtual void OnRequestExpired(const int httpCode) = 0;
public:
    ISearchReplier(IReplyContext::TPtr client, const THttpStatusManagerConfig* config);
    virtual ~ISearchReplier();

    static ui64 GetGlobalActiveObjects() {
        return AtomicGet(ISearchReplier::GlobalActiveObjects);
    }

    void Reply();
    // IObjectInQueue
    virtual void Process(void* /*ThreadSpecificResource*/) override;

    using TPtr = TAutoPtr<ISearchReplier>;
protected:
    virtual void OnQueueFailure();
    virtual void OnAccessDenied(const NSaas::TNotAuthorizedInfo&) {
    }

    virtual void SearchAndReply() final;
    virtual void DoSearchAndReply() = 0;
    virtual IThreadPool* DoSelectHandler() = 0;
    virtual const TSearchHandlers* GetSearchHandlers() const {
        return nullptr;
    }

protected:
    IThreadPool* GetHandler(ERequestType reqType);

    IReplyContext::TPtr Context;
};

class TCommonSearchReplier: public ISearchReplier, public IReportCallback {
protected:
    struct TStatistics {
        ui64 TotalDocsCount = 0;
        ui64 ReportByteSize = 0;
        ui64 ReportDocsCount = 0;
        ui32 UnanswerCount = 0;
        float CacheHit = 0;
        ui32 FastCacheHit = 0;

        enum EAnswerStatus {
            AS_UNKNOWN = -1,
            AS_INCOMPLETE = 0,
            AS_COMPLETE = 1
        } AnswerIsComplete = AS_UNKNOWN;

        TString SearchErrors;
    };

    class TUnistatFrameWithStat : public TUnistatFrame {
        TStatistics& Stat;
    public:
        TUnistatFrameWithStat(TStatistics& stat) : Stat(stat) {
        }

        virtual void OnFastCacheHit(ui64) override {
            Stat.FastCacheHit = 1;
        }
    };


protected:
    mutable TStatistics Stat;
protected:
    int BeforeReport(ISearchContext* ctx) const override;
    void AfterReport(ISearchContext* ctx) const override;
    void AfterReport(IPassageContext* ctx) const override;
public:
    TCommonSearchReplier(IReplyContext::TPtr context, const TCommonSearch* commonSearcher, const THttpStatusManagerConfig* httpStatusConfig);
protected:
    virtual void DoSearchAndReply() override;
    virtual IThreadPool* DoSelectHandler() override;
private:
    void CreateLogFrame();
    virtual const TSearchHandlers* GetSearchHandlers() const override;
protected:
    virtual bool ProcessInfo();
    virtual bool ProcessFetch();
    virtual bool ProcessRequest();

    TMakePageContext CreateMakePageContext();

protected:
    const TCommonSearch* CommonSearcher;

    TSelfFlushLogFramePtr LogFrame;
    TUnistatFramePtr StatFrame;
    ERequestType RequestType;
};
