from dectate.app import App, directive
from dectate.config import commit, Action, Composite
from dectate.error import ConflictError, ConfigError

import pytest


def test_simple():
    class MyDirective(Action):
        config = {"my": list}

        def __init__(self, message):
            self.message = message

        def identifier(self, my):
            return self.message

        def perform(self, obj, my):
            my.append((self.message, obj))

    class MyApp(App):
        foo = directive(MyDirective)

    @MyApp.foo("hello")
    def f():
        pass

    commit(MyApp)

    assert MyApp.config.my == [("hello", f)]


def test_decorator():
    class MyApp(App):
        @directive
        class foo(Action):
            config = {"my": list}

            def __init__(self, message):
                self.message = message

            def identifier(self, my):
                return self.message

            def perform(self, obj, my):
                my.append((self.message, obj))

    @MyApp.foo("hello")
    def f():
        pass

    commit(MyApp)

    assert MyApp.config.my == [("hello", f)]


def test_commit_method():
    class MyDirective(Action):
        config = {"my": list}

        def __init__(self, message):
            self.message = message

        def identifier(self, my):
            return self.message

        def perform(self, obj, my):
            my.append((self.message, obj))

    class MyApp(App):
        foo = directive(MyDirective)

    @MyApp.foo("hello")
    def f():
        pass

    result = MyApp.commit()

    assert MyApp.config.my == [("hello", f)]
    assert list(result) == [MyApp]


def test_directive_name():
    class MyDirective(Action):
        config = {"my": list}

        def __init__(self, message):
            self.message = message

        def identifier(self, my):
            return self.message

        def perform(self, obj, my):
            my.append(self)

    class MyApp(App):
        foo = directive(MyDirective)

    @MyApp.foo("hello")
    def f():
        pass

    MyApp.commit()

    MyApp.config.my[0].directive.directive_name == "foo"


def test_conflict_same_directive():
    class MyDirective(Action):
        config = {"my": list}

        def __init__(self, message):
            self.message = message

        def identifier(self, my):
            return self.message

        def perform(self, obj, my):
            my.append((self.message, obj))

    class MyApp(App):
        foo = directive(MyDirective)

    @MyApp.foo("hello")
    def f():
        pass

    @MyApp.foo("hello")
    def f2():
        pass

    with pytest.raises(ConflictError):
        commit(MyApp)


def test_app_inherit():
    class Registry:
        pass

    class MyDirective(Action):
        config = {"my": Registry}

        def __init__(self, message):
            self.message = message

        def identifier(self, my):
            return self.message

        def perform(self, obj, my):
            my.message = self.message
            my.obj = obj

    class MyApp(App):
        foo = directive(MyDirective)

    class SubApp(MyApp):
        pass

    @MyApp.foo("hello")
    def f():
        pass

    commit(MyApp, SubApp)

    assert MyApp.config.my.message == "hello"
    assert MyApp.config.my.obj is f
    assert SubApp.config.my.message == "hello"
    assert SubApp.config.my.obj is f


def test_app_override():
    class Registry:
        pass

    class MyDirective(Action):
        config = {"my": Registry}

        def __init__(self, message):
            self.message = message

        def identifier(self, my):
            return self.message

        def perform(self, obj, my):
            my.message = self.message
            my.obj = obj

    class MyApp(App):
        foo = directive(MyDirective)

    class SubApp(MyApp):
        pass

    @MyApp.foo("hello")
    def f():
        pass

    @SubApp.foo("hello")
    def f2():
        pass

    commit(MyApp, SubApp)

    assert MyApp.config.my.message == "hello"
    assert MyApp.config.my.obj is f
    assert SubApp.config.my.message == "hello"
    assert SubApp.config.my.obj is f2


def test_different_group_no_conflict():
    class FooDirective(Action):
        config = {"foo": list}

        def __init__(self, message):
            self.message = message

        def identifier(self, foo):
            return self.message

        def perform(self, obj, foo):
            foo.append((self.message, obj))

    class BarDirective(Action):
        config = {"bar": list}

        def __init__(self, message):
            self.message = message

        def identifier(self, bar):
            return self.message

        def perform(self, obj, bar):
            bar.append((self.message, obj))

    class MyApp(App):
        foo = directive(FooDirective)
        bar = directive(BarDirective)

    @MyApp.foo("hello")