#include "exception.h"

#include "magic_strings.h"
#include <maps/wikimap/mapspro/libs/acl/include/exception.h>

#include <maps/libs/common/include/exception.h>
#include <maps/libs/json/include/exception.h>
#include <maps/libs/xml/include/xmlexception.h>
#include <maps/libs/enum_io/include/enum_io.h>
#include <maps/infra/yacare/include/yacare.h>

namespace maps::wiki::aclsrv {

namespace {

constexpr enum_io::Representations<Error::Status> ERROR_STATUS_ENUM_REPRESENTATION {
    {Error::Status::UserAlreadyExists, "ERR_USER_ALREADY_EXISTS"},
    {Error::Status::RoleAlreadyExists, "ERR_ROLE_ALREADY_EXISTS"},
    {Error::Status::GroupAlreadyExists, "ERR_GROUP_ALREADY_EXISTS"},
    {Error::Status::UserNotExists, "ERR_USER_NOT_EXISTS"},
    {Error::Status::RoleNotExists, "ERR_ROLE_NOT_EXISTS"},
    {Error::Status::GroupNotExists, "ERR_GROUP_NOT_EXISTS"},
    {Error::Status::Forbidden, "ERR_FORBIDDEN"}
};

const std::string ERR_BAD_REQUEST = "ERR_BAD_REQUEST";
const std::string INTERNAL_ERROR = "INTERNAL_ERROR";

void formatJsonError(
    yacare::Response& response,
    std::string_view status,
    std::string_view message)
{
    response << YCR_JSON(respBuilder) {
        respBuilder[ERROR] << YCR_JSON(errorBuilder) {
            errorBuilder[STATUS] = status;
            errorBuilder[MESSAGE] = message;
        };
    };
}

void formatStdException(
    yacare::Response& response,
    const std::exception& ex)
{
    response.setStatus(500);
    formatJsonError(response, INTERNAL_ERROR, ex.what()); // HTTP 500
}

} // namespace

DEFINE_ENUM_IO(Error::Status, ERROR_STATUS_ENUM_REPRESENTATION);

Error::Error(Status status)
    : yacare::Error(303, {})
    , status_(status)
{}

void Error::format(yacare::Response& response) const
{
    formatJsonError(response, toString(status_), what());
}

void errorReporter(const yacare::Request& /*request*/, yacare::Response& response)
{
    try
    {
        throw;
    } catch (const maps::xml3::NodeNotFound& ex) {
        formatJsonError(response, ERR_BAD_REQUEST, ex.what());
    } catch (const maps::xml3::AttributeNotFound& ex) {
        formatJsonError(response, ERR_BAD_REQUEST, ex.what());
    } catch (const maps::xml3::NodeIsBlank& ex) {
        formatJsonError(response, ERR_BAD_REQUEST, ex.what());
    } catch (const yacare::ClientError& ex) {
        formatJsonError(response, ERR_BAD_REQUEST, ex.what());
    } catch (const acl::UserNotExists& ex) {
        response << (Error(Error::Status::UserNotExists) << ex.what());
    } catch (const acl::RoleNotExists& ex) {
        response << (Error(Error::Status::RoleNotExists) << ex.what());
    } catch (const acl::GroupNotExists& ex) {
        response << (Error(Error::Status::GroupNotExists) << ex.what());
    } catch (const acl::DuplicateGroup& ex) {
        response << (Error(Error::Status::GroupAlreadyExists) << ex.what());
    } catch (const acl::DuplicateUser& ex) {
        response << (Error(Error::Status::UserAlreadyExists) << ex.what());
    } catch (const acl::DuplicateRole& ex) {
        response << (Error(Error::Status::RoleAlreadyExists) << ex.what());
    } catch (const acl::AccessDenied& ex) {
        response << (Error(Error::Status::Forbidden) << ex.what());
    } catch (const json::Error& ex) {
        ERROR() << "Json error : " << ex;
        formatJsonError(response, ERR_BAD_REQUEST, ex.what()); // HTTP 200
    } catch (const Error& e) {
        response << e;
    } catch (const acl::ACLException& ex) {
        ERROR() << "Unknown acl error : " << ex;
        formatStdException(response, ex); // HTTP 500
    } catch (const maps::Exception& ex) {
        ERROR() << "Maps error : " << ex;
        formatStdException(response, ex); // HTTP 500
    } catch (const std::exception& ex) {
        ERROR() << "Std error : " << ex.what();
        formatStdException(response, ex); // HTTP 500
    }
}

} // namespace maps::wiki::aclsrv
