"""
Looks through data object to double-check it makes sense. Will fail for missing or duplicate definitions or
duplicate claims and give warnings for unused and unignored locations or warps.
"""
import logging
from typing import List


_IGNORABLE_LOCATIONS = frozenset({
    "HIDDEN_ITEM_TRICK_HOUSE_NUGGET",  # Is permanently mssiable and has special behavior that sets the flag early

    # Duplicate rival fights. All variations are represented by the Brandon + Mudkip version
    "TRAINER_BRENDAN_ROUTE_103_TREECKO_REWARD",
    "TRAINER_BRENDAN_ROUTE_103_TORCHIC_REWARD",
    "TRAINER_MAY_ROUTE_103_MUDKIP_REWARD",
    "TRAINER_MAY_ROUTE_103_TREECKO_REWARD",
    "TRAINER_MAY_ROUTE_103_TORCHIC_REWARD",
    "TRAINER_BRENDAN_ROUTE_110_TREECKO_REWARD",
    "TRAINER_BRENDAN_ROUTE_110_TORCHIC_REWARD",
    "TRAINER_MAY_ROUTE_110_MUDKIP_REWARD",
    "TRAINER_MAY_ROUTE_110_TREECKO_REWARD",
    "TRAINER_MAY_ROUTE_110_TORCHIC_REWARD",
    "TRAINER_BRENDAN_ROUTE_119_TREECKO_REWARD",
    "TRAINER_BRENDAN_ROUTE_119_TORCHIC_REWARD",
    "TRAINER_MAY_ROUTE_119_MUDKIP_REWARD",
    "TRAINER_MAY_ROUTE_119_TREECKO_REWARD",
    "TRAINER_MAY_ROUTE_119_TORCHIC_REWARD",
    "TRAINER_BRENDAN_RUSTBORO_TREECKO_REWARD",
    "TRAINER_BRENDAN_RUSTBORO_TORCHIC_REWARD",
    "TRAINER_MAY_RUSTBORO_MUDKIP_REWARD",
    "TRAINER_MAY_RUSTBORO_TREECKO_REWARD",
    "TRAINER_MAY_RUSTBORO_TORCHIC_REWARD",
    "TRAINER_BRENDAN_LILYCOVE_TREECKO_REWARD",
    "TRAINER_BRENDAN_LILYCOVE_TORCHIC_REWARD",
    "TRAINER_MAY_LILYCOVE_MUDKIP_REWARD",
    "TRAINER_MAY_LILYCOVE_TREECKO_REWARD",
    "TRAINER_MAY_LILYCOVE_TORCHIC_REWARD",
})

