import pytest
from assertpy import (
    assert_that,
    fail,
)
from cfgtools import (
    InvalidConfigXml,
    read,
    read_string,
)


def test_version_1_is_the_default():
    cfg = read_string("""<config/>""")
    assert_that(cfg.version).is_equal_to("1.0")

def test_version_is_correctly_parsed():
    cfg = read_string("""<config version="2.0"/>""")
    assert_that(cfg.version).is_equal_to("2.0")

def test_cannot_have_a_key_inside_config():
    try:
        read_string("""<config version="1.0"><key name="a"/></config>""")
        fail("Must raise InvalidConfigXml")
    except InvalidConfigXml:
        pass

def test_group_must_have_a_name():
    try:
        read_string("""<config version="1.0"><group/></config>""")
        fail("Must raise InvalidConfigXml")
    except InvalidConfigXml:
        pass

def test_group_is_correctly_parsed():
    cfg = read_string("""<config version="1.0"><group name="group"/></config>""")
    assert_that(cfg.groups).is_length(1)
    assert_that(cfg.groups[0].name).is_equal_to("group")
    assert_that(cfg.groups[0].descr).is_none()


def test_group_can_have_an_optional_descriptions():
    cfg = read_string("""<config version="1.0"><group name="group" descr="descr"/></config>""")
    assert_that(cfg.groups).is_length(1)
    assert_that(cfg.groups[0].name).is_equal_to("group")
    assert_that(cfg.groups[0].descr).is_equal_to("descr")


def test_groups_can_be_nested_recursively():
    cfg = read_string("""
<config version="1.0">
  <group name="group1">
    <group name="group2">
      <group name="group3"/>
    </group>
  </group>
</config>
""")
    assert_that(cfg.groups).is_length(1)
    assert_that(cfg.groups[0].name).is_equal_to("group1")
    assert_that(cfg.groups[0].groups).is_length(1)
    assert_that(cfg.groups[0].groups[0].name).is_equal_to("group2")
    assert_that(cfg.groups[0].groups[0].groups).is_length(1)
    assert_that(cfg.groups[0].groups[0].groups[0].name).is_equal_to("group3")


def test_cannot_have_config_inside_a_group():
    try:
        read_string("""<config version="1.0"><group name="group"><config/><group></config>""")
        fail("Must raise InvalidConfigXml")
    except InvalidConfigXml:
        pass


def test_key_must_contain_a_name():
    xml = """
<config version="1.0">
  <group name="group">
    <key/>
  </group>
</config>
"""
    try:
        read_string(xml)
        fail("Must raise InvalidConfigXml")
    except InvalidConfigXml:
        pass

def test_keys_can_have_an_optional_description():
    xml = """
<config version="1.0">
  <group name="group">
    <key name="key" descr="descr"/>
  </group>
</config>
"""

    cfg = read_string(xml)
    key = cfg.groups[0].keys[0]
    assert_that(key.name).is_equal_to("key")
    assert_that(key.descr).is_equal_to("descr")


def test_keys_contain_values():
    xml = """
<config version="1.0">
  <group name="group">
    <key name="key">
      <string>value</string>
    </key>
  </group>
</config>
"""

    cfg = read_string(xml)
    key = cfg.groups[0].keys[0]
    assert_that(key.values).is_length(1)
    assert_that(key.values[0]).is_equal_to("value")


def test_keys_values_can_be_arrays():
    xml = """
<config version="1.0">
  <group name="group">
    <key name="key">
      <array>
        <string>value1</string>
        <string>value2</string>
      </array>
    </key>
  </group>
</config>
"""

    cfg = read_string(xml)
    key = cfg.groups[0].keys[0]
    assert_that(key.values).is_length(2)
    assert_that(key.values[0]).is_equal_to("value1")
    assert_that(key.values[1]).is_equal_to("value2")


def test_keys_cannot_contain_groups():
    xml = """
<config version="1.0">
  <group name="group">
    <key name="key">
      <group name="group2"/>
    </key>
  </group>
</config>
"""

    try:
        read_string(xml)
        fail("Must raise InvalidConfigXml")
    except InvalidConfigXml:
        pass


def test_can_parse_multiple_groups():
    xml = """
<config version="1.0">
  <group name="group1">
    <key name="key1">
      <string>value1</string>
    </key>
  </group>
  <group name="group2">
    <key name="key2">
      <string>value2</string>
    </key>
  </group>
</config>
"""


    cfg = read_string(xml)
    assert_that(cfg.groups).is_length(2)
    assert_that(cfg.groups[0].name).is_equal_to("group1")
    assert_that(cfg.groups[0].keys).is_length(1)
    assert_that(cfg.groups[0].keys[0].name).is_equal_to("key1")
    assert_that(cfg.groups[0].keys[0].values).is_length(1)
    assert_that(cfg.groups[0].keys[0].values[0]).is_equal_to("value1")


@pytest.mark.parametrize(
    argnames="xml",
    argvalues=(
        '<something version="1.0"/>',
        '<config version="1.0"><something/></config>',
        '<config version="1.0"><group name="group"><something/></group></config>',
        """<config version="1.0">
             <group name="group">
               <key name="key">
                 <invalid>a</invalid>
               </key>
             </group>
            </config>""",
    ),
    ids=(
        "invalid root",
        "invalid config child",
        "invalid group child",
        "invalid key type"
    )
)
def test_tags_can_only_be_config_group_and_key(xml):
    try:
        read_string(xml)
        fail("Must raise InvalidConfigXml")
    except InvalidConfigXml:
        pass


def test_can_parse_complex_file():
    with open("loader.cfg", "r", encoding="utf-8") as f:
        read(f)
