#pragma once

#include <search/daemons/httpsearch/neh/request.h>
#include <saas/library/searchserver/client.h>
#include <saas/library/searchserver/neh.h>
#include <saas/searchproxy/proxy_meta/search.h>
#include <apphost/api/service/cpp/service.h>
#include <search/request/data/reqdata.h>

#include <library/cpp/tvmauth/client/facade.h>

class ISearchContext;
class TSearchLog;
struct TSearchProxyCluster;
class TSearchProxyConfig;
class TSearchProxyServer;

class TSearchProxyRequestFeatures {
protected:
    TSearchProxyServer* Server;
    const TSearchProxyConfig& Config;

    TString Service;
    TString FilteredScript;

protected:
    TSearchProxyRequestFeatures(TSearchProxyServer* server);
    virtual ~TSearchProxyRequestFeatures() {}

    TString CalculateName(const TCgiParameters& cgi);
    void ParseRequest();
    void DoHandleException(IReplyContext* context);
    ISearchReplier::TPtr DoSelectHandlerImpl(IReplyContext::TPtr context);
    void ProcessTvmAuth();
    void ProcessTvmAuth(const TString& service);

private:
    virtual TSearchRequestData& GetRD() = 0;
    virtual IOutputStream& GetOutput() = 0;
};

class TSearchProxyClient
    : public TSearchClient
    , public TSearchProxyRequestFeatures
{
private:
    virtual ISearchReplier::TPtr DoSelectHandler(IReplyContext::TPtr context) override;

    void AdjustClientRequest(const TString& service);
    void ProcessBroadcast(const TString& service = {}, const TString& request = {});
public:
    TSearchProxyClient(TSearchProxyServer* server);

    virtual bool ProcessSpecialRequest() override;
    virtual void ProcessAuth() override;
    virtual bool ProcessPreAuth() override;
    virtual void OnBeginProcess(IReplyContext::TPtr context) override;
    virtual void OnAccessDenied(const NSaas::TNotAuthorizedInfo& auth, IReplyContext::TPtr context) override;

private: // TSearchProxyRequestFeatures
    virtual TSearchRequestData& GetRD() override {
        return RD;
    }
    virtual IOutputStream& GetOutput() override {
        return Output();
    }
};

class TSearchProxyNehRequest
    : public TYsNehClientRequest
    , public TSearchProxyRequestFeatures
    , public TNehReplyContext
{
private:
    bool Preprocessed;
    TInstant RequestStartTime;
    TBlob Buf;

public:
    TSearchProxyNehRequest(YandexHttpServer* yserver, TSearchProxyServer* server, NNeh::IRequestRef req);
    void ScanQuery() override {} // move scaning query to constructor
    bool CreateSearcher(const char* name) override;
    void Process(void* ts) override;

    void MakeAuth() override {
        ProcessTvmAuth();
    }

private: // TSearchProxyRequestFeatures
    virtual NNeh::IRequest& GetRequest() override {
        return *Request_;
    }

    virtual NNeh::TRequestOut& GetReplyOutput() override {
        return Reply_;
    }

    virtual TSearchRequestData& GetRD() override {
        return RequestData();
    }
    virtual IOutputStream& GetOutput() override {
        return Reply_;
    }

    virtual NSearch::IOutputContext& Output() override {
        return ReplyContext_;
    }

    virtual const TSearchRequestData& GetRequestData() const override {
        return RD_;
    }

    virtual TSearchRequestData& MutableRequestData() override {
        return RequestData();
    }

    virtual TBlob& GetBuf() override {
        return Buf;
    }
    virtual TInstant GetRequestStartTime() const override {
        return RequestStartTime;
    }

    void OnBeforeRequestHandlerRun() override;
};

class TAppHostNehRequest : public NNeh::IRequest {
public:
    explicit TAppHostNehRequest(NAppHost::TServiceContextPtr appHostContext);
    virtual TStringBuf Scheme() const override;
    virtual TString RemoteHost() const override;
    virtual TStringBuf Service() const override;
    virtual TStringBuf Data() const override;
    virtual TStringBuf RequestId() const override;
    virtual bool Canceled() const override;
    virtual void SendReply(NNeh::TData& data) override;
    virtual void SendError(TResponseError err, const TString& details = TString()) override;
    NThreading::TFuture<void> GetFuture() const;

private:
    NThreading::TPromise<void> Promise;
    NAppHost::TServiceContextPtr AppHostContext;
    TString RequestStr;
    TString RequestIdStr;
    bool ProtobufResult = false;
};