_IGNORABLE_WARPS = frozenset({
    # Trick House
    "MAP_ROUTE110_TRICK_HOUSE_PUZZLE2:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!",
    "MAP_ROUTE110_TRICK_HOUSE_PUZZLE2:2/MAP_ROUTE110_TRICK_HOUSE_END:0!",
    "MAP_ROUTE110_TRICK_HOUSE_PUZZLE3:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!",
    "MAP_ROUTE110_TRICK_HOUSE_PUZZLE3:2/MAP_ROUTE110_TRICK_HOUSE_END:0!",
    "MAP_ROUTE110_TRICK_HOUSE_PUZZLE4:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!",
    "MAP_ROUTE110_TRICK_HOUSE_PUZZLE4:2/MAP_ROUTE110_TRICK_HOUSE_END:0!",
    "MAP_ROUTE110_TRICK_HOUSE_PUZZLE5:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!",
    "MAP_ROUTE110_TRICK_HOUSE_PUZZLE5:2/MAP_ROUTE110_TRICK_HOUSE_END:0!",
    "MAP_ROUTE110_TRICK_HOUSE_PUZZLE6:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!",
    "MAP_ROUTE110_TRICK_HOUSE_PUZZLE6:2/MAP_ROUTE110_TRICK_HOUSE_END:0!",
    "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!",
    "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:10/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:9",
    "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:11/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:12",
    "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:12/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:11",
    "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:2/MAP_ROUTE110_TRICK_HOUSE_END:0!",
    "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:3/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:4",
    "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:4/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:3",
    "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:5/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:6",
    "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:6/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:5",
    "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:7/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:8",
    "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:8/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:7",
    "MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:9/MAP_ROUTE110_TRICK_HOUSE_PUZZLE7:10",
    "MAP_ROUTE110_TRICK_HOUSE_PUZZLE8:0,1/MAP_ROUTE110_TRICK_HOUSE_ENTRANCE:2!",
    "MAP_ROUTE110_TRICK_HOUSE_PUZZLE8:2/MAP_ROUTE110_TRICK_HOUSE_END:0!",

    # Department store elevator
    "MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0,1/MAP_DYNAMIC:-1!",
    "MAP_LILYCOVE_CITY_DEPARTMENT_STORE_1F:3/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0!",
    "MAP_LILYCOVE_CITY_DEPARTMENT_STORE_2F:2/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0!",
    "MAP_LILYCOVE_CITY_DEPARTMENT_STORE_3F:2/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0!",
    "MAP_LILYCOVE_CITY_DEPARTMENT_STORE_4F:2/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0!",
    "MAP_LILYCOVE_CITY_DEPARTMENT_STORE_5F:1/MAP_LILYCOVE_CITY_DEPARTMENT_STORE_ELEVATOR:0!",

    # Intro truck
    "MAP_INSIDE_OF_TRUCK:0,1,2/MAP_DYNAMIC:-1!",

    # Battle Frontier
    "MAP_BATTLE_FRONTIER_BATTLE_DOME_CORRIDOR:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1!",
    "MAP_BATTLE_FRONTIER_BATTLE_DOME_PRE_BATTLE_ROOM:0,1/MAP_BATTLE_FRONTIER_OUTSIDE_WEST:1!",
    "MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM:0,1/MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:2",
    "MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:0,1/MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:2",
    "MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:2/MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM:0",
    "MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:3/MAP_BATTLE_FRONTIER_BATTLE_PALACE_BATTLE_ROOM:0!",
    "MAP_BATTLE_FRONTIER_BATTLE_PALACE_LOBBY:2/MAP_BATTLE_FRONTIER_BATTLE_PALACE_CORRIDOR:0",

    # Terra Cave and Marine Cave
    "MAP_TERRA_CAVE_ENTRANCE:0/MAP_DYNAMIC:-1!",
    "MAP_ROUTE113:1/MAP_TERRA_CAVE_ENTRANCE:0!",
    "MAP_ROUTE113:2/MAP_TERRA_CAVE_ENTRANCE:0!",
    "MAP_ROUTE114:3/MAP_TERRA_CAVE_ENTRANCE:0!",
    "MAP_ROUTE114:4/MAP_TERRA_CAVE_ENTRANCE:0!",
    "MAP_ROUTE115:1/MAP_TERRA_CAVE_ENTRANCE:0!",
    "MAP_ROUTE115:2/MAP_TERRA_CAVE_ENTRANCE:0!",
    "MAP_ROUTE116:3/MAP_TERRA_CAVE_ENTRANCE:0!",
    "MAP_ROUTE116:4/MAP_TERRA_CAVE_ENTRANCE:0!",
    "MAP_ROUTE118:0/MAP_TERRA_CAVE_ENTRANCE:0!",
    "MAP_ROUTE118:1/MAP_TERRA_CAVE_ENTRANCE:0!",

    "MAP_UNDERWATER_MARINE_CAVE:0/MAP_DYNAMIC:-1!",
    "MAP_UNDERWATER_ROUTE105:0/MAP_UNDERWATER_MARINE_CAVE:0!",
    "MAP_UNDERWATER_ROUTE105:1/MAP_UNDERWATER_MARINE_CAVE:0!",
    "MAP_UNDERWATER_ROUTE125:0/MAP_UNDERWATER_MARINE_CAVE:0!",
    "MAP_UNDERWATER_ROUTE125:1/MAP_UNDERWATER_MARINE_CAVE:0!",
    "MAP_UNDERWATER_ROUTE127:0/MAP_UNDERWATER_MARINE_CAVE:0!",
    "MAP_UNDERWATER_ROUTE127:1/MAP_UNDERWATER_MARINE_CAVE:0!",
    "MAP_UNDERWATER_ROUTE129:0/MAP_UNDERWATER_MARINE_CAVE:0!",
    "MAP_UNDERWATER_ROUTE129:1/MAP_UNDERWATER_MARINE_CAVE:0!",

    # Altering Cave
    "MAP_ALTERING_CAVE:0/MAP_ROUTE103:0",
    "MAP_ROUTE103:0/MAP_ALTERING_CAVE:0",

    # Event islands
    "MAP_BIRTH_ISLAND_EXTERIOR:0/MAP_BIRTH_ISLAND_HARBOR:0",
    "MAP_BIRTH_ISLAND_HARBOR:0/MAP_BIRTH_ISLAND_EXTERIOR:0",

    "MAP_FARAWAY_ISLAND_ENTRANCE:0,1/MAP_FARAWAY_ISLAND_INTERIOR:0,1",
    "MAP_FARAWAY_ISLAND_INTERIOR:0,1/MAP_FARAWAY_ISLAND_ENTRANCE:0,1",

    "MAP_SOUTHERN_ISLAND_EXTERIOR:0,1/MAP_SOUTHERN_ISLAND_INTERIOR:0,1",
    "MAP_SOUTHERN_ISLAND_INTERIOR:0,1/MAP_SOUTHERN_ISLAND_EXTERIOR:0,1",

    "MAP_NAVEL_ROCK_B1F:0/MAP_NAVEL_ROCK_ENTRANCE:0",
    "MAP_NAVEL_ROCK_B1F:1/MAP_NAVEL_ROCK_FORK:1",
    "MAP_NAVEL_ROCK_BOTTOM:0/MAP_NAVEL_ROCK_DOWN11:0",
    "MAP_NAVEL_ROCK_DOWN01:0/MAP_NAVEL_ROCK_FORK:2",
    "MAP_NAVEL_ROCK_DOWN01:1/MAP_NAVEL_ROCK_DOWN02:0",
    "MAP_NAVEL_ROCK_DOWN02:0/MAP_NAVEL_ROCK_DOWN01:1",
    "MAP_NAVEL_ROCK_DOWN02:1/MAP_NAVEL_ROCK_DOWN03:0",
    "MAP_NAVEL_ROCK_DOWN03:0/MAP_NAVEL_ROCK_DOWN02:1",
    "MAP_NAVEL_ROCK_DOWN03:1/MAP_NAVEL_ROCK_DOWN04:0",
    "MAP_NAVEL_ROCK_DOWN04:0/MAP_NAVEL_ROCK_DOWN03:1",
    "MAP_NAVEL_ROCK_DOWN04:1/MAP_NAVEL_ROCK_DOWN05:0",
    "MAP_NAVEL_ROCK_DOWN05:0/MAP_NAVEL_ROCK_DOWN04:1",
    "MAP_NAVEL_ROCK_DOWN05:1/MAP_NAVEL_ROCK_DOWN06:0",
    "MAP_NAVEL_ROCK_DOWN06:0/MAP_NAVEL_ROCK_DOWN05:1",
    "MAP_NAVEL_ROCK_DOWN06:1/MAP_NAVEL_ROCK_DOWN07:0",
    "MAP_NAVEL_ROCK_DOWN07:0/MAP_NAVEL_ROCK_DOWN06:1",
    "MAP_NAVEL_ROCK_DOWN07:1/MAP_NAVEL_ROCK_DOWN08:0",
    "MAP_NAVEL_ROCK_DOWN08:0/MAP_NAVEL_ROCK_DOWN07:1",
    "MAP_NAVEL_ROCK_DOWN08:1/MAP_NAVEL_ROCK_DOWN09:0",
    "MAP_NAVEL_ROCK_DOWN09:0/MAP_NAVEL_ROCK_DOWN08:1",
    "MAP_NAVEL_ROCK_DOWN09:1/MAP_NAVEL_ROCK_DOWN10:0",
    "MAP_NAVEL_ROCK_DOWN10:0/MAP_NAVEL_ROCK_DOWN09:1",
    "MAP_NAVEL_ROCK_DOWN10:1/MAP_NAVEL_ROCK_DOWN11:1",
    "MAP_NAVEL_ROCK_DOWN11:0/MAP_NAVEL_ROCK_BOTTOM:0",
    "MAP_NAVEL_ROCK_DOWN11:1/MAP_NAVEL_ROCK_DOWN10:1",
    "MAP_NAVEL_ROCK_ENTRANCE:0/MAP_NAVEL_ROCK_B1F:0",
    "MAP_NAVEL_ROCK_ENTRANCE:1/MAP_NAVEL_ROCK_EXTERIOR:1",
    "MAP_NAVEL_ROCK_EXTERIOR:0/MAP_NAVEL_ROCK_HARBOR:0",
    "MAP_NAVEL_ROCK_EXTERIOR:1/MAP_NAVEL_ROCK_ENTRANCE:1",
    "MAP_NAVEL_ROCK_FORK:0/MAP_NAVEL_ROCK_UP1:0",
    "MAP_NAVEL_ROCK_FORK:1/MAP_NAVEL_ROCK_B1F:1",
    "MAP_NAVEL_ROCK_FORK:2/MAP_NAVEL_ROCK_DOWN01:0",
    "MAP_NAVEL_ROCK_HARBOR:0/MAP_NAVEL_ROCK_EXTERIOR:0",
    "MAP_NAVEL_ROCK_TOP:0/MAP_NAVEL_ROCK_UP4:1",
    "MAP_NAVEL_ROCK_UP1:0/MAP_NAVEL_ROCK_FORK:0",
    "MAP_NAVEL_ROCK_UP1:1/MAP_NAVEL_ROCK_UP2:0",
    "MAP_NAVEL_ROCK_UP2:0/MAP_NAVEL_ROCK_UP1:1",
    "MAP_NAVEL_ROCK_UP2:1/MAP_NAVEL_ROCK_UP3:0",
    "MAP_NAVEL_ROCK_UP3:0/MAP_NAVEL_ROCK_UP2:1",
    "MAP_NAVEL_ROCK_UP3:1/MAP_NAVEL_ROCK_UP4:0",
    "MAP_NAVEL_ROCK_UP4:0/MAP_NAVEL_ROCK_UP3:1",
    "MAP_NAVEL_ROCK_UP4:1/MAP_NAVEL_ROCK_TOP:0",

    # Secret bases
    "MAP_SECRET_BASE_BROWN_CAVE1:0/MAP_DYNAMIC:-2!",
    "MAP_SECRET_BASE_BROWN_CAVE2:0/MAP_DYNAMIC:-2!",
    "MAP_SECRET_BASE_BROWN_CAVE3:0/MAP_DYNAMIC:-2!",
    "MAP_SECRET_BASE_BROWN_CAVE4:0/MAP_DYNAMIC:-2!",
    "MAP_SECRET_BASE_BLUE_CAVE1:0/MAP_DYNAMIC:-2!",
    "MAP_SECRET_BASE_BLUE_CAVE2:0/MAP_DYNAMIC:-2!",
    "MAP_SECRET_BASE_BLUE_CAVE3:0/MAP_DYNAMIC:-2!",
    "MAP_SECRET_BASE_BLUE_CAVE4:0/MAP_DYNAMIC:-2!",
    "MAP_SECRET_BASE_YELLOW_CAVE1:0/MAP_DYNAMIC:-2!",
    "MAP_SECRET_BASE_YELLOW_CAVE2:0/MAP_DYNAMIC:-2!",
    "MAP_SECRET_BASE_YELLOW_CAVE3:0/MAP_DYNAMIC:-2!",
    "MAP_SECRET_BASE_YELLOW_CAVE4:0/MAP_DYNAMIC:-2!",
    "MAP_SECRET_BASE_RED_CAVE1:0/MAP_DYNAMIC:-2!",
    "MAP_SECRET_BASE_RED_CAVE2:0/MAP_DYNAMIC:-2!",
    "MAP_SECRET_BASE_RED_CAVE3:0/MAP_DYNAMIC:-2!",
    "MAP_SECRET_BASE_RED_CAVE4:0/MAP_DYNAMIC:-2!",
    "MAP_SECRET_BASE_SHRUB1:0/MAP_DYNAMIC:-2!",
    "MAP_SECRET_BASE_SHRUB2:0/MAP_DYNAMIC:-2!",
    "MAP_SECRET_BASE_SHRUB3:0/MAP_DYNAMIC:-2!",
    "MAP_SECRET_BASE_SHRUB4:0/MAP_DYNAMIC:-2!",
    "MAP_SECRET_BASE_TREE1:0/MAP_DYNAMIC:-2!",
    "MAP_SECRET_BASE_TREE2:0/MAP_DYNAMIC:-2!",
    "MAP_SECRET_BASE_TREE3:0/MAP_DYNAMIC:-2!",
    "MAP_SECRET_BASE_TREE4:0/MAP_DYNAMIC:-2!",

    # Multiplayer rooms
    "MAP_RECORD_CORNER:0,1,2,3/MAP_DYNAMIC:-1!",

    "MAP_UNION_ROOM:0,1/MAP_DYNAMIC:-1!",
    "MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!",
    "MAP_MAUVILLE_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!",
    "MAP_PETALBURG_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!",
    "MAP_EVER_GRANDE_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!",
    "MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_2F:1/MAP_UNION_ROOM:0!",
    "MAP_DEWFORD_TOWN_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!",
    "MAP_MOSSDEEP_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!",
    "MAP_OLDALE_TOWN_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!",
    "MAP_SLATEPORT_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!",
    "MAP_RUSTBORO_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!",
    "MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!",
    "MAP_LILYCOVE_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!",
    "MAP_FORTREE_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!",
    "MAP_FALLARBOR_TOWN_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!",
    "MAP_LAVARIDGE_TOWN_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!",
    "MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!",
    "MAP_VERDANTURF_TOWN_POKEMON_CENTER_2F:1/MAP_UNION_ROOM:0!",

    "MAP_TRADE_CENTER:0,1/MAP_DYNAMIC:-1!",
    "MAP_PACIFIDLOG_TOWN_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!",
    "MAP_MAUVILLE_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!",
    "MAP_PETALBURG_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!",
    "MAP_EVER_GRANDE_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!",
    "MAP_EVER_GRANDE_CITY_POKEMON_LEAGUE_2F:2/MAP_TRADE_CENTER:0!",
    "MAP_DEWFORD_TOWN_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!",
    "MAP_MOSSDEEP_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!",
    "MAP_OLDALE_TOWN_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!",
    "MAP_SLATEPORT_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!",
    "MAP_RUSTBORO_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!",
    "MAP_BATTLE_FRONTIER_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!",
    "MAP_LILYCOVE_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!",
    "MAP_FORTREE_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!",
    "MAP_FALLARBOR_TOWN_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!",
    "MAP_LAVARIDGE_TOWN_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!",
    "MAP_SOOTOPOLIS_CITY_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!",
    "MAP_VERDANTURF_TOWN_POKEMON_CENTER_2F:2/MAP_TRADE_CENTER:0!",

    "MAP_BATTLE_COLOSSEUM_2P:0,1/MAP_DYNAMIC:-1!",
    "MAP_BATTLE_COLOSSEUM_4P:0,1,2,3/MAP_DYNAMIC:-1!",

    # Unused content
    "MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP1:0/MAP_CAVE_OF_ORIGIN_1F:1!",
    "MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP1:1/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:0",
    "MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:0/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP1:1",
    "MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:1/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP3:0",
    "MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP3:0/MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP2:1",
    "MAP_CAVE_OF_ORIGIN_UNUSED_RUBY_SAPPHIRE_MAP3:1/MAP_CAVE_OF_ORIGIN_B1F:0!",
    "MAP_LILYCOVE_CITY_UNUSED_MART:0,1/MAP_LILYCOVE_CITY:0!",
})


