#include <yandex/maps/wiki/validator/message.h>
#include <maps/libs/common/include/exception.h>

#include <algorithm>
#include <map>

namespace maps {
namespace wiki {
namespace validator {

namespace {

const std::string SEVERITY_WARNING_STR = "warn";
const std::string SEVERITY_ERROR_STR = "error";
const std::string SEVERITY_CRITICAL_STR = "crit";
const std::string SEVERITY_FATAL_STR = "fatal";

const std::string REGIONTYPE_IMPORTANT_STR = "important";
const std::string REGIONTYPE_UNIMPORTANT_STR = "unimportant";

const std::map<Severity, std::string> SEVERITY_TO_STR = {
    { Severity::Warning, SEVERITY_WARNING_STR },
    { Severity::Error, SEVERITY_ERROR_STR },
    { Severity::Critical, SEVERITY_CRITICAL_STR },
    { Severity::Fatal, SEVERITY_FATAL_STR }
};

const std::map<std::string, Severity> STR_TO_SEVERITY = {
    { SEVERITY_WARNING_STR, Severity::Warning },
    { SEVERITY_ERROR_STR, Severity::Error },
    { SEVERITY_CRITICAL_STR, Severity::Critical },
    { SEVERITY_FATAL_STR, Severity::Fatal }
};

} // namespace

// ---------------------- Severity -------------------------

//TODO: rewrite with the maps/libs/enum_io library
std::istream& operator>>(std::istream& is, Severity& severity)
{
    std::string str;
    is >> str;
    const auto iter = STR_TO_SEVERITY.find(str);
    REQUIRE(iter != STR_TO_SEVERITY.end(), "Bad severity string: " << str);
    severity = iter->second;
    return is;
}

std::ostream& operator<<(std::ostream& os, Severity severity)
{
    const auto iter = SEVERITY_TO_STR.find(severity);
    REQUIRE(iter != SEVERITY_TO_STR.end(),
        "Bad severity value: " << static_cast<size_t>(severity));
    os << iter->second;
    return os;
}

// ---------------------- RegionType -------------------------

//TODO: rewrite with the maps/libs/enum_io library
std::istream& operator>>(std::istream& is, RegionType& regionType)
{
    std::string str;
    is >> str;
    if (str == REGIONTYPE_IMPORTANT_STR) {
        regionType = RegionType::Important;
    } else if (str == REGIONTYPE_UNIMPORTANT_STR) {
        regionType = RegionType::Unimportant;
    } else {
        throw maps::RuntimeError() << "bad region type: " << str;
    }
    return is;
}

std::ostream& operator<<(std::ostream& os, RegionType regionType)
{
    switch (regionType) {
    case RegionType::Important:
        os << REGIONTYPE_IMPORTANT_STR;
        break;
    case RegionType::Unimportant:
        os << REGIONTYPE_UNIMPORTANT_STR;
        break;
    }
    return os;
}

// ---------------------- Message -------------------------

Message::Message(
        Severity severity,
        TCheckId checkId,
        std::string description,
        RegionType regionType,
        std::string wkb,
        std::vector<RevisionID> revisionIds)
    : attributes_(Attributes{severity,
                            std::move(checkId),
                            std::move(description),
                            regionType})
    , geomWkb_(std::move(wkb))
    , revisionIds_(std::move(revisionIds))
{
    std::sort(revisionIds_.begin(), revisionIds_.end());
}

} // namespace validator
} // namespace wiki
} // namespace maps
