#include <yandex/maps/wiki/validator/storage/issue_creator.h>
#include <yandex/maps/wiki/common/passport_to_staff.h>
#include <yandex/maps/wiki/common/secrets.h>
#include <yandex/maps/wiki/common/string_utils.h>
#include <maps/libs/log8/include/log8.h>

#include "helpers.h"
#include "magic_strings.h"

#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>

namespace maps::wiki::validator::storage {

namespace {

const std::string ST_QUEUE_KEY = "MAPSERRORS";

const boost::format DESCRIPTION_FORMAT(
R"(Номер ветки: %1%
Важность: %2%
Проверка: %3%
Описание ошибки: %4%
Объекты:
%5%
Сообщил об ошибке: %6%
Ссылка на результаты валидации: %7%)");

Message messageById(pqxx::transaction_base& txn, const MessageId& messageId)
{
    auto result = txn.exec(
        "SELECT " + MESSAGE_SUBSTANCE_COLUMNS
        + " FROM " + TASK_MESSAGE_TABLE + JOIN_MESSAGE_SUBSTANCE_TABLES
        + " WHERE attributes_id = " + std::to_string(messageId.attributesId())
        + "   AND content_id = " + std::to_string(messageId.contentId())
        + " LIMIT 1");
    REQUIRE(!result.empty(), "Invalid message id: " << messageId);
    return messageFromDbRow(result[0]);
}

} // namespace

IssueCreator::IssueCreator(std::string baseUrl)
    : stGateway_(st::Configuration(
            std::move(baseUrl),
            common::secrets::tokenByKey(
                common::secrets::Key::RobotWikimapStToken)))
{
}

IssueCreator::IssueCreator(std::string baseUrl, std::string oAuthToken)
    : stGateway_(st::Configuration(
            std::move(baseUrl),
            std::move(oAuthToken)))
{
}

std::string IssueCreator::getOrCreateIssue(
    pqxx::transaction_base& txn,
    const MessageId& messageId,
    TId branchId,
    const std::string& reporterLogin,
    const std::string& pageUrl)
{
    auto result = txn.exec(
        "SELECT issue_key "
        "FROM validation.startrek_issue "
        "WHERE attributes_id = " + std::to_string(messageId.attributesId()) +
        "  AND content_id = " + std::to_string(messageId.contentId()));
    if (!result.empty()) {
        return result[0][0].as<std::string>();
    }

    std::string key;
    try {
        key = createIssue(txn, messageId, branchId, reporterLogin, pageUrl);
    } catch (const st::ConflictError& e) {
        INFO() << "Conflict error. Try to find issue";
        key = findIssue(messageId);
    }
    REQUIRE(!key.empty(), "Key is empty");

    txn.exec(
        "INSERT INTO validation.startrek_issue "
        "VALUES (" +
            std::to_string(messageId.attributesId()) + "," +
            std::to_string(messageId.contentId()) + "," +
            txn.quote(key) +
        ")");

    return key;
}

std::string IssueCreator::createIssue(
    pqxx::transaction_base& txn,
    const MessageId& messageId,
    TId branchId,
    const std::string& reporterLogin,
    const std::string& pageUrl)
{
    st::IssuePatch issuePatch;
    issuePatch.summary().set("Ошибка в релизной ветке #" + std::to_string(branchId));

    auto message = messageById(txn, messageId);

    auto description = (boost::format(DESCRIPTION_FORMAT)
        % branchId
        % message.attributes().severity
        % message.attributes().checkId
        % message.attributes().description
        % common::join(
            message.revisionIds().begin(),
            message.revisionIds().end(),
            [&](const revision::RevisionID& revId) {
                return "https://n.maps.yandex.ru/#!/objects/" + std::to_string(revId.objectId());
            },
            "\n")
        % reporterLogin
        % pageUrl).str();

    issuePatch.description().set(description);

    const auto staffLogin = common::getStaffLoginByPassportLogin(reporterLogin);
    if (!staffLogin.empty()) {
        issuePatch.createdBy().set(staffLogin);
    }

    auto uniqueTag = "validation_" + boost::lexical_cast<std::string>(messageId);
    auto severityTag = "severity_" + boost::lexical_cast<std::string>(message.attributes().severity);
    auto checkTag = "check_" + message.attributes().checkId;
    auto descriptionTag = "description_" + message.attributes().description;
    auto loginTag = "login_" + reporterLogin;

    issuePatch.tags().set({uniqueTag, severityTag, checkTag, descriptionTag, loginTag});

    auto issue = stGateway_.createIssue(ST_QUEUE_KEY, issuePatch, uniqueTag);

    INFO() << "Issue created " << issue.key();

    return issue.key();
}

std::string IssueCreator::findIssue(const MessageId& messageId)
{
    auto unique = "validation_" + boost::lexical_cast<std::string>(messageId);

    auto query = "Tags: " + unique;

    auto collection = stGateway_.loadIssues(query);
    REQUIRE(collection.begin() != collection.end(),
        "Failed to find issue for the validation message " << messageId);
    const auto& issue = *collection.begin();

    INFO() << "Issue found " << issue.key();

    return issue.key();
}

} // namespace maps::wiki::validator
