#include "distributor_context.h"
#include <saas/indexerproxy/configs/config.h>
#include <saas/indexerproxy/dispatching/dispatch_queue.h>

TDistributorReplier::TDistributorReplier(const TString& serviceName, ISenderTask& task, const NRTYServer::TMessage& message, const TDispatcherConfig* config, TDeferredMessagesQueue& storage)
    : TReplier(serviceName, task, message, config, storage)
    , Handler(this, config)
{
}

bool TDistributorReplier::DoStoreStatus(const TString& backend, const TString& status) {
    VERIFY_WITH_LOG(GetServiceName() == backend, "%s != %s", GetServiceName().data(), backend.data());
    if (!!status)
        ReplyOverride = NRTYServer::TReply::dsCANT_STORE_IN_QUEUE;
    return !status;
}

void TDistributorReplier::DoFinish() {
    CHECK_WITH_LOG(!IsAsync);
    if (GetDispStatus() == NRTYServer::TReply::dsNO_REPLY || GetDispStatus() == NRTYServer::TReply::dsQUORUM_FAILED) {
        DEBUG_LOG << "Distributor's message storing in queue... " << GetServiceName() << "/" << Message.GetDocument().GetUrl() << Endl;
        StoreInQueue(GetServiceName(), Message);
        DEBUG_LOG << "Distributor's message stored in queue... OK " << GetServiceName() << "/" << Message.GetDocument().GetUrl() << Endl;
    }
}

NRTYServer::TReply::TDispStatus TDistributorReplier::DoGetDispStatus() const {
    ui32 success = 0;
    ui32 failure = 0;
    ui32 incorrect = 0;

    for (auto&& i : Status) {
        if (!i.second.Defined()) {
            continue;
        }

        const TSendStatus& status = i.second.GetRef();
        if (!status.IsReplyRead()) {
            ++failure;
            continue;
        }

        switch (status.GetRTYStatus()) {
        case NRTYServer::TReply::OK:
            ++success;
            break;
        case NRTYServer::TReply::INCORRECT_DOCUMENT:
            ++incorrect;
            break;
        default:
            ++failure;
            break;
        }
    }

    if (success && incorrect) {
        return NRTYServer::TReply::dsDIFFERENT_ANSWERS;
    }
    if (success && failure) {
        const float threshold = ServiceConfig.Quorum;
        const float quorum = float(success) / (success + failure);
        return (quorum >= threshold) ? NRTYServer::TReply::dsQUORUM_PASSED : NRTYServer::TReply::dsQUORUM_FAILED;
    }
    if (success) {
        return NRTYServer::TReply::dsOK;
    }
    if (incorrect) {
        return NRTYServer::TReply::dsUSER_ERROR;
    }

    return NRTYServer::TReply::dsNO_REPLY;
}

void TDistributorReplier::THandlerOnSend::OnBeforeSending(const TVector<NBus::TNetAddr>& addrs) const {
    Replier->SetBackendCounter(addrs.size());
}

void TDistributorReplier::THandlerOnSend::OnReply(const NBus::TNetAddr& addr, const NRealTime::TIndexedDocDepSlip::EStatus status, const TString& message) const {
    Replier->RegisterStatus(TSendStatus(addr, status, message));
}

void TDistributorReplier::THandlerOnSend::OnError(const NBus::TNetAddr& addr, const TString& error) const {
    Replier->RegisterStatus(TSendStatus(addr, error));
}

TDistributorReplier::THandlerOnSend::THandlerOnSend(ISrvDispContext* replier, const TDispatcherConfig* config) {
    Replier = replier;
    Config = config;
}

bool TDistributorReplier::DoVerify(const TProxyConfig& /*config*/, const TRTYMessageBehaviour& bh) const {
    if (bh.IsBroadcastMessage || IsAsyncMessage(GetMessage())) {
        ForceMessageStatus(NRTYServer::TReply::dsUSER_ERROR, "broadcast and async messages are not supported in distributor");
        return false;
    }
    return true;
}

bool TDistributorReplier::DoSend(const TProxyConfig& config, TDispatchServiceSelector& selector) {
    const auto* it = config.GetServiceInfo(GetServiceName());
    VERIFY_WITH_LOG(it, "incorrect service %s", GetServiceName().data());
    CHECK_WITH_LOG(it->IndexingTarget == NSearchMapParser::Distributor);
    VERIFY_WITH_LOG(!!it->Stream, "incorrect distributor stream");
    VERIFY_WITH_LOG(!!it->GetDistributors(), "incorrect distributors list");

    selector.GetDistributors().Send(GetServiceName(), GetMessage(), &Handler);
    return true;
}

