from .samples import MyClass
from ...utils.decorators import class_logger, decorate_functions, CLASS_ATTR_LOGGER
from ...utils.functions import find_class
from ...utils.logging import get_logger_name
from ...utils.predicates import func_name_is_in
from django.test import SimpleTestCase
from typing import Tuple
import functools


def double_result(func):
    """ décorateur pour doubler les résultats """
    @functools.wraps(func)
    def inner(*args, **kwargs):
        return 2 * func(*args, **kwargs)
    return inner


class DecoratorTest(SimpleTestCase):

    def test_decorate_functions(self):
        num = 5
        a = 1
        b = 2
        res = a + b
        res_x2 = res * 2

        def given():
            msg = 'le résultat initial est bien celui attendu'
            self.assertEqual(MyClass.class_met(a, b), res, msg)
            self.assertEqual(MyClass.static_met(a, b), res, msg)
            self.assertEqual(MyClass(num).std_met(a, b), res, msg)
        given()

        # tout est décoré
        Type = decorate_functions(double_result, lambda func: True)(MyClass)
        self.assertEqual(Type.class_met(a, b), res_x2)
        self.assertEqual(Type.static_met(a, b), res_x2)
        self.assertEqual(Type(num).std_met(a, b), res_x2)

        # une seule méthode est décorée (1ère)
        Type = decorate_functions(double_result, func_name_is_in(MyClass.class_met.__name__,))(MyClass)
        self.assertEqual(Type.class_met(a, b), res_x2)
        self.assertEqual(Type.static_met(a, b), res)
        self.assertEqual(Type(num).std_met(a, b), res)

        # une seule méthode est décorée (2ème)
        Type = decorate_functions(double_result, func_name_is_in(MyClass.static_met.__name__,))(MyClass)
        self.assertEqual(Type.class_met(a, b), res)
        self.assertEqual(Type.static_met(a, b), res_x2)
        self.assertEqual(Type(num).std_met(a, b), res)

        # une seule méthode est décorée (3ème)
        Type = decorate_functions(double_result, func_name_is_in(MyClass.std_met.__name__,))(MyClass)
        self.assertEqual(Type.class_met(a, b), res)
        self.assertEqual(Type.static_met(a, b), res)
        self.assertEqual(Type(num).std_met(a, b), res_x2)

        # une seule méthode est décorée deux fois (3ème)
        Type = decorate_functions(double_result, func_name_is_in(MyClass.std_met.__name__,))(Type)
        self.assertEqual(Type.class_met(a, b), res)
        self.assertEqual(Type.static_met(a, b), res)
        self.assertEqual(Type(num).std_met(a, b), 2 * res_x2)

        # pas de changement au niveau de la classe d'origine
        self.assertEqual(MyClass.class_met(a, b), res)
        self.assertEqual(MyClass.static_met(a, b), res)
        self.assertEqual(MyClass(num).std_met(a, b), res)

    def test_class_logger(self):
        def given():
            self.assertEqual(hasattr(MyClass, CLASS_ATTR_LOGGER), False, "la classe initiale n'a pas de logger")
        given()

        Type = class_logger(MyClass)
        self.assertEqual(hasattr(Type, CLASS_ATTR_LOGGER), True)
        self.assertEqual(getattr(Type, CLASS_ATTR_LOGGER).name, get_logger_name(MyClass))

        # 2ème décoration
        Type = class_logger(Type)
        self.assertEqual(hasattr(Type, CLASS_ATTR_LOGGER), True)
        self.assertEqual(getattr(Type, CLASS_ATTR_LOGGER).name, get_logger_name(MyClass))

        # pas de changement au niveau de la classe d'origine
        self.assertEqual(hasattr(MyClass, CLASS_ATTR_LOGGER), False)
