from save_to_db.core.item import Item
from save_to_db.utils.test_base import TestBase


class TestItemClsManager(TestBase):
    """Contains tests for
    :py:class:`~save_to_db.core.item_cls_manager.ItemClsManager`.
    """

    def test_getting_items_by_path(self):
        # --- module `.m0.m1` ---
        m0_m1_module = "{}.m0.m1".format(TestItemClsManager.__module__)

        M1_ItemA = type("M1_ItemA", (Item,), {})
        M1_ItemA.__module__ = m0_m1_module

        M1_ItemB = type("M1_ItemB", (Item,), {})
        M1_ItemB.__module__ = m0_m1_module

        M1_ItemRepeated = type("ItemRepeated", (Item,), {})
        M1_ItemRepeated.__module__ = m0_m1_module

        # --- module `.m0.m2` ---
        m0_m2_module = "{}.m0.m2".format(TestItemClsManager.__module__)

        M2_ItemA = type("M2_ItemA", (Item,), {})
        M2_ItemA.__module__ = m0_m2_module

        M2_ItemB = type("M2_ItemB", (Item,), {})
        M2_ItemB.__module__ = m0_m2_module

        M2_ItemRepeated = type("ItemRepeated", (Item,), {})
        M2_ItemRepeated.__module__ = m0_m2_module

        # getting one item -----------------------------------------------------
        paths = (
            # absolute
            "{}.{}".format(M1_ItemA.__module__, M1_ItemA.__name__),
            # class name only
            M1_ItemA.__name__,
            # relative one dot
            ".m0.m1.M1_ItemA",
            # relative multiple dots
            "..test_item_cls_manager.m0.m1.M1_ItemA",
        )
        this_module = TestItemClsManager.__module__

        for path in paths:
            if not path.startswith("."):
                items = self.item_cls_manager.get_by_path(path)
            else:
                items = self.item_cls_manager.get_by_path(path, relative_to=this_module)

            # although module can contain multiple classes with the same name,
            # in this case we know that there are only one such class
            self.assertTrue(
                len(items) == 1, "One item must be returned: {}".format(path)
            )

            self.assertIn(
                M1_ItemA,
                items,
                "Item was not found in registry, path: {}".format(path),
            )

        # getting multiple items -----------------------------------------------
        path = "ItemRepeated"
        items = self.item_cls_manager.get_by_path(path)
        self.assertGreaterEqual(
            len(items), 2, "Not all items were returned by class name"
        )

        self.assertTrue(len(items) == 2, "Two items must be returned: {}".format(path))

        self.assertIn(M1_ItemRepeated, items, "Item was not found")
        self.assertIn(M2_ItemRepeated, items, "Item was not found")

    def test_item_autogeneration(self):
        self.item_cls_manager.autogenerate = True

        # --- auto generated item two ---
        class ItemGeneralOne_A(Item):
            model_cls = self.ModelGeneralOne

        item_one = ItemGeneralOne_A()
        # item two class will be auto generated
        item_one["two_1_1"](f_boolean=True)

        ItemGeneralTwo11 = ItemGeneralOne_A.relations["two_1_1"]["item_cls"]
        ItemGeneralTwoXX = ItemGeneralOne_A.relations["two_x_x"]["item_cls"]

        self.assertIs(ItemGeneralTwo11, ItemGeneralTwoXX)

        self.assertFalse(ItemGeneralOne_A.metadata["autogenerated_item_cls"])
        self.assertTrue(ItemGeneralTwo11.metadata["autogenerated_item_cls"])
        self.assertTrue(ItemGeneralTwoXX.metadata["autogenerated_item_cls"])

        # --- using existing  item two ---
        self.item_cls_manager.clear()

        class ItemGeneralOne_B(Item):
            model_cls = self.ModelGeneralOne

        class ItemGeneralTwo_B(Item):
            model_cls = self.ModelGeneralTwo

        item_one = ItemGeneralOne_B()
        # item two class will be auto generated
        item_one["two_1_1"](f_boolean=True)

        ItemGeneralTwo11 = ItemGeneralOne_B.relations["two_1_1"]["item_cls"]
        ItemGeneralTwoXX = ItemGeneralOne_B.relations["two_x_x"]["item_cls"]

        self.assertIs(ItemGeneralTwo11, ItemGeneralTwo_B)
        self.assertIs(ItemGeneralTwoXX, ItemGeneralTwo_B)

        self.assertFalse(ItemGeneralOne_B.metadata["autogenerated_item_cls"])
        self.assertFalse(ItemGeneralTwo11.metadata["autogenerated_item_cls"])
        self.assertFalse(ItemGeneralTwoXX.metadata["autogenerated_item_cls"])
