"""Implement NUnit class that can be serialized as XML"""

from dataclasses import dataclass, field
from abc import ABC
from typing import Iterable, Optional

from lxml.etree import Element, ElementBase

@dataclass
class CaseFailure():
    """renders  failure, in case a test suite's result is failed."""
    root_element: ElementBase = Element("failure")
    message_element: ElementBase = Element("message")

    def __post_init__(self, message: str):
        self.message_element.text = message
        self.root_element.append(message)

@dataclass
class TestCase():
    """Base for test cases"""

    # id The unique id of this test. Coded as "mmm-nnn" where the part before the hyphen
    # represents the assembly and the part after it represents a test in that assembly.
    # Currently, mmm and nnn are ints, but that is merely an accident of the implementation
    # and should not be relied on.
    case_id: str
    # name The display name of the test as generated by NUnit or, in the case of some parameterized
    # tests, specified by the user.
    name: str
    # result The basic result of the test. May be Passed, Failed, Inconclusive or Skipped.
    result: str
    failure: Optional[CaseFailure] = None

    root_element: ElementBase = Element("test-case")

    @staticmethod
    def __validate_result(result: str) -> bool:
        """Return True, if the passed value is a valid result."""
        return result in ["Passed", "Failed", "Inconclusive", "Skipped"]

    def __post_init__(self):
        if not self.__validate_result(self.result):
            raise ValueError

        self.root_element = Element(
            "test-case", case_id=self.case_id, name=self.name, result=self.result)
        if not self.failure is None:
            self.failure = CaseFailure(self.failure)
            self.root_element.append(self.failure.root_element)

@dataclass
class CaseContainer(ABC):
    """Represents ny element in the test-run that can contain test cases."""
    root_element: ElementBase
    cases_contained: Iterable[TestCase] = field(default_factory=list)

    def add_case(self, case: TestCase):
        """Add a case to the data model as well as the XML."""
        self.cases_contained.append(case)
        self.root_element.append(case.root_element)

@dataclass
class SuiteBase(ABC):
    """Base class to put in SuiteContainer, and to implement TestSuite.
    This is needed as they need each other recursively."""
    root_element: ElementBase = Element("test-suite")

@dataclass
class SuiteContainer(ABC):
    """A class implementing this interface can contain a list of suites."""
    root_element: ElementBase
    suites_contained: Iterable[SuiteBase] = field(default_factory=list)

    def add_suite(self, suite: SuiteBase):
        """Add one more suite to the list"""
        self.suites_contained.append(suite)
        self.root_element.append(suite.root_element)

@dataclass
class TestSuite(SuiteBase, SuiteContainer, CaseContainer):
    """Create a base for all test-suites."""

@dataclass
class TestRun(SuiteContainer):
    """A test run is the result of an entire unit test."""
    root_element: ElementBase = Element("test-run")