def validate_group_maps() -> bool:
    from .data import data
    from .groups import _LOCATION_GROUP_MAPS

    failed = False

    for group_name, map_set in _LOCATION_GROUP_MAPS.items():
        for map_name in map_set:
            if map_name not in data.maps:
                failed = True
                logging.error("Pokemon Emerald: Map named %s in location group %s does not exist", map_name, group_name)

    return not failed


def validate_regions() -> bool:
    """
    Verifies that Emerald's data doesn't have duplicate or missing
    regions/warps/locations. Meant to catch problems during development like
    forgetting to add a new location or incorrectly splitting a region.
    """
    from .data import load_json_data, data

    extracted_data_json = load_json_data("extracted_data.json")
    error_messages: List[str] = []
    warn_messages: List[str] = []
    failed = False

    def error(message: str) -> None:
        nonlocal failed
        failed = True
        error_messages.append(message)

    def warn(message: str) -> None:
        warn_messages.append(message)

    # Check regions
    for name, region in data.regions.items():
        for region_exit in region.exits:
            if region_exit not in data.regions:
                error(f"Pokemon Emerald: Region [{region_exit}] referenced by [{name}] was not defined")

    # Check warps
    for warp_source, warp_dest in data.warp_map.items():
        if warp_source in _IGNORABLE_WARPS:
            continue

        if warp_dest is None:
            error(f"Pokemon Emerald: Warp [{warp_source}] has no destination")
        elif not data.warps[warp_dest].connects_to(data.warps[warp_source]) and not data.warps[warp_source].is_one_way:
            error(f"Pokemon Emerald: Warp [{warp_source}] appears to be a one-way warp but was not marked as one")

    # Check locations
    claimed_locations = [location for region in data.regions.values() for location in region.locations]
    claimed_locations_set = set()
    for location_name in claimed_locations:
        if location_name in claimed_locations_set:
            error(f"Pokemon Emerald: Location [{location_name}] was claimed by multiple regions")
        claimed_locations_set.add(location_name)

    for location_name in extracted_data_json["locations"]:
        if location_name not in claimed_locations and location_name not in _IGNORABLE_LOCATIONS:
            warn(f"Pokemon Emerald: Location [{location_name}] was not claimed by any region")

    warn_messages.sort()
    error_messages.sort()

    for message in warn_messages:
        logging.warning(message)
    for message in error_messages:
        logging.error(message)

    logging.debug("Pokemon Emerald sanity check done. Found %s errors and %s warnings.", len(error_messages), len(warn_messages))

    return not failed
