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

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

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

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

using categories::ADDR;
using categories::ADDR_NM;
using categories::AD;
using categories::AD_NM;
using categories::RD;

VALIDATOR_SIMPLE_CHECK( addr_relations, ADDR, ADDR_NM, AD, AD_NM, RD )
{
    auto viewAd = context->objects<AD>();
    auto viewRd = context->objects<RD>();

    context->objects<ADDR>().visit([&](const AddressPoint* addressPoint)
    {
        switch (addressPoint->associatedObject().type) {
            case AddressPoint::AssociatedObject::Type::NotSet: {
                context->fatal("stray-address-point",
                    addressPoint->geom(),
                    { addressPoint->id() });
            }
            break;

            case AddressPoint::AssociatedObject::Type::AdmUnit: {
                auto admUnitId = addressPoint->associatedObject().id;
                if (!viewAd.loaded(admUnitId)) {
                    context->fatal(
                        "adm-unit-not-loaded",
                        addressPoint->geom(),
                        { addressPoint->id(), admUnitId });
                    break;
                }
                const AdmUnit* admUnit = viewAd.byId(admUnitId);

                if (!utils::isNamed<AD_NM>(admUnit, context)) {
                    context->error(
                        "bound-to-unnamed-ad",
                        addressPoint->geom(),
                        { addressPoint->id(), admUnitId });
                }

                if (admUnit->levelKind() == AdmUnit::LevelKind::Other) {
                    context->fatal(
                        "bound-to-ad-level-kind-other",
                        addressPoint->geom(),
                        { addressPoint->id(), admUnitId });
                }
            }
            break;

            case AddressPoint::AssociatedObject::Type::Road: {
                auto roadId = addressPoint->associatedObject().id;
                if (!viewRd.loaded(roadId)) {
                    context->fatal(
                        "road-not-loaded",
                        addressPoint->geom(),
                        { addressPoint->id(), roadId });
                    break;
                }
                const Road* road = viewRd.byId(roadId);

                if (common::isIn(road->type(),
                            { Road::Type::Route, Road::Type::CountryHighway })) {
                    context->error(
                        "addr-bound-to-wrong-type-road",
                        addressPoint->geom(),
                        { addressPoint->id(), roadId });
                }

                const int SEARCH_CLASS_IGNORE = 10;
                if (road->searchClass() == SEARCH_CLASS_IGNORE) {
                    context->critical(
                        "addr-bound-to-road-search-class-ignore",
                        addressPoint->geom(),
                        { addressPoint->id(), roadId });
                }
            }
            break;

            case AddressPoint::AssociatedObject::Type::Feature: {
                // Skip checking as we don't support features yet
            }
            break;
        }
    });
}

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