from save_to_db.core import signals

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



class TestSignals(TestBase):
    
    ModelGeneralOne = None
    ModelGeneralTwo = None
    
    def test_db_persist_signals(self):
        
        class ItemGeneralOne(Item):
            model_cls = self.ModelGeneralOne
            creators = [{'f_integer'}]
            getters = [{'f_integer'}]
        
        class ItemGeneralTwo(Item):
            model_cls = self.ModelGeneralTwo
            creators = [{'f_integer'}]
            getters = [{'f_integer'}]
        
        persister = self.persister
        
        signal_calls = {
            'before_one': 0,
            'before_two': 0,
            'after_one': 0,
            'after_two': 0,
        }
        
        item_one = ItemGeneralOne(f_integer=1)
        item_one['two_x_x'](f_integer=1)
        persister.persist(item_one)
        self.assertEqual(signal_calls, {'before_one': 0,
                                        'before_two': 0,
                                        'after_one': 0,
                                        'after_two': 0,
                                        })
        
        @signals.before_db_persist.register
        def before_db_persist_signal_one(item, item_structure):
            nonlocal expected_item, expected_item_structure
            self.assertIs(item, expected_item)
            self.assertEqual(item_structure, expected_item_structure)
            self.assertIsInstance(item_structure, dict)
            signal_calls['before_one'] += 1
        
        expected_item = item_one
        expected_item_structure = item_one.process()
        persister.persist(item_one)
        self.assertEqual(signal_calls, {'before_one': 1,
                                        'before_two': 0,
                                        'after_one': 0,
                                        'after_two': 0,
                                        })
        
        def before_db_persist_signal_two(item, item_structure):
            nonlocal expected_item, expected_item_structure
            self.assertIs(item, expected_item)
            self.assertEqual(item_structure, expected_item_structure)
            self.assertIsInstance(item_structure, dict)
            signal_calls['before_two'] += 1
        signals.before_db_persist.register(before_db_persist_signal_two)
        
        persister.persist(item_one)
        self.assertEqual(signal_calls, {'before_one': 2,
                                        'before_two': 1,
                                        'after_one': 0,
                                        'after_two': 0,
                                        })
        
        @signals.after_db_persist.register
        def after_db_persist_signal_one(top_item, items, models):
            nonlocal expected_item, signal_items, signal_models
            self.assertIs(top_item, expected_item)
            signal_items, signal_models = items, models
            signal_calls['after_one'] += 1
        
        signal_items, signal_models = None, None
        returned_items, returned_models = persister.persist(item_one)
        self.assertIs(returned_items, signal_items)
        self.assertIs(returned_models, signal_models)
        self.assertEqual(signal_calls, {'before_one': 3,
                                        'before_two': 2,
                                        'after_one': 1,
                                        'after_two': 0,
                                        })
        
        def after_db_persist_signal_two(top_item, items, models):
            nonlocal expected_item, signal_items, signal_models
            self.assertIs(top_item, expected_item)
            signal_items, signal_models = items, models
            signal_calls['after_two'] += 1
        signals.after_db_persist.register(after_db_persist_signal_two)
            
        returned_items, returned_models = persister.persist(item_one)
        self.assertIs(returned_items, signal_items)
        self.assertIs(returned_models, signal_models)
        self.assertEqual(signal_calls, {'before_one': 4,
                                        'before_two': 3,
                                        'after_one': 2,
                                        'after_two': 1,
                                        })
        
        # unregister
        signals.before_db_persist.unregister(before_db_persist_signal_one)
        signals.after_db_persist.unregister(after_db_persist_signal_one)
        
        returned_items, returned_models = persister.persist(item_one)
        self.assertIs(returned_items, signal_items)
        self.assertIs(returned_models, signal_models)
        self.assertEqual(signal_calls, {'before_one': 4,
                                        'before_two': 4,
                                        'after_one': 2,
                                        'after_two': 2,
                                        })
            
    def test_item_dropped_signal(self):
        
        class ItemGeneralOne(Item):
            model_cls = self.ModelGeneralOne
            creators = [{'f_integer'}]
            getters = [{'f_integer'}]
        
        class ItemGeneralTwo(Item):
            model_cls = self.ModelGeneralTwo
            creators = [{'f_integer'}]
            getters = [{'f_integer'}]
        
        persister = self.persister
        
        
        item_one = ItemGeneralOne(f_integer='10')
        # dropped because cannot be created
        item_two_dropped = item_one['two_x_x'].gen(f_string='str-20')
        # update only mode
        item_one_dropped = item_two_dropped['one_x_1'](f_integer='30')
        item_one_dropped.update_only_mode = True
        # get only mode
        item_one_dropped_2 = item_two_dropped['one_x_x'].gen(f_integer='40')
        item_one_dropped_2.get_only_mode = True
        
        items_to_reasons = [
            [
                item_two_dropped,
                signals.item_dropped.reason_cannot_create_not_enough_data,
            ],
            [
                item_one_dropped,
                signals.item_dropped.reason_cannot_create_update_only_mode,
            ],
            [
                item_one_dropped_2,
                signals.item_dropped.reason_cannot_create_get_only_mode,
            ],
        ]
        
        @signals.item_dropped.register
        def item_dropped_listener(item, reason):
            nonlocal items_to_reasons
            
            for entry in items_to_reasons:
                if entry[0] is item:
                    try:
                        self.assertEqual(entry[1], reason, item)
                    except:
                        print(reason, entry[0].update_only_mode, entry[0]['f_integer'])
                        raise
                    items_to_reasons.remove(entry)
                    break
            else:
                raise Exception('Entry not found: [{}, {}]'.format(
                    item, reason))
            
        
        items, model_list = persister.persist(item_one)
        self.assertEqual(len(items), 1)
        self.assertEqual(len(model_list), 1)
        self.assertEqual(len(model_list[0]), 1)
        self.assertIs(items[0], item_one)
        
        self.assertEqual(len(items_to_reasons), 0)
        
        item_ones = self.get_all_models(self.ModelGeneralOne)
        self.assertEqual(len(item_ones), 1)
        item_twos = self.get_all_models(self.ModelGeneralTwo)
        self.assertEqual(len(item_twos), 0)
        
        
            
            
            
            
            
        