#pragma once

#include "indexer_proxy_specific.h"
#include "request_opts.h"

#include <saas/indexerproxy/adapters/interface_adapter.h>
#include <saas/indexerproxy/configs/config.h>
#include <saas/indexerproxy/context/action_reply.h>
#include <saas/indexerproxy/context/abstract_context.h>
#include <saas/indexerproxy/context/document.h>
#include <saas/indexerproxy/logging/rty_ipr.h>

#include <saas/library/daemon_base/metrics/servicemetrics.h>


class TTaskExecutor;
class TUkropModule;
class TAdaptersManager;

class TCommonSenderTask: public ISenderTask, public IObjectInQueue {
private:
    TTaskExecutor& Executor;
    TFinishExecutor& FinishExecutor;
    ISenderTask::TDestroyer& Destroyer;
    TAtomic RepliesReady;
    TMutex Mutex;
    TActionReply ActionReply;
    TStaticServiceMetrics& Metrics;

    bool Authorized;
    bool Initialized;
    bool DumpOriginalMessage;
    bool WasReplied;

private:
    bool PrepareDataFromContext(TAdaptersManager& processor);
    void BuildContexts(NRTYServer::TMessage& message);
    void DoLogAndMetric(const TString& serviceName, int replyCode, bool fromMeta, const TString& message, bool logging) const;

protected:
    TAtomic ClientId;
    THolder<IAdapterContext> Context;
    TVector<ISrvDispContext::TPtr> ServiceContexts;
    TMap<TString, int> DistributorStats;

public:
    TCommonSenderTask(TTaskExecutor& executor, TFinishExecutor& finishExecutor, IAdapterContext* context, TStaticServiceMetrics& metrics, ISenderTask::TDestroyer& destroyer);

    virtual ~TCommonSenderTask() {
        CHECK_WITH_LOG(!Initialized || WasReplied);
    }

    virtual TFinishExecutor& GetFinishExecutor() override {
        return FinishExecutor;
    }

    virtual ui64 GetRequestId() const override {
        return ClientId;
    }

    virtual ui64 GetMessageId() const override;

    virtual IAdapterContext& GetContext() override {
        CHECK_WITH_LOG(!!Context);
        return *Context;
    }

    virtual const IAdapterContext& GetContext() const override {
        CHECK_WITH_LOG(!!Context);
        return *Context;
    }

    ui64 GetDocKeyPrefix() const;
    ui64 GetDocTimestamp() const;
    TString GetDocUrl() const;

    bool IsExpired() const {
        if (Context->GetRequestOptions().HasTimeout()) {
            TDuration elapsed = Now() - Context->GetReceiveTime();
            return elapsed > Context->GetRequestOptions().GetTimeout();
        } else {
            return false;
        }
    }

    virtual TActionReply& GetReply() override {
        return ActionReply;
    }

    TString GetStatusString(ui16 httpCode) const {
        switch (httpCode) {
        case 200:
            return "success";
        case 202:
            return "wait";
        default:
            return "fail";
        }
    }

    virtual void SendReply() override;

    virtual bool BackendAllowed(const TString& backend) const override {
        return Context->GetRequestOptions().BackendAllowed(backend);
    }

    TString GetReplyMessage(ui32 httpStatus) const;

    void OnDocFinished() override {
        if (AtomicIncrement(RepliesReady) == ServiceContexts.ysize()) {
            Destroyer.Register(this);
        }
    }

    virtual const NDispatchableDocument::TTrace* GetTrace() const {
        return nullptr;
    }

    void Process(void* ThreadSpecificResource) override;

    virtual int DoFillDocuments(TAdaptersManager& processor) = 0;

};
