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`.
    """

    ModelGeneralOne = None
    ModelGeneralTwo = None


    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'])