#pragma once

#include "sender_executor.h"
#include "request_opts.h"

#include <saas/library/daemon_base/daemon/info.h>
#include <saas/library/daemon_base/daemon/base_http_client.h>
#include <saas/util/destroyer.h>
#include <saas/indexerproxy/context/document.h>

#include <util/datetime/base.h>

class TFlowMirror;

class TCommonContext: public IAdapterContext {
protected:
    TTaskExecutor& Executor;
    TRequestOptions RequestOptions;
    TInstant ReceiveTime;
    TString ServiceName;
    TString AdapterName;
    TDispatchableDocument::TPtr RequestDoc;

public:
    TCommonContext(TCommonContext& context)
        : IAdapterContext(context)
        , Executor(context.Executor)
        , RequestOptions(context.RequestOptions)
        , ReceiveTime(context.ReceiveTime)
        , ServiceName(context.ServiceName)
        , AdapterName(context.AdapterName)
        , RequestDoc(context.RequestDoc)
    {
    }

    TCommonContext(TTaskExecutor& executor)
        : Executor(executor)
        , ReceiveTime(Now())
    {
    }

    virtual TAtomicSharedPtr<TDispatchableDocument>& GetDocument() override {
        return RequestDoc;
    }

    virtual const TAtomicSharedPtr<TDispatchableDocument>& GetDocument() const override {
        return RequestDoc;
    }

    virtual const IRequestOptions& GetRequestOptions() const override {
        return RequestOptions;
    }

    virtual const TString& GetServiceName() const override {
        return ServiceName;
    }

    virtual const TString& GetAdapterName() const override {
        return AdapterName;
    }

    TInstant GetReceiveTime() const override {
        return ReceiveTime;
    }

    inline ui64 Duration() const {
        return (TInstant::Now() - ReceiveTime).MilliSeconds();
    }
};

class THttpContext: public TCommonContext {
public:

    THttpContext(TTaskExecutor& executor)
        : TCommonContext(executor)
    {

    }

    THttpContext(THttpContext& context)
        : TCommonContext(context)
    {

    }

    virtual TString GetOrigin() const {
        return "http_request";
    }

};

class THttpProxyIndexerClient: public TBaseHttpClient, public THttpContext {
private:
    TString OriginHeader;
public:
    THttpProxyIndexerClient(TTaskExecutor& executor, TFlowMirror& /*flowMirror*/);

    bool ProcessRequest();

    void DoReply(ui32 codeReply, const TString& message, const TString& statusMessage) override {
        if (!!message) {
            Output() << "HTTP/1.1 " << codeReply << " " << statusMessage << "\r\n"
                << "Content-Type: text/json\r\nContent-Length: " << ToString<size_t>(message.size()) << "\r\n" << OriginHeader << "\r\n" << message;
        } else {
            Output() << "HTTP/1.1 " << codeReply << " " << statusMessage << "\r\n"
                << "Content-Type: text/html\r\nContent-Length: " << ToString<size_t>(message.size()) << "\r\n" << OriginHeader << "\r\n" << message;
        }
        Output().Flush();
    }

    using IAdapterContext::Reply;

    bool Reply(void* /*ThreadSpecificResource*/) override;
    virtual bool ProcessSpecialRequest() override;

    void GetMetrics(IOutputStream& out) const override;

    virtual const TBlob& GetPost() const override {
        return Buf;
    }

    virtual const TServerRequestData& GetRD() const override {
        return RD;
    }

    TStringBuf GetRemoteAddr() const override {
        return GetRD().RemoteAddr();
    }

    TStringBuf GetQueryString() const override {
        return GetRD().Query();
    }

    TStringBuf GetScriptName() const override {
        return GetRD().ScriptName();
    }

    virtual TServerInfo GetServerInfo(bool isHumanReadable) const override;

};

class TNoReplyHttpContext: public THttpContext {
private:
    TBlob Buf;
    TString RemoteAddr;
    TString QueryString;
    TString ScriptName;
    TString PostBoundary;
    TServerRequestData FakeRD;
public:
    TNoReplyHttpContext(THttpProxyIndexerClient& context)
        : THttpContext(context)
    {
        Buf = context.GetPost();
        RemoteAddr = context.GetRD().RemoteAddr();
        QueryString = context.GetRD().Query();
        ScriptName = context.GetRD().ScriptName();

        for (const auto& header: context.ParsedHeaders) {
            FakeRD.AddHeader(header.first, header.second);
        }
    }

    TStringBuf GetRemoteAddr() const override {
        return RemoteAddr;
    }

    TStringBuf GetQueryString() const override {
        return QueryString;
    }

    TStringBuf GetScriptName() const override {
        return ScriptName;
    }

    virtual const TBlob& GetPost() const override {
        return Buf;
    }

    virtual const TServerRequestData& GetRD() const override {
        return FakeRD;
    }

    void DoReply(ui32 /*codeReply*/, const TString& /*message*/, const TString& /*statusMessage*/) override {
    }
};
