#include <maps/wikimap/ugc/account/src/lib/gdpr/takeout.h>

#include <maps/wikimap/ugc/libs/common/constants.h>

#include <maps/wikimap/mapspro/libs/query_builder/include/insert_query.h>

#include <maps/infra/yacare/include/yacare.h>

#include <maps/libs/json/include/builder.h>
#include <maps/libs/json/include/exception.h>
#include <maps/libs/json/include/value.h>

namespace maps::wiki::ugc::gdpr {

namespace {

const std::string ID = "ugc_id";
const std::string SLUG = "ugc_data";

query_builder::InsertQuery insertTakeoutQuery(
    Uid uid,
    const gdpr::RequestId& requestId)
{
    query_builder::InsertQuery query(tables::GDPR_TAKEOUT);
    query.append(
            columns::UID,
            std::to_string(uid.value()))
        .appendQuoted(
            columns::REQUEST_ID,
            requestId.value());

    return query;
}

} // namespace

std::string makeTakeoutErrorJson(const std::string& message, const std::string& code)
{
    maps::json::Builder builder;

    builder << [&](maps::json::ObjectBuilder b) {
        b["errors"] << [&](maps::json::ArrayBuilder b) {
            b << [&](maps::json::ObjectBuilder b) {
                b["code"] << code;
                b["message"] << message;
            };
        };
        b["status"] << "error";
    };

    return builder.str();
}

std::string makeTakeoutStatusResponseJson(const TakeoutStatus& takeoutStatus)
{
    maps::json::Builder builder;

    builder << [&](maps::json::ObjectBuilder b) {
        b["data"] << [&](maps::json::ArrayBuilder b) {
                b << [&](maps::json::ObjectBuilder b) {
                    b["id"] << ID;
                    b["slug"] << SLUG;
                    b["state"] << toString(takeoutStatus.state);
                    if (takeoutStatus.updateDate) {
                        b["update_date"] <<
                            maps::chrono::formatIsoDateTime(*takeoutStatus.updateDate);
                    }
                };
            };
        b["status"] << "ok";
    };

    return builder.str();
}

void checkCategoryId(const std::string& body) {
    try {
        auto bodyJson = maps::json::Value::fromString(body);

        std::vector<std::string> ids{bodyJson["id"]};
        if (std::find(ids.begin(), ids.end(), ID) == ids.end()) {
            throw yacare::errors::BadRequest()
                << "Takeout delete request body doesn't have correct id: " << ID << ", got " << body;
        }
    } catch (const maps::json::ParserError& e) {
        throw yacare::errors::BadRequest()
            << "Invalid json in request body: " << e.what();
    }
}

bool createTakeoutDeleteRequest(
    pqxx::transaction_base& txn,
    Uid uid,
    const gdpr::RequestId& requestId,
    TakeoutState state)
{
    switch (state) {
        case TakeoutState::Empty:
            return true;
        case TakeoutState::ReadyToDelete:
            break;
        case TakeoutState::DeleteInProgress:
            return false;
    }

    auto insertQuery = insertTakeoutQuery(uid, requestId);
    insertQuery.exec(txn);

    return true;
}

bool scheduleUserDataRemoval(
    pqxx::transaction_base& txn,
    Uid uid,
    const gdpr::RequestId& requestId)
{
    auto takeoutStatus = getTakeoutStatus(txn, uid);
    return createTakeoutDeleteRequest(txn, uid, requestId, takeoutStatus.state);
}

} // namespace maps::wiki::ugc::gdpr
