#include "tools.h"

#include <yandex/maps/wiki/common/pg_retry_helpers.h>
#include <yandex/maps/wiki/common/retry_duration.h>
#include <yandex/maps/wiki/revision/branch_manager.h>
#include <yandex/maps/wiki/tasks/tasks.h>
#include <yandex/maps/wiki/tasks/branch.h>

#include <sstream>

namespace maps::wiki::release {
namespace {

const std::string SCHEMA = "service";
const std::string WORKING_DATA_TABLE = TASK_NAME + "_working_data";
const std::string BRANCH_ID_COLUMN = "branch_id";
const std::string TASK_ID_COLUMN = "task_id";
const std::string RUN_SCHEDULED_TASK_ATTRIBUTE = "run_scheduled_task";

const size_t ARCHIVE_BRANCH_MAX = 7;

bool getBoolTaskAttribute(pgpool3::Pool& pool, const std::string& attributeName)
{
    auto attributes = common::retryDuration([&] {
        auto txn = pool.masterReadOnlyTransaction();
        return tasks::attributesForTaskType(*txn, TASK_NAME);
    });

    auto it = attributes.find(attributeName);
    return it != attributes.end() && it->second == "1";
}

} // anonymous namespace

revision::Branch getApprovedBranch(pgpool3::Pool& pool)
{
    return common::retryDuration([&] {
        auto txn = pool.masterReadOnlyTransaction();
        revision::BranchManager branchManager(*txn);
        return branchManager.loadApproved();
    });
}

std::optional<revision::Branch> getStableBranch(pgpool3::Pool& pool)
{
    auto branches = common::retryDuration([&] {
        auto txn = pool.masterReadOnlyTransaction();
        revision::BranchManager branchManager(*txn);
        return branchManager.load(
            {{revision::BranchType::Stable, revision::BranchManager::UNLIMITED}});
    });

    if (branches.empty()) {
        return std::nullopt;
    }
    return branches.front();
}

std::optional<revision::DBID> mostRecentArchiveBranchId(pgpool3::Pool& pool)
{
    auto branches = common::retryDuration([&] {
        auto txn = pool.masterReadOnlyTransaction();
        revision::BranchManager branchManager(*txn);
        return branchManager.load({{revision::BranchType::Archive, 1}});
    });

    if (branches.empty()) {
        return std::nullopt;
    }
    return branches.front().id();
}

bool isTaskFrozen(pgpool3::Pool& pool, TaskId taskId)
{
    return common::retryDuration([&] {
        auto txn = pool.masterReadOnlyTransaction();
        return tasks::isFrozen(*txn, taskId);
    });
}

void saveResult(pgpool3::Pool& pool, TaskId taskId, revision::DBID branchId)
{
    std::ostringstream sql;
    sql << "INSERT INTO " << SCHEMA << "." << WORKING_DATA_TABLE
        << "(" << TASK_ID_COLUMN << ", " << BRANCH_ID_COLUMN
        << ") VALUES (" << taskId << ", " << branchId << ")";

    common::execCommitWithRetries(
        pool,
        "saveResult",
        {}, // searchPath
        sql.str());
}

bool isTaskScheduled(pgpool3::Pool& pool)
{
    return getBoolTaskAttribute(pool, RUN_SCHEDULED_TASK_ATTRIBUTE);
}

std::optional<revision::Branch>
publishStableBranch(
    pgpool3::Pool& corePool,
    pgpool3::Pool& labelsPool,
    revision::UserID uid)
{
    auto branches = common::retryDuration([&] {
        auto txnCore = corePool.masterReadOnlyTransaction();
        revision::BranchManager branchManager(*txnCore);
        return branchManager.load(
            {{revision::BranchType::Stable, revision::BranchManager::UNLIMITED}});
    });

    if (branches.empty()) {
        return std::nullopt;
    }

    auto branch = branches.back();

    auto txnCore = corePool.masterWriteableTransaction();
    auto txnLabels = labelsPool.masterWriteableTransaction();

    tasks::publishBranch(*txnCore, *txnLabels, branch, uid);

    txnLabels->commit();
    txnCore->commit();
    return branch;
}

std::optional<revision::Branch>
deleteExceedingArchiveBranch(
    pgpool3::Pool& corePool,
    pgpool3::Pool& viewPool,
    pgpool3::Pool& labelsPool,
    revision::UserID uid)
{
    auto branches = common::retryDuration([&] {
        auto txnCore = corePool.masterReadOnlyTransaction();
        revision::BranchManager branchManager(*txnCore);
        return branchManager.load(
            {{revision::BranchType::Archive, revision::BranchManager::UNLIMITED}});
    });

    if (branches.empty()) {
        return std::nullopt;
    }
    if (branches.size() < ARCHIVE_BRANCH_MAX) {
        return std::nullopt;
    }

    auto branch = branches.back();

    auto txnCore = corePool.masterWriteableTransaction();
    {
        auto txnView = viewPool.masterWriteableTransaction();
        tasks::deleteBranch(*txnCore, *txnView, branch, uid);
        txnView->commit();
    }
    {
        auto txnLabels = labelsPool.masterWriteableTransaction();
        tasks::deleteVrevisionsForBranch(*txnLabels, branch);
        txnLabels->commit();
    }
    txnCore->commit();

    return branch;
}

} // namespace maps::wiki::release
