#include "module.h"

#include <yandex/maps/wiki/validator/check.h>
#include <yandex/maps/wiki/validator/categories.h>
#include <yandex/maps/wiki/validator/objects/road.h>

#include "../utils/misc.h"
#include "../utils/names.h"

#include <yandex/maps/wiki/common/misc.h>

#include <algorithm>
#include <map>

namespace maps {
namespace wiki {
namespace validator {
namespace checks {

using categories::RD_EL;
using categories::RD;
using categories::RD_NM;

const std::string SECONDARY_ROAD_TAG = "(дублер)";

VALIDATOR_CHECK_PART( rd_rd_el_relations, empty_roads, RD )
{
    context->objects<RD>().visit(
            [&](const Road* road)
    {
        if (road->elements().empty()) {
            context->critical("empty-road", boost::none, {road->id()});
        }
    });
}

VALIDATOR_CHECK_PART( rd_rd_el_relations, elements_relations, RD_EL, RD, RD_NM )
{
    auto viewRd = context->objects<RD>();

    context->objects<RD_EL>().visit(
            [&](const RoadElement* element)
    {
        if (context->canSkipValidation<RD_EL>(element->id())) {
            return;
        }

        const auto fow = element->fow();
        const auto& roads = element->parents();

        std::map<Road::Type, TId> roadTypes;

        for (TId roadId : roads) {
            if (!viewRd.loaded(roadId)) {
                context->fatal(
                    "road-not-loaded",
                    utils::geomForReport(element),
                    { element->id(), roadId });
                continue;
            }
            auto road = viewRd.byId(roadId);

            if (common::isIn(fow, {common::FOW::Roundabout, common::FOW::Ramp})
                  && utils::isNamed<RD_NM>(road, context)
                  && road->type() != Road::Type::NamedExit) {
                context->error(
                    "named-roundabout-or-exit",
                    utils::geomForReport(element),
                    { element->id(), roadId });
            }

            if (element->fc() == 8) {
                context->error(
                    "road-with-fc8-element",
                    utils::geomForReport(element),
                    { element->id(), roadId });
            }

            if (fow == common::FOW::AlternateRoad) {
                for (const NameDatum& officialNameDatum
                        : utils::officialNames<RD_NM>(road, context)) {
                    if (officialNameDatum.lang == LANG_RU
                            && officialNameDatum.name.find(
                                SECONDARY_ROAD_TAG) == std::string::npos) {
                        context->error(
                            "secondary-road-named-without-tag",
                            utils::geomForReport(element),
                            { element->id(), roadId });
                    }
                }
            }

            // There may be arbitrary number of routes
            if (road->type() != Road::Type::Route) {
                auto insertionResult =
                    roadTypes.insert({road->type(), roadId});
                if (!insertionResult.second) {
                    context->error(
                        "same-type-roads",
                        utils::geomForReport(element),
                        { element->id(),
                          insertionResult.first->second, roadId });
                }
            }
        }
    });
}

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