#include <maps/wikimap/mapspro/libs/rubrics/include/mapping_utils.h>

#include <maps/libs/log8/include/log8.h>
#include <maps/libs/xml/include/xml.h>

#include <fstream>

namespace maps {
namespace wiki {
namespace rubrics {

RubricMapping
readBackaMapping(const std::string& fileName)
{
    RubricMapping backaMapping;
    try {
        xml3::Doc doc(fileName);
        doc.addNamespace("backa", "http://maps.yandex.ru/backa/1.x");
        auto rubricWithFtTypeNodes = doc.nodes("//backa:Rubric[@ftTypeId]", true);
        for (size_t i = 0; i < rubricWithFtTypeNodes.size(); ++i) {
            const auto& rubricNode = rubricWithFtTypeNodes[i];
            auto nameNode = rubricNode.node("backa:name[@xml:lang=\"ru\"]", true);
            auto ftTypeId = rubricNode.attr<FtTypeId>("ftTypeId");
            backaMapping[ftTypeId].insert(
                RubricMappingRecord {
                    rubricNode.attr<RubricId>("id"),
                    ftTypeId,
                    nameNode.isNull() ? std::string() : nameNode.value<std::string>()
                }
            );
        }
    } catch (const std::exception& ex) {
        WARN() << "Exception on read/parse backa rubrics xml: "
            << fileName << ": " << ex.what();
    }
    REQUIRE(!backaMapping.empty(),
        "Failed to read/parse backa rubrics xml: "
            << fileName);
    return backaMapping;
}

RubricMapping
loadExportPoiMapping(const std::string& content)
{
    RubricMapping exportMapping;
    try {
        xml3::Doc doc(content, xml3::Doc::Source::String);
        auto rubricNodes = doc.nodes("//pair", true);
        for (size_t i = 0; i < rubricNodes.size(); ++i) {
            const auto& rubricNode = rubricNodes[i];
            auto ftTypeId = rubricNode.attr<FtTypeId>("ft_type_id");
            exportMapping[ftTypeId].insert(
                RubricMappingRecord {
                    rubricNode.attr<RubricId>("rubric_id"),
                    ftTypeId,
                    rubricNode.attr<std::string>("comment")
                }
            );
        }
    } catch (const std::exception& ex) {
        WARN() << "Exception on read/parse export rubrics mapping xml: "
            << ex.what();
    }
    REQUIRE(!exportMapping.empty(),
        "Failed to read/parse export rubrics mapping xml");
    return exportMapping;
}

RubricMapping
readExportPoiMapping(const std::string& fileName)
{
    std::ifstream in(fileName);
    std::stringstream buffer;
    buffer << in.rdbuf();
    return loadExportPoiMapping(buffer.str());
}

namespace {

void
writePair(std::ostream& out, const RubricMappingRecord& record)
{
    out << "    <pair ft_type_id=\"" << record.ftTypeId
        << "\" rubric_id=\"" << record.rubricId
        << "\" comment=\"" << record.comment<<"\"/>" << std::endl;
}

} // namespace

void
writeExportMapping(const RubricMapping& mapping, const std::string& fileName) try {
    std::ofstream out(fileName);
    out << "<rubrics_mapping>" << std::endl;
    for (const auto& pair : mapping) {
        for (const auto& mappingRec : pair.second) {
            writePair(out, mappingRec);
        }
    }
    out << "</rubrics_mapping>" << std::endl;
} catch (const std::exception& ex) {
    WARN() << "Exception writing mapping file " << fileName << ":" << ex.what();
}

} // namespace rubrics
} // namespace wiki
} // namespace maps
