#include "module.h"

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

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

#include <algorithm>

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

namespace {

// as per MAPSPRO-831
const std::vector<std::string> COUNTRY_MANDATORY_NAME_PARTS = { "государство" };
const std::vector<std::string> PROVINCE_MANDATORY_NAME_PARTS = {
    "автономная область",
    "автономный округ",
    "автономная республика",
    "город республиканского значения",
    "город федерального значения",
    "край",
    "область",
    "регион",
    "республика",
    "Республика",
    "федеральный округ",
};
const std::vector<std::string> AREA_MANDATORY_NAME_PARTS = {
    "административный округ",
    "волость",
    "городской акимат",
    "городской округ",
    "городское поселение",
    "городской совет",
    "горсовет",
    "город",
    "кожуун",
    "муниципалитет",
    "муниципальное образование",
    "муниципальный округ",
    "муниципальный район",
    "поселок",
    "посёлок",
    "район",
    "регион",
    "сельский округ",
    "сельское поселение",
    "сельский совет",
    "сельсовет",
    "улус",
};
const std::vector<std::string> LOCALITY_DISTRICT_MANDATORY_NAME_PARTS = {
    "аал",
    "аул",
    "агрогородок",
    "административный округ",
    "арбан",
    "вахтовый поселок",
    "вахтовый посёлок",
    "выселок",
    "город",
    "городок",
    "городской поселок",
    "городской посёлок",
    "деревня",
    "железнодорожная будка",
    "железнодорожная казарма",
    "железнодорожная платформа",
    "жилая зона",
    "жилой район",
    "жилой комплекс",
    "жилмассив",
    "жилой массив",
    "заимка",
    "зимник",
    "казарма",
    "квартал",
    "кишлак",
    "комплекс",
    "кордон",
    "кутан",
    "массив",
    "местечко",
    "местность",
    "микрорайон",
    "мыза",
    "населенный пункт",
    "населённый пункт",
    "округ",
    "планировочный район",
    "погост",
    "полярная станция",
    "поселение",
    "поселок",
    "посёлок",
    "поселок городского типа",
    "посёлок городского типа",
    "поселок сельского типа",
    "посёлок сельского типа",
    "поселок при разъезде",
    "посёлок при разъезде",
    "поселок при станции",
    "посёлок при станции",
    "поселок железнодорожного разъезда",
    "посёлок железнодорожного разъезда",
    "поселок железнодорожной станции",
    "посёлок железнодорожной станции",
    "поселок разъезда",
    "посёлок разъезда",
    "поселок станции",
    "посёлок станции",
    "починок",
    "промзона",
    "промышленная зона",
    "промышленный массив",
    "рабочий поселок",
    "рабочий посёлок",
    "район",
    "разъезд",
    "садовое товарищество",
    "село",
    "слобода",
    "станица",
    "станция",
    "сторона",
    "территория",
    "улус",
    "хутор",
    "дачно-строительный кооператив",
    "дачное товарищество",
    "дачное хозяйство",
    "дачные участки",
    "дачный комплекс",
    "дачный кооператив",
    "дачный поселок",
    "дачный посёлок",
    "дом отдыха",
    "жилой комплекс",
    "комплекс",
    "коттеджный поселок",
    "коттеджный посёлок",
    "личное подсобное хозяйство",
    "садоводческий массив",
    "садовое товарищество",
    "садовые участки",
    "СНТ",
    "СОТ",
    "СДТ",
    "ДНТ",
    "ДНП",
    "ПСК",
    "НСО",
    "санаторий",
    "территория",
    "слободка",
    "селище",
    "урочище",
    "коллективный сад",
};

icu::RegexPattern prepareRegex(const std::vector<std::string>& nameParts)
{
    bool first = true;
    std::string regexStr =  "\\b(";
    for (const std::string& part : nameParts) {
        if (first) {
            regexStr.append(part);
            first = false;
        } else {
            regexStr.append("|" + part);
        }
    }
    regexStr.append(")\\b");

    return utils::compilePattern(regexStr);
}

const std::map<AdmUnit::LevelKind, icu::RegexPattern>
MANDATORY_NAME_PARTS_BY_LEVEL_KIND = {
    { AdmUnit::LevelKind::Country, prepareRegex(COUNTRY_MANDATORY_NAME_PARTS) },
    { AdmUnit::LevelKind::Province, prepareRegex(PROVINCE_MANDATORY_NAME_PARTS) },
    { AdmUnit::LevelKind::Area, prepareRegex(AREA_MANDATORY_NAME_PARTS) },
    { AdmUnit::LevelKind::Locality,
      prepareRegex(LOCALITY_DISTRICT_MANDATORY_NAME_PARTS) },
    { AdmUnit::LevelKind::District,
      prepareRegex( LOCALITY_DISTRICT_MANDATORY_NAME_PARTS) },
};

} // namespace

using categories::AD;
using categories::AD_SUBST;
using categories::AD_NM;

namespace {

template <class AdmUnitT>
void runNamingCheck(CheckContext* context)
{
    context->objects<AdmUnitT>().visit([&](const typename AdmUnitT::TObject* admUnit) {
        utils::runBasicNamesCheck<AD_NM>(admUnit, context, Severity::Fatal);

        for (const NameDatum& officialNameDatum
                : utils::officialNames<AD_NM>(admUnit, context)) {
            if (officialNameDatum.lang != LANG_RU ||
                    utils::isNameEmpty(officialNameDatum.name)) {
                return;
            }

            auto mandatoryNameParts =
                MANDATORY_NAME_PARTS_BY_LEVEL_KIND.find(admUnit->levelKind());
            if (mandatoryNameParts == MANDATORY_NAME_PARTS_BY_LEVEL_KIND.end()) {
                continue;
            }
            if (!utils::matchesPattern(
                    officialNameDatum.name,
                    mandatoryNameParts->second)) {
                context->error(
                        "mandatory-name-part-not-found",
                        boost::none, { admUnit->id() });
            }
        }
    });
}

}  // namespace

VALIDATOR_SIMPLE_CHECK( ad_naming, AD, AD_SUBST, AD_NM )
{
    runNamingCheck<AD>(context);
    runNamingCheck<AD_SUBST>(context);
}

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