#include "abstract_context.h"

#include <saas/library/behaviour/behaviour.h>
#include <saas/indexerproxy/server/client.h>
#include <saas/indexerproxy/configs/dispatch_config.h>
#include <saas/indexerproxy/dispatching/dispatch_queue.h>

TAtomic ISrvDispContext::GlobalCounter = 0;

namespace {
    class TFinishActor: public IObjectInQueue {
    private:
        ISrvDispContext* DispContext;

    public:
        TFinishActor(ISrvDispContext* dispContext) {
            DispContext = dispContext;
        }

        void Process(void* /*ThreadSpecificResource*/) override {
            DispContext->Finish();
        }
    };
}

void ISrvDispContext::RegisterStatus(const TSendStatus& status) {
    DEBUG_LOG << "status=register;url=" << Message.GetDocument().GetUrl() << ";status=" << (ui32)status.GetDispStatus() << Endl;
    AtomicDecrement(GlobalCounter);
    bool toFinish = false;
    {
        TGuard<TMutex> g(MutexRegisterStatus);
        AccountStatus(status);
        CounterReplies = *CounterReplies - 1;
        if (!*CounterReplies) {
            DEBUG_LOG << "status=register_finish;url=" << Message.GetDocument().GetUrl() << Endl;
            toFinish = true;
        }
    }
    if (toFinish) {
        Task.GetFinishExecutor().Finish(new TFinishActor(this));
    }
}

bool ISrvDispContext::Verify(const TProxyConfig& config) const {
    bool result = true;
    DEBUG_LOG << "action=dispatch_message_verification;status=start;client_id=" << GetContext().GetRequestId() << Endl;
    const TRTYMessageBehaviour& bh = GetBehaviour(GetMessage().GetMessageType());
    if ((bh.IsContentMessage || !bh.IsBroadcastMessage) && !GetMessage().HasDocument()) {
        ForceMessageStatus(NRTYServer::TReply::dsUSER_ERROR, "Message does not have document");
        result = false;
    } else {
        result = DoVerify(config, bh);
    }
    if (result) {
        DEBUG_LOG << "action=dispatch_message_verification;status=ok;client_id=" << GetContext().GetRequestId() << Endl;
    } else {
        DEBUG_LOG << "action=dispatch_message_verification;status=fail;id=" << GetContext().GetRequestId() << Endl;
    }
    return result;
}

namespace {
    TMutex MutexGlobalISrvDispContextSend;
}

void ISrvDispContext::Send(const TProxyConfig& config, TDispatchServiceSelector& selector) {
    ui64 reqId = GetContext().GetRequestId();
    DEBUG_LOG << "action=dispatch_task;status=construction_start;id=" << reqId << Endl;
    CHECK_WITH_LOG(Verify(config));
    if (!DoSend(config, selector)) {
        FinishDocument();
    }

    DEBUG_LOG << "action=dispatch_task;status=finished;id=" << reqId << Endl;

}

void ISrvDispContext::WriteLog(const TString& backend, ui32 status, const TString& description) const
{
    if (Log.IsNullLog()) {
        return;
    }
    TString log = NLoggingImpl::GetLocalTimeS();
    log += '\t';
    log += NRTYServer::TMessage::TMessageType_Name(GetMessageType());

    log += '\t';
    log += GetUrl();
    log += '\t';
    log += (TString)Task.GetContext().GetRemoteAddr();
    log += '\t';
    log += backend;
    log += '\t';
    log += "STATUS" + ToString(status);
    log += '\t';
    log += description;
    log += '\t';
    if (GetMessageId()) {
        log += "ID" + ::ToString(GetMessageId());
    }
    Log << log << Endl;
}

ISrvDispContext::ISrvDispContext(const TString& serviceName, ISenderTask& task, const NRTYServer::TMessage& message, const TDispatcherConfig* config)
    : ServiceName(serviceName)
    , Task(task)
    , Log(config->GetLog())
    , Message(message)
    , ServiceConfig(config->GetOwner().GetServicesConfig().GetConfig(serviceName))
{

}

