import unittest

from worlds.AutoWorld import AutoWorldRegister, call_all
from . import setup_solo_multiworld


class TestBase(unittest.TestCase):
    def test_create_item(self):
        """Test that a world can successfully create all items in its datapackage"""
        for game_name, world_type in AutoWorldRegister.world_types.items():
            proxy_world = world_type(None, 0)  # this is identical to MultiServer.py creating worlds
            for item_name in world_type.item_name_to_id:
                with self.subTest("Create Item", item_name=item_name, game_name=game_name):
                    item = proxy_world.create_item(item_name)
                    self.assertEqual(item.name, item_name)

    def test_item_name_group_has_valid_item(self):
        """Test that all item name groups contain valid items. """
        # This cannot test for Event names that you may have declared for logic, only sendable Items.
        # In such a case, you can add your entries to this Exclusion dict. Game Name -> Group Names
        exclusion_dict = {
            "A Link to the Past":
                {"Pendants", "Crystals"},
            "Ocarina of Time":
                {"medallions", "stones", "rewards", "logic_bottles"},
            "Starcraft 2 Wings of Liberty":
                {"Missions"},
        }
        for game_name, world_type in AutoWorldRegister.world_types.items():
            with self.subTest(game_name, game_name=game_name):
                exclusions = exclusion_dict.get(game_name, frozenset())
                for group_name, items in world_type.item_name_groups.items():
                    if group_name not in exclusions:
                        with self.subTest(group_name, group_name=group_name):
                            for item in items:
                                self.assertIn(item, world_type.item_name_to_id)

    def test_item_name_group_conflict(self):
        """Test that all item name groups aren't also item names."""
        for game_name, world_type in AutoWorldRegister.world_types.items():
            with self.subTest(game_name, game_name=game_name):
                for group_name in world_type.item_name_groups:
                    with self.subTest(group_name, group_name=group_name):
                        self.assertNotIn(group_name, world_type.item_name_to_id)

    def test_item_count_greater_equal_locations(self):
        """Test that by the pre_fill step under default settings, each game submits items >= locations"""
        for game_name, world_type in AutoWorldRegister.world_types.items():
            with self.subTest("Game", game=game_name):
                multiworld = setup_solo_multiworld(world_type)
                self.assertGreaterEqual(
                    len(multiworld.itempool),
                    len(multiworld.get_unfilled_locations()),
                    f"{game_name} Item count MUST meet or exceed the number of locations",
                )

    def test_items_in_datapackage(self):
        """Test that any created items in the itempool are in the datapackage"""
        for game_name, world_type in AutoWorldRegister.world_types.items():
            with self.subTest("Game", game=game_name):
                multiworld = setup_solo_multiworld(world_type)
                for item in multiworld.itempool:
                    self.assertIn(item.name, world_type.item_name_to_id)

    def test_item_descriptions_have_valid_names(self):
        """Ensure all item descriptions match an item name or item group name"""
        for game_name, world_type in AutoWorldRegister.world_types.items():
            valid_names = world_type.item_names.union(world_type.item_name_groups)
            for name in world_type.item_descriptions:
                with self.subTest("Name should be valid", game=game_name, item=name):
                    self.assertIn(name, valid_names,
                                  "All item descriptions must match defined item names")

    def test_itempool_not_modified(self):
        """Test that worlds don't modify the itempool after `create_items`"""
        gen_steps = ("generate_early", "create_regions", "create_items")
        additional_steps = ("set_rules", "generate_basic", "pre_fill")
        excluded_games = ("Links Awakening DX", "Ocarina of Time", "SMZ3")
        worlds_to_test = {game: world
                          for game, world in AutoWorldRegister.world_types.items() if game not in excluded_games}
        for game_name, world_type in worlds_to_test.items():
            with self.subTest("Game", game=game_name):
                multiworld = setup_solo_multiworld(world_type, gen_steps)
                created_items = multiworld.itempool.copy()
                for step in additional_steps:
                    with self.subTest("step", step=step):
                        call_all(multiworld, step)
                        self.assertEqual(created_items, multiworld.itempool,
                                         f"{game_name} modified the itempool during {step}")
