#include "xml_formatter.h"
#include "xml_writer.h"

#include <yandex/maps/wiki/common/geom.h>
#include <yandex/maps/wiki/common/date_time.h>

namespace maps {
namespace wiki {

namespace vs = validator::storage;

namespace {

const std::string TASKS_XML_NS = "http://maps.yandex.ru/mapspro/tasks/1.x";
const std::string TASKS_XML_NS_PREFIX = "mpt";
const std::string TASKS_XML_NS_DECL =
    "xmlns:" + TASKS_XML_NS_PREFIX + "=" + "\"" + TASKS_XML_NS + "\"";

void serializeNode(const ValidationStatsNode& node, std::ostream& os)
{
    os << "<" << TASKS_XML_NS_PREFIX << ":" << node.field
       << " id=\"" << node.value << "\""
       << " count=\"" << node.count << "\">";

    for (const auto& child : node.children) {
        serializeNode(child, os);
    }

    os << "</" << TASKS_XML_NS_PREFIX << ":" << node.field << ">";
}

void serializeStatistics(
        const std::vector<ValidationStatsNode>& regionTypeStats,
        const std::vector<ValidationStatsNode>& severityStats,
        const std::vector<ValidationStatsNode>& checkStats,
        std::ostream& os)
{
    os << "<" << TASKS_XML_NS_PREFIX << ":region-types>";
    for (const auto& node : regionTypeStats) {
        serializeNode(node, os);
    }
    os << "</" << TASKS_XML_NS_PREFIX << ":region-types>";

    os << "<" << TASKS_XML_NS_PREFIX << ":severities>";
    for (const auto& node : severityStats) {
        serializeNode(node, os);
    }
    os << "</" << TASKS_XML_NS_PREFIX << ":severities>";

    os << "<" << TASKS_XML_NS_PREFIX << ":checks>";
    for (const auto& node : checkStats) {
        serializeNode(node, os);
    }
    os << "</" << TASKS_XML_NS_PREFIX << ":checks>";
}

void serializeMessage(
        const validator::storage::StoredMessageDatum& storedMessage,
        std::ostream& os)
{
    const validator::Message& message = storedMessage.message();
    os << "<" << TASKS_XML_NS_PREFIX << ":message"
       << " id=\"" << storedMessage.id() << "\""
       << " severity=\"" << message.attributes().severity << "\""
       << " check=\"" << message.attributes().checkId << "\""
       << " description=\"" << message.attributes().description << "\""
       << " region-type=\"" << message.attributes().regionType << "\""
       << " active=\"" << (storedMessage.isActive() ? "true" : "false") << "\">";

    const auto& exclusionInfo = storedMessage.exclusionInfo();
    if (exclusionInfo) {
        os << "<" << TASKS_XML_NS_PREFIX << ":exclusion"
           << " created=\""
           << common::canonicalDateTimeString(exclusionInfo->createdAt,
              common::WithTimeZone::Yes) << "\""
           << " created-by=\"" << exclusionInfo->createdBy << "\"/>";
    }

    if (!message.geomWkb().empty()) {
        Geom geom(message.geomWkb());

        os << "<" << TASKS_XML_NS_PREFIX << ":point>";
        geom.center().geoJson(os, SpatialRefSystem::Geodetic);
        os << "</" << TASKS_XML_NS_PREFIX << ":point>";

        if (geom.geometryTypeName() != common::Geom::geomTypeNamePoint) {
            os << "<" << TASKS_XML_NS_PREFIX << ":geometry>";
            geom.geoJson(os, SpatialRefSystem::Geodetic);
            os << "</" << TASKS_XML_NS_PREFIX << ":geometry>";
        }
    }

    os << "<" << TASKS_XML_NS_PREFIX << ":oids>";
    for (const auto& revId : message.revisionIds()) {
        os << "<" << TASKS_XML_NS_PREFIX << ":oid>"
           << revId.objectId()
           << "</" << TASKS_XML_NS_PREFIX << ":oid>";
    }
    os << "</" << TASKS_XML_NS_PREFIX << ":oids>";

    os << "</" << TASKS_XML_NS_PREFIX << ":message>";
}

} // namespace

std::string
XMLFormatter::operator ()(const ResultType<GetValidationExclusions>& result)
{
    XmlOutputStream output;
    XmlWriter::putHeader(output);
    output << "<response-validation-exclusions " << TASKS_XML_NS_DECL << ">"
         << "<exclusion-statistics>";
    serializeStatistics(result.regionTypeStats, result.severityStats, result.checkStats, output);
    output << "</exclusion-statistics>"
         << "<exclusions"
         << " total-count=\"" << result.totalCount << "\""
         << " page=\"" << result.page << "\""
         << " per-page=\"" << result.perPage << "\">";
        for (const auto& exclusion : result.exclusions) {
            serializeMessage(exclusion, output);
        }
        output << "</exclusions></response-validation-exclusions>";
    XmlWriter::putFooter(output);
    return output.str();
}

std::string
XMLFormatter::operator ()(const ResultType<SaveValidationExclusion>& result)
{
    XmlOutputStream output;
    XmlWriter::putHeader(output);
    output << "<response-save-validation-exclusion " << TASKS_XML_NS_DECL << ">";
    output << "<internal><token/></internal>";
    serializeMessage(*result.exclusion, output);
    output << "</response-save-validation-exclusion>";
    XmlWriter::putFooter(output);
    return output.str();
}


} // namespace wiki
} // namespace maps
