import xml.etree.ElementTree
import unittest

import pytest
from pytest_cases import parametrize, fixture, fixture_ref

from pymasep.common import Environment
from test.unit.mocks.game_mock import GameMock
from pymasep.utils import import_from_dotted_path, native_type_to_xml, native_type_to_xml_none, \
    native_type_from_xml, gen_dict_extract


class Test:
    pass


def test_import_from_dotted_path_class_created():
    to_import = 'unittest.TestCase'

    classdef = import_from_dotted_path(to_import)

    assert classdef == unittest.TestCase


def test_import_from_dotted_path_not_exists():
    to_import = 'unittest.runner.UnknownClass'

    try:
        _ = import_from_dotted_path(to_import)
        assert False
    except AttributeError:
        assert True


@fixture
def base_object():
    base_xml_str = '<BaseObject name="BO1" state="run" />'
    env = Environment()
    game = GameMock()
    env.game = game
    xml_node = xml.etree.ElementTree.fromstring(base_xml_str)
    base_object = env.create_object(xml_node=xml_node)
    yield {'obj': base_object}


@fixture
def base_object_xml():
    base_xml_str = '<BaseObject name="BO1" state="run" />'
    yield xml.etree.ElementTree.fromstring(base_xml_str)


@parametrize("value, expected_result",
             [(1, '<int>1</int>'),
              ('1', '<str>1</str>'),
              ([1, 2, 3], '<list>'
                          '<item><int>1</int></item>'
                          '<item><int>2</int></item><'
                          'item><int>3</int></item><'
                          '/list>'),
              ({}, '<dict />'),
              (Test, "<type>&lt;class 'test.unit.test_utils.Test'&gt;</type>"),
              ({'key1': 1, 'key2': '2'}, '<dict>'
                                         '<item key="key1"><int>1</int></item>'
                                         '<item key="key2"><str>2</str></item>'
                                         '</dict>'),
              (fixture_ref(base_object), '<dict>'
                                         '<item key="obj">'
                                         '<BaseObject name="BO1" state="run" />'
                                         '</item></dict>')])
def test_native_type_to_xml_value_convert(value, expected_result):
    result = native_type_to_xml(value)

    assert xml.etree.ElementTree.tostring(result, encoding='unicode') == expected_result


def test_native_type_to_xml_none_int():
    expected_result = '<int />'

    result = native_type_to_xml_none(int)

    assert xml.etree.ElementTree.tostring(result, encoding='unicode') == expected_result


@pytest.mark.parametrize("expected_value, expected_type, xml_input_str",
                         [(1, int, '<int>1</int>'),
                          ({}, dict, '<dict />'),
                          ([1, 2, 3], list, '<list>'
                                            '<item><int>1</int></item>'
                                            '<item><int>2</int></item>'
                                            '<item><int>3</int></item>'
                                            '</list>'),
                          ((1, 2), tuple, '<tuple>'
                                          '<item><int>1</int></item>'
                                          '<item><int>2</int></item>'
                                          '</tuple>'),
                          ({1, 2}, set, '<set>'
                                        '<item><int>1</int></item>'
                                        '<item><int>2</int></item>'
                                        '</set>'),
                          ({'key1': 1, 'key2': '2'}, dict, '<dict>'
                                                           '<item key="key1"><int>1</int></item>'
                                                           '<item key="key2"><str>2</str></item>'
                                                           '</dict>'),
                          ({'key1': None}, dict, '<dict><item key="key1"><int /></item></dict>'),
                          (Test, type, "<type>&lt;class 'test.unit.test_utils.Test'&gt;</type>")])
def test_native_type_from_xml_value_read(expected_value, expected_type, xml_input_str):
    xml_input = xml.etree.ElementTree.fromstring(xml_input_str)

    result, value_type = native_type_from_xml(xml_input)

    assert result == expected_value
    assert value_type == expected_type


def test_native_type_from_xml_int_none():
    expected_value = None
    expected_type = int
    xml_input_str = '<int />'
    xml_input = xml.etree.ElementTree.fromstring(xml_input_str)

    result, value_type = native_type_from_xml(xml_input)

    assert result == expected_value
    assert value_type == expected_type


def test_native_type_from_xml_pymas_object(base_object_xml):
    xml_node = base_object_xml
    expected_value = base_object_xml
    expected_type = xml.etree.ElementTree.Element
    env = Environment()
    game = GameMock()
    env.game = game

    result, value_type = native_type_from_xml(xml_node)

    assert result == expected_value
    assert value_type == expected_type


def test_native_type_from_xml_pymas_object_inside_native(base_object_xml):
    xml_input_str = '<dict><item key="obj"><BaseObject name="BO1" state="run" /></item></dict>'
    env = Environment()
    game = GameMock()
    env.game = game
    xml_input = xml.etree.ElementTree.fromstring(xml_input_str)
    expected_type = dict

    result, value_type = native_type_from_xml(xml_input)

    assert value_type == expected_type
    assert xml.etree.ElementTree.tostring(result['obj']) == xml.etree.ElementTree.tostring(base_object_xml)


def test_gen_dict_extract():
    d = {9: {0: 'A', 1: 'B'}}

    result = list(gen_dict_extract(0, d))

    assert result == ['A']
