import pytest

from eunomia.backend import BackendDict
from eunomia.config import Group, Option
from eunomia.config import keys as K


# ========================================================================= #
# Test Config Objects                                                       #
# ========================================================================= #


def test_simple_option():
    bk = BackendDict()

    assert bk.dump_option(Option(data={'foo': 'bar'})) == {'__data__': {'foo': 'bar'}, '__defaults__': [], '__package__': '<group>', '__type__': 'option'}
    assert bk.dump_option(Option(data={'foo': 'bar'}, pkg=K.PKG_ROOT)) == {'__data__': {'foo': 'bar'}, '__defaults__': [], '__package__': '<root>', '__type__': 'option'}
    assert bk.dump_option(Option(data={'foo': 'bar'}, pkg=K.PKG_GROUP)) == {'__data__': {'foo': 'bar'}, '__defaults__': [], '__package__': '<group>', '__type__': 'option'}
    assert bk.dump_option(Option(data={'foo': 'bar'}, pkg=K.PKG_OPTION)) == {'__data__': {'foo': 'bar'}, '__defaults__': [], '__package__': '<option>', '__type__': 'option'}

    assert bk.dump_option(Option(data={'foo': 'bar'}, defaults=[])) == {'__data__': {'foo': 'bar'}, '__defaults__': [], '__package__': '<group>', '__type__': 'option'}

    # test relative path
    assert bk.dump_option(Option(data={'foo': 'bar'}, defaults=['group1/option1'])) == {'__data__': {'foo': 'bar'}, '__defaults__': ['group1/option1'], '__package__': '<group>', '__type__': 'option'}
    assert bk.dump_option(Option(data={'foo': 'bar'}, defaults=['group1/group2/option2'])) == {'__data__': {'foo': 'bar'}, '__defaults__': ['group1/group2/option2'], '__package__': '<group>', '__type__': 'option'}
    # test absolute path
    assert bk.dump_option(Option(data={'foo': 'bar'}, defaults=['/group1/group2/option2'])) == {'__data__': {'foo': 'bar'}, '__defaults__': ['/group1/group2/option2'], '__package__': '<group>', '__type__': 'option'}

    with pytest.raises(ValueError):  # TODO: fix error messages, match='is_identifier'):
        bk.dump_option(Option(data={'foo': 'bar'}, defaults=[{'group1': '1invalid'}]))
    with pytest.raises(ValueError):  # TODO: fix error messages, match='Wrong key'):
        bk.dump_option(Option(data={'foo': 'bar'}, defaults=[{'1invalid': 'option2'}]))
    with pytest.raises(ValueError):  # TODO: fix error messages, match='Wrong key'):
        bk.dump_option(Option(data={'foo': 'bar'}, defaults=[{'group1/2invalid': 'option2'}]))

    assert bk.dump_option(Option(data={'foo': 'bar'}, pkg='key1')) == {'__data__': {'foo': 'bar'}, '__defaults__': [], '__package__': 'key1', '__type__': 'option'}
    assert bk.dump_option(Option(data={'foo': 'bar'}, pkg='key1.key2')) == {'__data__': {'foo': 'bar'}, '__defaults__': [], '__package__': 'key1.key2', '__type__': 'option'}


def test_simple_config():
    g = Group({
        'subgroup': Group({
            'suboption2': Option({'bar': 2}),
            'suboption1': Option({'bar': 1}),
        }),
        'subgroup2': Group({
            'sub2option1': Option({'baz': 1}),
            'sub2option2': Option({'baz': 2}),
        }),
        'default': Option({
            'foo': 1
        }),
    })

    bck = BackendDict()

    # forward and backwards conversion!
    d1 = BackendDict().dump(g)
    g1 = BackendDict().load_group(d1)
    assert repr(g1) == repr(g)


def _make_config_group(suboption='suboption1', suboption2=None, package1='<group>', package2='<group>', check_cycle=False) -> Group:
    g = Group({
        'subgroup': Group({
            'suboption1': Option({'bar': 1}, pkg=package1),
            'suboption2': Option({'bar': 2}, pkg=package1),
        }),
        'subgroup2': Group({
            'subgroup3': Group({
                'suboption1': Option({'baz': 1}, pkg=package2),
                'suboption2': Option({'baz': 2}, pkg=package2),
            }),
        }),
        'default': Option(
            data={'foo': 1},
            defaults=[
                *([{'subgroup': suboption}] if suboption else []),  # relative
                *([{'/subgroup2/subgroup3': suboption2}] if suboption2 else []),  # absolute
            ],
        ),
    })

    if check_cycle:
        # forward and backwards conversion!
        d1 = BackendDict().dump(g)
        g1 = BackendDict().load_group(d1)
        assert repr(g1) == repr(g)

    return g


def test_config_objects():
    _make_config_group(suboption='suboption1', check_cycle=True)
    _make_config_group(suboption='suboption2', check_cycle=True)
    _make_config_group(suboption='invalid___', check_cycle=True)  # invalid but not an error


def test_option_init():
    # raw data in option
    data = {'foo': 'bar'}
    defaults = ['/group1/group2/option2']
    pkg = K.PKG_ROOT
    # make equivalent options
    option = Option(data=data, defaults=defaults, pkg=pkg)
    # check circular conversion
    bk = BackendDict()
    assert bk.dump(option) == bk.dump(bk.load_option(bk.dump(option)))


# ========================================================================= #
# END                                                                       #
# ========================================================================= #
