"""
    Tests for _LeObject and LeObject
"""

import unittest
from unittest import TestCase
from unittest.mock import patch

import EditorialModel
import DataSource.dummy
import leapi
import leapi.test.utils
from leapi.leobject import _LeObject

## Testing methods that need the generated code
class LeObjectTestCase(TestCase):

    @classmethod
    def setUpClass(cls):
        """ Write the generated code in a temporary directory and import it """
        cls.tmpdir = leapi.test.utils.tmp_load_factory_code()

    @classmethod
    def tearDownClass(cls):
        """ Remove the temporary directory created at class setup """
        leapi.test.utils.cleanup(cls.tmpdir)

    def test_uid2leobj(self):
        """ Testing _Leobject.uid2leobj() """
        import dyncode
        from dyncode import LeType, LeClass
        for i in dyncode.LeObject._me_uid.keys():
            cls = dyncode.LeObject.uid2leobj(i)
            if LeType in cls.__bases__:
                self.assertEqual(i, cls._type_id)
            elif LeClass in cls.__bases__:
                self.assertEqual(i, cls._class_id)
            else:
                self.fail("Bad value returned : '%s'"%cls)
        i=10
        while i in dyncode.LeObject._me_uid.keys():
            i+=1
        with self.assertRaises(KeyError):
            dyncode.LeObject.uid2leobj(i)
    
    @unittest.skip("Obsolete but may be usefull for datasources tests")
    def test_prepare_targets(self):
        """ Testing _prepare_targets() method """
        from dyncode import Publication, Numero, LeObject

        test_v = {
            (None, None) : (None, None),

            (Publication, Numero): (Publication, Numero),
            (Publication, None): (Publication, None),
            (None, Numero): (Publication, Numero),

            (Publication,'Numero'): (Publication, Numero),
            ('Publication', Numero): (Publication, Numero),

            ('Publication', 'Numero'): (Publication, Numero),
            ('Publication', None): (Publication, None),
            (None, 'Numero'): (Publication, Numero),
        }

        for (leclass, letype), (rleclass, rletype) in test_v.items():
            self.assertEqual((rletype,rleclass), LeObject._prepare_targets(letype, leclass))

    @unittest.skip("Obsolete but may be usefull for datasources tests")
    def test_invalid_prepare_targets(self):
        """ Testing _prepare_targets() method with invalid arguments """
        from dyncode import Publication, Numero, LeObject, Personnes
        
        test_v = [
            ('',''),
            (Personnes, Numero),
            (leapi.leclass.LeClass, Numero),
            (Publication, leapi.letype.LeType),
            ('foobar', Numero),
            (Publication, 'foobar'),
            (Numero, Numero),
            (Publication, Publication),
            (None, Publication),
            ('foobar', 'foobar'),
            (42,1337),
            (type, Numero),
            (LeObject, Numero),
            (LeObject, LeObject),
            (Publication, LeObject),
        ]

        for (leclass, letype) in test_v:
            with self.assertRaises(ValueError):
                LeObject._prepare_targets(letype, leclass)

class LeObjectMockDatasourceTestCase(TestCase):
    """ Testing _LeObject using a mock on the datasource """

    @classmethod
    def setUpClass(cls):
        """ Write the generated code in a temporary directory and import it """
        cls.tmpdir = leapi.test.utils.tmp_load_factory_code()
    @classmethod
    def tearDownClass(cls):
        """ Remove the temporary directory created at class setup """
        leapi.test.utils.cleanup(cls.tmpdir)
    
    @patch('DataSource.dummy.leapidatasource.DummyDatasource.insert')
    def test_insert(self, dsmock):
        from dyncode import Publication, Numero, LeObject
        ndatas = [
            {'titre' : 'FooBar'},
            {'titre':'hello'},
            {'titre':'world'},
        ]
        for ndats in ndatas:
            Publication.insert(ndats, classname='Numero')
            dsmock.assert_called_once_with(Numero, **ndats)
            dsmock.reset_mock()

            LeObject.insert(ndats, classname = 'Numero')
            dsmock.assert_called_once_with(Numero, **ndats)
            dsmock.reset_mock()

    @unittest.skip("Wait FieldTypes modification (cf. #90) and classmethod capabilities for update")
    @patch('DataSource.dummy.leapidatasource.DummyDatasource.update')
    def test_update(self, dsmock):
        from dyncode import Publication, Numero, LeObject

        args = [
            (   ['lodel_id = 1'],
                {'titre':'foobar'},
                [('lodel_id','=','1')],
                []
            ),
            (   ['superior.parent in [1,2,3,4,5,6]', 'titre != "FooBar"'],
                {'titre':'FooBar'},
                [( 'titre','!=','"FooBar"')],
                [( (leapi.leobject.REL_SUP, 'parent') ,' in ', '[1,2,3,4,5,6]')]
            ),
        ]

        for filters, datas, ds_filters, ds_relfilters in args:
            LeObject.update(Numero, filters, datas)
            dsmock.assert_called_once_with(Numero, Publication, ds_filters, ds_relfilters, datas)
            dsmock.reset_mock()

            LeObject.update('Numero', filters, datas)
            dsmock.assert_called_once_with(Numero, Publication, ds_filters, ds_relfilters, datas)
            dsmock.reset_mock()
    
    @patch('DataSource.dummy.leapidatasource.DummyDatasource.delete')
    def test_delete(self, dsmock):
        from dyncode import Publication, Numero, LeObject, LeType

        args = [
            (
                'Numero',
                ['lodel_id=1'],
                [('lodel_id', '=', '1'), ('type_id', '=', Numero._type_id)],
                []
            ),
            (
                'Publication',
                ['subordinate.parent not in [1,2,3]', 'titre = "titre nul"'],
                [('titre','=', '"titre nul"'), ('class_id', '=', Publication._class_id)],
                [( (leapi.leobject.REL_SUB, 'parent'), ' not in ', '[1,2,3]')]
            ),
        ]

        for classname, filters, ds_filters, ds_relfilters in args:
            ccls = LeObject.name2class(classname)

            LeObject.delete(filters, classname)
            dsmock.assert_called_once_with(ccls, ds_filters, ds_relfilters)
            dsmock.reset_mock()
            
            if not (LeType in ccls.__bases__): #tests for calls with a LeClass child
                ccls.delete(filters)
                dsmock.assert_called_once_with(ccls, ds_filters, ds_relfilters)
                dsmock.reset_mock()