#pragma once

#include <saas/library/daemon_base/daemon/daemon.h>
#include <saas/library/daemon_base/daemon/base_http_client.h>
#include <saas/library/daemon_base/module/module.h>
#include <saas/util/queue.h>

#include <library/cpp/neh/rpc.h>

#include <atomic>

struct TEmulatorServerConfig {
    void Init(TYandexConfig::Section& section);
    THttpServerOptions Server;
    TDuration ReplyTimeoutMin;
    TDuration ReplyTimeoutMax;
    ui32 ReplyThreads;

    TString EmulatorProcessor;
    bool AsyncMode = true;
};

class TEmulatorServer {
public:
    class TRequest : public TAtomicRefCount<TRequest> {
    public:
        typedef TIntrusivePtr<TRequest> TPtr;

        TRequest(TEmulatorServer& owner);
        virtual ~TRequest() {}
        virtual void DoReply() = 0;
        TInstant GetDeadline() const {
            return Deadline;
        }

        TDuration GetReplyTime() const {
            return ReplyTime;
        }

    protected:
        TInstant Deadline;
        TDuration ReplyTime;
    };

public:
    TEmulatorServer(const TEmulatorServerConfig& config);
    virtual ~TEmulatorServer() {}
    virtual bool Start();
    virtual void Stop();
    void Add(TRequest::TPtr request);

protected:
    const TEmulatorServerConfig& Config;

private:
    typedef TDeque<TRequest::TPtr> TQueueType;

    struct TReplyer : public IObjectInQueue {
        TReplyer(TEmulatorServer& owner);
        virtual void Process(void*) override;

    private:
        TEmulatorServer& Owner;
    };

private:
    TRequest::TPtr Get();
    TQueueType Queue;
    TMutex Mutex;
    TCondVar ElementAdded;
    std::atomic<bool> Stopped;
    TRTYMtpQueue ReplyQueue;
};


class TNehEmulatorRequest : public TEmulatorServer::TRequest {
public:
    TNehEmulatorRequest(TEmulatorServer& owner, NNeh::IRequestRef req)
        : TRequest(owner)
        , Req(req)
    {}

    virtual void DoReply() override {
        if (!Req->Canceled()) {
            Req->SendReply(DataSaver);
        }
    }

protected:
    NNeh::IRequestRef Req;
    NNeh::TDataSaver DataSaver;
};

