import pathlib
from unittest import BaseTestSuite

class QTCGTestDiscovery:
    def __init__(self, processor, suite_dir: pathlib.Path):
        self._processor = processor
        self._document_builder = None
        self._test_sets = None
        self.suite_dir = suite_dir

    @property
    def processor(self):
        return self._processor

    @property
    def document_builder(self):
        if not(self._document_builder):
            self._document_builder = self.processor.new_document_builder()
        return self._document_builder

    def parse_xml(self, path: pathlib.Path):
        return self.document_builder.parse_xml(xml_file_name=str(path))

    def evaluate_xpath(self, context, xpath: str):
        xpc = self.processor.new_xpath_processor()
        xpc.set_context(xdm_item = context)
        return xpc.evaluate(xpath)

class TestSetDiscovery(QTCGTestDiscovery):
    def __init__(self, processor, suite_dir: pathlib.Path, test_set_path: pathlib.Path, name: str):
        super().__init__(processor, suite_dir)
        self._suite = None
        self.test_set_path = test_set_path
        self.name = name

    @property
    def suite(self):
        if not(self._suite):
            self._suite = BaseTestSuite()
        return self._suite

    def discover(self):
        if not(self.test_set_path.exists()):
            raise FileNotFoundError(f"Test set file <{self.test_set_path}> does not exist")

        test_set = self.parse_xml(self.test_set_path)
        test_case_nodes = self.evaluate_xpath(test_set, "//*:test-case")

        for test_case_node in test_case_nodes:
            self.add_test_case(test_case_node)

        return self.suite

    def add_test_case(self, test_case_node):
        pass

def process_test_set(doc_builderi: saxonc.PyDocumentBuilder, proci: saxonc.PySaxonProcessor, test_set_file: pathlib.Path):
    global total_tests
    global localEnvironments
    global not_run
    global test_report
    global test_case_found

    localEnvironments.clear()
    if test_set_file.exists():

        test_set_xml = doc_builderi.parse_xml(xml_file_name=str(test_set_file))

        if test_set_xml is not None:
            create_local_environments(test_set_xml, xpathProcForEnv)
            test_set_nodes = test_set_xml.axis_nodes(3)
            child0 = None
            for x in test_set_nodes:
                if x.node_kind == 1:
                    child0 = x
                    break
            xpathProcForEnv.set_context(xdm_item=child0)
            envs = xpathProcForEnv.evaluate("//environment[@name]")

            dependencies = xpathProcForEnv.evaluate("//test-set/dependency")

            xpathProcForEnv.set_context(xdm_item=test_set_xml)
            test_cases = xpathProcForEnv.evaluate("//*:test-case")
            tcs_count = 0
            if test_cases is not None:
                tcs_count = test_cases.size
                total_tests += tcs_count

            if dependencies is not None:

                for ii in range(dependencies.size):
                    dep = dependencies.item_at(ii).get_node_value()
                    if not ensure_dependency_satisfied(dep, localEnvironments["default"]):
                        print("dependency failed at test set level")
                        test_report.start_test_set_element(child0, "dependency not satisfied")
                        not_run = not_run + tcs_count
                        return

            if envs is not None:
                for i in range(envs.size):
                    environment = Environment()
                    if dependencies is not None:

                        for ii in range(dependencies.size):
                            dep = dependencies.item_at(ii).get_node_value()
                            if not ensure_dependency_satisfied(dep, environment):
                                print("dependency failed at test set level XXXX")
                                return
                    env = envs.item_at(i)
                    process_environment(xpathProcForEnv, env.get_node_value(), spec, localEnvironments,
                                        environment)
            if child0 is None:
                print("Cannot find child node of test " + test_set_file)

                return None
            else:
                test_set_name = child0.get_attribute_value("name")
                '''if run_test is not None and test_set_name is not run_test:
                    return None'''
                ''' get environment'''

                '''print(test_set_file)
                    print("Test cases count = " + str(tcs_count))'''
                result = "NA"
                test_report.start_test_set_element(child0, None)

                for x, test_case in enumerate(test_cases):
                    if test_case.is_node:
                        test_casei = test_case.get_node_value()
                        test_case_name = test_casei.get_attribute_value("name")
                        if (test_case_name_pattern is None or test_case_name_pattern.fullmatch(test_case_name)) and test_set_name not in exceptionList:
                            print("-s:" + test_set_name + " -t:" + test_case_name)
                            run_test_case(test_case, xpathProcForEnv)
                    else:
                        print("Error test_case is not a node")

        else:
            print("Cannot find test " + test_set_file)

class TestSuiteDiscovery(QTCGTestDiscovery):
    def __init__(self, processor, suite_dir: pathlib.Path, catalog: pathlib.Path):
        super().__init__(processor, suite_dir)
        self._suite = None
        self.catalog = catalog

    @property
    def test_sets(self):
        if not(self._test_sets):
            catalog_node = self.parse_xml(self.catalog)
            self._test_sets = [n.get_node_value() for n in self.evaluate_xpath(catalog_node, "//*:test-set")]
        return self._test_sets

    @property
    def suite(self):
        if not(self._suite):
            self._suite = BaseTestSuite()
        return self._suite

    def discover(self):
        if not(self.catalog.exists()):
            raise Exception(f"Test suite catalog <{self.catalog}> does not exist")

        for test_set_node in self.test_sets:
            if test_set_node is not None:
                self.add_test_set(test_set_node)

        return self.suite

    def add_test_set(self, test_set_node):
        test_set_name = test_set_node.get_attribute_value("name")
        test_set_file = self.suite_dir / test_set_node.get_attribute_value("file")

        discoverer = TestSetDiscovery(self.processor, self.suite_dir, test_set_file, test_set_name)
        self.suite.addTest(discoverer.discover())


