#pragma once

#include "common.h"
#include <yandex/maps/wiki/revision/branch.h>
#include <maps/libs/pgpool/include/pgpool3.h>
#include <pqxx/pqxx>
#include <string>
#include <list>

namespace maps {
namespace wiki {

const std::string BRANCH_TYPE_PRODUCTION = "production";

const std::string STABLE_BRANCH_CREATION_ATTR = "stable_branch_creation";

typedef std::list<revision::Branch> Branches;

std::string vrevisionsSchemaName(TBranchId branchId);

Token getSocialToken(const Token& token);

void checkUnavailable(const revision::Branch& branch);
void checkProgress(const revision::Branch& branch);
void checkFinished(const revision::Branch& branch);

// for approved branch
bool hasStabeBranchCreationAttribute(const revision::Branch& branch);

Branches loadHeadBranches(pqxx::transaction_base& work);

class SharedTransaction
{
public:
    SharedTransaction() = default;

    SharedTransaction(pgpool3::TransactionHandle&& handle)
        : handle_(std::make_shared<pgpool3::TransactionHandle>(std::move(handle)))
    {}

    operator bool() const { return !!handle_; }

    pqxx::transaction_base* operator->() const
    {
        return handle_->getPqxxPtr();
    }

    pqxx::transaction_base& operator*() const
    {
        return **handle_;
    }

private:
    std::shared_ptr<pgpool3::TransactionHandle> handle_;
};

class BranchContext
{
public:
    BranchContext(
        revision::Branch branch,
        AccessMode accessMode,
        SharedTransaction workCore,
        SharedTransaction workSocial = SharedTransaction(),
        SharedTransaction workView = SharedTransaction());

    Token commit();

    const revision::Branch branch;
    const AccessMode accessMode;

    pqxx::transaction_base& txnCore() const { return *workCore_; }
    const SharedTransaction& workCore() const { return workCore_; }

    pqxx::transaction_base& txnSocial() const;
    pqxx::transaction_base& txnView() const;
    pqxx::transaction_base& txnLabels() const;

    void releaseViewTxn();

private:
    SharedTransaction workCore_;

    struct OptionalTransactions {
        SharedTransaction workSocial;
        SharedTransaction workView;
        SharedTransaction workLabels;
    };
    std::shared_ptr<OptionalTransactions> optionalTransactions_;
};


class BranchContextFacade
{
public:
    enum class CheckBranchState { Skip, CheckNormal };

    explicit BranchContextFacade(
            TBranchId branchId = revision::TRUNK_BRANCH_ID,
            CheckBranchState checkBranchState = CheckBranchState::Skip)
        : branchId(branchId)
        , checkBranchState(checkBranchState)
    {}

    virtual ~BranchContextFacade() = default;

    BranchContext acquireManageBranches(TUid uid) const;

    static BranchContext acquireRead(TBranchId branchId, const Token& token);
    static BranchContext acquireWrite(TBranchId branchId, TUid uid);

    BranchContext acquireLongReadCoreOnly(TCommitId commitId) const;
    BranchContext acquireReadCoreView(const Token& token) const;
    BranchContext acquireReadCoreSocial(const Token& token) const;
    BranchContext acquireWrite() const;

    static pgpool3::TransactionHandle acquireWorkReadViewOnly(TBranchId branchId, const Token& token);
    static pgpool3::TransactionHandle acquireWorkWriteViewOnly(TBranchId branchId);
    static pgpool3::TransactionHandle acquireWorkWriteLabelsOnly(TBranchId branchId);

protected:
    void check(const revision::Branch& branch) const;

private:
    TBranchId branchId;
    CheckBranchState checkBranchState;
};

class CheckedTrunkBranchContextFacade : public BranchContextFacade
{
public:
    CheckedTrunkBranchContextFacade()
        : BranchContextFacade(
            revision::TRUNK_BRANCH_ID, CheckBranchState::CheckNormal)
    {}
};

} // namespace wiki
} // namespace maps
