#pragma once

#include <google/protobuf/message.h>
#include <util/memory/blob.h>
#include <util/network/socket.h>
#include <library/cpp/deprecated/atomic/atomic.h>
#include <library/cpp/http/misc/httpreqdata.h>
#include <library/cpp/logger/global/global.h>
#include <saas/util/destroyer.h>

class TActionReply;
class TDispatchableDocument;
class TFinishExecutor;
class TServerRequestData;

class IRequestOptions {
public:
    using TPtr = TAtomicSharedPtr<IRequestOptions>;

public:
    virtual ~IRequestOptions() {}
    virtual bool IsTrace() const = 0;
    virtual bool IsDeferredMessage() const = 0;
    virtual bool GetVerboseLogging() const = 0;
    virtual bool GetWaitReply() const = 0;
    virtual bool IsRealtime() const = 0;
    virtual bool HasTimeout() const = 0;
    virtual ui64 GetSizeLimit() const = 0;
    virtual const TString& GetAuthCookie() const = 0;
    virtual const TString& GetSource() const = 0;
    virtual TDuration GetTimeout() const = 0;
    virtual bool GetBypassDistributor() const = 0;
    virtual bool QueueDisabled() const = 0;
    virtual bool BackendAllowed(const TString& backend) const = 0;
    virtual bool GetHasRealtimeFlag() const = 0;
    virtual TDuration GetIndexSleep() const = 0;

    virtual bool IsInstantReply() const {
        return false;
    }

};

class IAdapterContext {
private:
    TAtomic RepliesCounterChecker;
protected:
    virtual void DoReply(ui32 codeReply, const TString& message, const TString& statusMessage) = 0;
public:

    IAdapterContext(const IAdapterContext& context) {
        RepliesCounterChecker = context.RepliesCounterChecker;
    }

    IAdapterContext() {
        RepliesCounterChecker = 0;
    }

    virtual ~IAdapterContext() {
        CHECK_WITH_LOG(AtomicGet(RepliesCounterChecker) == 1 || UncaughtException());
    }

    virtual const TString& GetServiceName() const = 0;
    virtual const TString& GetAdapterName() const = 0;
    virtual const TServerRequestData& GetRD() const = 0;
    virtual const IRequestOptions& GetRequestOptions() const = 0;
    virtual const TBlob& GetPost() const = 0;
    virtual TAtomicSharedPtr<TDispatchableDocument>& GetDocument() = 0;
    virtual const TAtomicSharedPtr<TDispatchableDocument>& GetDocument() const = 0;
    virtual TInstant GetReceiveTime() const = 0;
    virtual TString GetOrigin() const = 0;

    virtual TStringBuf GetRemoteAddr() const = 0;
    virtual TStringBuf GetQueryString() const = 0;
    virtual TStringBuf GetScriptName() const = 0;

    void NoNeedInReply() {
        CHECK_WITH_LOG(AtomicIncrement(RepliesCounterChecker) == 1);
    }

    IAdapterContext& Reply(ui32 codeReply, const TString& message, const TString& statusMessage) {
        CHECK_WITH_LOG(AtomicIncrement(RepliesCounterChecker) == 1);
        try {
            DoReply(codeReply, message, statusMessage);
        } catch (...) {
            ERROR_LOG << "Can't made reply: " << codeReply << "/" << message << "/" << statusMessage << Endl;
            AtomicDecrement(RepliesCounterChecker);
            throw;
        }
        return *this;
    }
};

class ISenderTask {
public:
    virtual ~ISenderTask() {}

    virtual TActionReply& GetReply() = 0;
    virtual IAdapterContext& GetContext() = 0;
    virtual const IAdapterContext& GetContext() const = 0;
    virtual ui64 GetRequestId() const = 0;
    virtual ui64 GetMessageId() const = 0;
    virtual void OnDocFinished() = 0;
    virtual void SendReply() = 0;
    virtual bool BackendAllowed(const TString& backend) const = 0;
    virtual TFinishExecutor& GetFinishExecutor() = 0;

    using TDestroyer = ::TDestroyer<ISenderTask, ISenderTask>;

    static void BeforeDelete(ISenderTask& client) {
        client.SendReply();
    }

};

struct TAdapterMinimalConfig {
    TString Name;
    TString SecretCode;
};

struct TAdapterInitializationList {
    const TAdapterMinimalConfig& Config;
    TString& ErrorMessage;

    TAdapterInitializationList(const TAdapterMinimalConfig& config, TString& errorMessage)
        : Config(config)
        , ErrorMessage(errorMessage)
    {

    }
};

class IAdapter {
public:
    virtual void ProcessMessage(ISenderTask& context) const = 0;
    virtual const TString& GetFormatName() const = 0;
    virtual ~IAdapter() {}

    IAdapter() {}
    IAdapter(const TAdapterInitializationList& /*arguments*/) {}
};
