#include "yacare_helpers.h"
#include "magic_strings.h"
#include <maps/wikimap/mapspro/services/editor/src/exception_handling.h>

void UidParamType::assign(maps::wiki::TUid uid)
{
    WIKI_REQUIRE(uid, maps::wiki::ERR_FORBIDDEN,
                 " Invalid parameter uid");
    uid_ = uid;
}

std::istream& operator >> (std::istream& is, UidParamType& uidParam)
{
    maps::wiki::TUid uid = 0;
    is >> uid;
    uidParam.assign(uid);
    return is;
}

maps::wiki::common::FormatType
requestedFormat(const yacare::Request& r)
{
    return getParam<maps::wiki::common::FormatType>(
        r, "format", maps::wiki::common::FormatType::XML);
}

std::string
httpContentType(maps::wiki::common::FormatType formatType)
{
    switch (formatType)
    {
        case maps::wiki::common::FormatType::XML:
            return "text/xml";
        case maps::wiki::common::FormatType::JSON:
            return "application/json";
    }
    return {};
}

boost::optional<maps::chrono::TimePoint>
getOptionalTimePointParam(const yacare::Request& request, const std::string& name)
{
    auto str = getOptionalParam<std::string>(request, name);
    boost::optional<maps::chrono::TimePoint> result;
    if (str) {
        result = maps::chrono::parseIsoDateTime(*str);
    }
    return result;
}

maps::wiki::StringMap
gatherCommitAttributes(const yacare::Request& request)
{
    maps::wiki::StringMap commitAttributes;
    for (const auto& param : request.input()) {
        const auto& paramName = param.first;
        const auto& paramValues = param.second;
        if (paramValues.empty() || paramValues.front().empty()) {
            continue;
        }
        if (maps::wiki::STR_BLD_RECOGNITION_TASK_ID != paramName) {
            continue;
        }
        WIKI_REQUIRE(paramValues.size() == 1, maps::wiki::ERR_BAD_REQUEST,
            "Request to commit array params are not supported: " << paramName);
        commitAttributes.emplace(paramName, paramValues.front());
    }
    return commitAttributes;
}

boost::optional<maps::wiki::social::EventType>
getEventType(const yacare::Request& r)
{
    static const std::string key = "event-type";
    if (!r.input().has(key)) {
        return boost::none;
    }
    auto value = getParam<std::string>(r, key);
    if (value == "all") {
        return boost::none;
    }
    return maps::enum_io::fromString<maps::wiki::social::EventType>(value);
}

void wrapError(yacare::Response& resp, const maps::wiki::controller::ExceptionInfo& exInfo)
{
    resp.setStatus(
        exInfo.isInternalError() == maps::wiki::controller::IsInternalError::Yes
            ? yacare::HTTPStatus::InternalError
            : yacare::HTTPStatus::OK);

    resp << exInfo.formattedMessage();
}

void yacareErrorReporter(const yacare::Request& req, yacare::Response& resp)
{
    resp.reset();
    auto requestFormat = requestedFormat(req);
    resp[maps::wiki::HTTP_HEADER_CONTENT_TYPE] = httpContentType(requestFormat);
    std::stringstream reqStr;
    reqStr << req;
    try {
        try {
            throw;
        }
        catch (const yacare::errors::BadRequest& ex) {
            THROW_WIKI_LOGIC_ERROR(maps::wiki::ERR_BAD_REQUEST, "");
        }
    } catch (const yacare::Error& err) {
        auto exInfo = maps::wiki::handleException(
            reqStr.str(), requestFormat,
            maps::wiki::InternalErrorPolicy::Serialize, nullptr);

        resp.setStatus(yacare::HTTPStatus::find(err.status()));
        resp << exInfo.formattedMessage();
    } catch (...) {
        wrapError(
            resp,
            maps::wiki::handleException(
                reqStr.str(), requestFormat,
                maps::wiki::InternalErrorPolicy::Serialize, nullptr));
    }
}
