#include "grade_status.h"

#include <maps/wikimap/mapspro/libs/assessment/impl/magic_strings.h>
#include <maps/wikimap/mapspro/libs/assessment/impl/sql_helpers.h>

#include <fmt/format.h>

namespace maps::wiki::assessment::units {

using namespace fmt::literals;

namespace sqlClause {

namespace {

const auto GRADE_CONFIRMED = fmt::format(
    "{qualification} = '{basic}' AND "
    "{value} = {last_expert_value}",

    "value"_a = sql::col::VALUE,
    "last_expert_value"_a = sql::col::LAST_EXPERT_VALUE,
    "qualification"_a = sql::col::QUALIFICATION,

    "basic"_a = toString(Qualification::Basic));

const auto GRADE_REFUTATION_NOT_ADDRESSED = fmt::format(
    "{qualification} = '{basic}' AND "
    "{value} != {last_expert_value} AND "
    "{refutation_accepted_at} IS NULL",

    "value"_a = sql::col::VALUE,
    "qualification"_a = sql::col::QUALIFICATION,
    "last_expert_value"_a = sql::col::LAST_EXPERT_VALUE,
    "refutation_accepted_at"_a = sql::col::REFUTATION_ACCEPTED_AT,

    "basic"_a = toString(Qualification::Basic));

const auto GRADE_REFUTATION_ACCEPTED = fmt::format(
    "{qualification} = '{basic}' AND "
    "{value} != {last_expert_value} AND "
    "{refutation_accepted_at} IS NOT NULL",

    "value"_a = sql::col::VALUE,
    "qualification"_a = sql::col::QUALIFICATION,
    "last_expert_value"_a = sql::col::LAST_EXPERT_VALUE,
    "refutation_accepted_at"_a = sql::col::REFUTATION_ACCEPTED_AT,

    "basic"_a = toString(Qualification::Basic));

const auto TRY_ACCEPT_GRADE_REFUTATION_FMT = fmt::format(
    "UPDATE {grade} as g "
    "SET {refutation_accepted_at} = NOW() "
    "FROM {unit} as u "
    "WHERE g.{unit_id} = {{unit_id}} AND u.{unit_id} = {{unit_id}} AND {{refutation_acceptable}}",

    "grade"_a = sql::table::GRADE,
    "unit"_a = sql::table::UNIT,

    "unit_id"_a = sql::col::UNIT_ID,
    "refutation_accepted_at"_a = sql::col::REFUTATION_ACCEPTED_AT);

} // namespace

std::string gradeStatusEquals(GradeStatus status)
{
    switch (status) {
    case GradeStatus::Confirmed:
        return GRADE_CONFIRMED;

    case GradeStatus::Refuted:
        return GRADE_REFUTATION_NOT_ADDRESSED;

    case GradeStatus::RefutationAccepted:
        return GRADE_REFUTATION_ACCEPTED;
    };
}

std::string refutationAcceptable(TUid uid)
{
    return "COALESCE(" +
        sql::col::GRADED_BY + " = " + std::to_string(uid) + " AND (" + GRADE_REFUTATION_NOT_ADDRESSED + "), "
        "FALSE)";
}

} // sqlClause

bool tryAcceptRefutation(pqxx::transaction_base& txn, TUid uid, TId unitId)
{
    const auto result = txn.exec(fmt::format(
        sqlClause::TRY_ACCEPT_GRADE_REFUTATION_FMT,
        "unit_id"_a = unitId,
        "refutation_acceptable"_a = sqlClause::refutationAcceptable(uid)));

    return result.affected_rows();
}

} // maps::wiki::assessment::units
