#include "pubsub.h"

#include <yandex/maps/wiki/revision/revisionsgateway.h>

#include <boost/format.hpp>

namespace maps {
namespace wiki {
namespace contours {
namespace {

// todo: move to pubsub library
typedef int64_t TransactionID;

constexpr TransactionID INVALID_TRANSACTION_ID = 0;

struct Watermark {
    TransactionID txnId;
    pubsub::CommitId commitId;
};

Watermark currentWatermark(pqxx::transaction_base& viewTxn)
{
    auto res = viewTxn.exec(
        ( boost::format(
            "SELECT txid, commit_id FROM pubsub.watermark\n"
            "WHERE consumer_id='%1%' AND branch_id=%2%")
        % CONSUMER_ID
        % revision::TRUNK_BRANCH_ID
        ).str());
    return res.empty()
        ? Watermark{INVALID_TRANSACTION_ID, INVALID_COMMIT_ID}
        : Watermark{res[0][0].as<TransactionID>(), res[0][1].as<pubsub::CommitId>()};
}

} // namespace

PubsubWrapper::PubsubWrapper(pqxx::transaction_base& viewTxn, size_t commitsBatchSize)
    : viewTxn_(viewTxn)
    , consumer_(viewTxn_, CONSUMER_ID, revision::TRUNK_BRANCH_ID)
    , isFirstTime_(INVALID_COMMIT_ID == currentWatermark(viewTxn_).commitId)
{
    consumer_.setBatchSizeLimit(commitsBatchSize);
    consumer_.setOutOfOrderDisabled(true);
}

CommitRange PubsubWrapper::consumeBatch(pqxx::transaction_base& revisionTxn)
{
    CommitRange result {INVALID_COMMIT_ID, INVALID_COMMIT_ID};
    if (isFirstTime_) {
        while (!consumer_.consumeBatch(revisionTxn).empty());
        result.second = currentWatermark(viewTxn_).commitId;
        isFirstTime_ = false;
    } else {
        auto commitIds = consumer_.consumeBatch(revisionTxn);
        if (!commitIds.empty()) {
            auto itrs = std::minmax_element(commitIds.begin(), commitIds.end());
            result.first = *itrs.first;
            result.second = *itrs.second;
        }
    }
    return result;
}

void PubsubWrapper::reset()
{
    viewTxn_.exec(
        ( boost::format(
            "DELETE FROM pubsub.watermark\n"
            "WHERE consumer_id='%1%' AND branch_id=%2%")
        % CONSUMER_ID
        % revision::TRUNK_BRANCH_ID
        ).str());
    isFirstTime_ = true;
}

// todo: move to pubsub library
size_t queueSize
    ( pqxx::transaction_base& revisionTxn
    , pqxx::transaction_base& viewTxn
    )
{
    auto watermark = currentWatermark(viewTxn);
    auto res = revisionTxn.exec(
        ( boost::format(
            "SELECT count(*)\n"
            "FROM revision.commit_queue\n"
            "WHERE branch_id=%1%\n"
            "AND (txid,commit_id) > (%2%,%3%)\n")
        % revision::TRUNK_BRANCH_ID
        % watermark.txnId
        % watermark.commitId
        ).str());
    return res.empty()? 0: res[0][0].as<size_t>();
}

} // contours
} // wiki
} // maps
