#pragma once

#include <yandex/maps/wiki/pubsub/item.h>

#include <pqxx/pqxx>

#include <string>
#include <vector>
#include <optional>

namespace maps::wiki::pubsub {

class CommitConsumer
{
public:
    // can raise AlreadyLockedException
    CommitConsumer(pqxx::transaction_base& consumerTxn,
                   ConsumerId consumerId,
                   BranchId branchId);

    CommitConsumer(const CommitConsumer&) = delete;
    CommitConsumer& operator=(const CommitConsumer&) = delete;

    std::vector<CommitId> consumeBatch(pqxx::transaction_base& revisionTxn);

    // default: 10
    size_t batchSizeLimit() const { return batchSizeLimit_; }
    CommitConsumer& setBatchSizeLimit(size_t);

    // default: false
    bool isOutOfOrderDisabled() const { return outOfOrderLimit_ == 0; }
    CommitConsumer& setOutOfOrderDisabled(bool);

    // Set watermark to the latest commit available. The chosen commit depends
    // on out-of-order knob. I.e. in case OOO is allowed it just choses the
    // commit with greatest ID even if there are unfinished transactions with
    // lower ID than commit's transaction ID
    // Returns the commit id it moved to
    std::optional<CommitId> fastForwardToLastCommit();

    // Test if a consumer is run for the first time
    bool isFirstRun() const;

private:
    void updateWatermark(const Item& newWatermark);
    void insertOutOfOrder(const std::vector<Item>& newOutOfOrder);

    pqxx::transaction_base& consumerTxn_;
    ConsumerId consumerId_;
    BranchId branchId_;

    size_t batchSizeLimit_;
    size_t outOfOrderLimit_;

    Item watermark_;
    std::set<Item> outOfOrderItems_;
};

// Get number of commits in the commits queue awaiting consumption
// Note: this function does not use PG mutex for synchronization so
// the result value is an approximation of the queue length.
size_t countCommitsInQueue(
    pqxx::transaction_base& consumerTxn,
    pqxx::transaction_base& revisionTxn,
    const ConsumerId& consumerId,
    BranchId branchId);

} // namespace maps::wiki::pubsub
