"""
Cucumber result to Jira Xray Test repository

"""
__version__ = "0.1.0"


import datetime

import cujirax.cucumber as cucumber
import cujirax.xray.import_results as result
import cujirax.xray.import_tests as test
from cujirax.cucumber import Element
from cujirax.jira import Jirakey, JiraX, Project


class CuJiraX:
    def __init__(self, jira_project: str, parent_testset_key: str = None) -> None:
        self.jira_project = jira_project
        self.jira = JiraX(jira_project)
        
        self.testexecution = None
        self.testexecution_name = None
        self.testexecution_desc = None
        self.parent_testset_key = parent_testset_key

    def set_testexcution(self, jira_key: str):
        self.testexecution = Jirakey(jira_key)

    def set_testexecution_name(self, testexecution_name: str):
        self.testexecution_name = testexecution_name

    def set_testexecution_desc(self, description: str):
        self.testexecution_desc = description


    def to_xray(
            self, 
            cucumber_json: str, 
            testplan_key: str = None, 
            import_result=True, 
            ignore_duplicate=True
        )-> dict:

        s1 = cucumber.Model.parse_file(cucumber_json)
        output = {}
        for f in s1.__root__:
            testset_name = self.testset_name or f.uri.split("/")[-1]
            testexecution_name = self.testexecution_name or datetime.date.today().strftime("%Y%m%d") + " :: " + testset_name

            ticket_ts, ticket_te = [
                self.testset or self.jira.create_testset(
                    summary=testset_name, 
                    description=self.testset_desc or f.description
                ),
                self.testexecution or self.jira.create_testexecution(
                    summary=testexecution_name, 
                    description=self.testexecution_desc or f.description
                ),
            ]

            # Import Test cases
            exists, new = self._split_elements_to_exist_and_new(
                elements=f.elements, 
                j=self.jira, 
                ignore_duplicate=ignore_duplicate
            )
            
            tests = [n for n in map(lambda x: self._update_description_if_exist(x,self.jira), exists)]
            output.update({
                'test_set': ticket_ts,
                'testset_name': testset_name,
                'parent_testset': self.parent_testset,
                'test_execution': ticket_te,
                'testexecution_name': testexecution_name,
                'test_plan': testplan_key,
                'tests': tests,
            })
            
            test_cases = [n for n in map(lambda x: self._new_testcase(x, self.jira_project, ticket_ts, self.parent_testset), new)]
            test.bulk_import(test_cases) if test_cases else None
            
            # Import Results
            if import_result:
                _tests = self._get_results(f.elements, self.jira, ignore_duplicate)
                req = result.RequestBody(
                    info=result.Info(
                        summary=testexecution_name,
                        description=self.testexecution_desc or f.description,
                        testPlanKey=str(testplan_key) if testplan_key else testplan_key
                    ),
                    tests= _tests,
                    testExecutionKey=str(ticket_te)
                )
                res = result.import_xray_json_results(req)
                output.update({
                    'import_status': res.status_code,
                    'import_response': res.json()
                })
        return output    
        
    
    def create_testplan(self, testplan_name:str, testplan_desc: str) -> Jirakey:
        """
        This function return Jira key, create new test plan when not found.

        It is idempotent, which means that call it multiple times with same input 
        produces the same result as call it once.
        """
        assert testplan_name, "Test plan name cannot be None"
        return self.jira.create_testplan(summary=testplan_name, description=testplan_desc)

    @classmethod
    def _get_results(cls, elements: Element, j: JiraX, ignore_duplicate):
        result_tests = []
        for el in elements:
            test_name = cls.scenarioid_to_tescasename(el.id, el.keyword)
            tests = j.get_tests(test_name)
            assert tests, "Test not created: {}".format(test_name)
            
            test_key = tests[0] if ignore_duplicate else None
            assert test_key, "Test key cannot be None"
            
            statuses = [step.result.status.value for step in el.steps]
            agg_result = "passed" if all(s == 'passed' for s in statuses) else "failed"
            result_tests.append(result.Test(testKey=str(test_key), status=agg_result))
        return result_tests

    @classmethod
    def _split_elements_to_exist_and_new(cls, elements: Element, j: JiraX, ignore_duplicate):
        found= []
        not_found = []
        
        for el in elements:
            test_name = cls.scenarioid_to_tescasename(el.id, el.keyword)
            tests = j.get_tests(test_name)
            if tests and not ignore_duplicate:
                assert len(tests) == 1, "Duplicate key detected: {}".format(tests)
            print("[searching...]", test_name, tests)
            found.append(el) if j.get_tests(test_name) else not_found.append(el)

        return found, not_found
    
    @classmethod
    def _new_testcase(
        cls, 
        element: Element, 
        project_key: str, 
        testset_key: Jirakey=None, 
        parent_testset_key: Jirakey = None
    ):
        step_definitions = [(f"{step.keyword} {step.name}", step.result.status.value) for step in element.steps]
        test_name = cls.scenarioid_to_tescasename(element.id, element.keyword)
        test_sets = [str(x) for x in [testset_key, parent_testset_key] if x] if parent_testset_key else []

        return test.CucumberTestCase(
            fields=test.Fields(
                summary=test_name, 
                project=Project(key=project_key),
                description="\n".join([s[0] for s in step_definitions])
            ),
            xray_test_sets=test_sets
        )
    
    @classmethod
    def _update_description_if_exist(cls, el: Element, j: JiraX)-> Jirakey:
        test_name = cls.scenarioid_to_tescasename(el.id, el.keyword)
        step_definitions = [(f"{step.keyword} {step.name}", step.result.status.value) for step in el.steps]

        jira_key = j.get_tests(test_name)
        if jira_key: 
            print("found test:", jira_key[0])
            
            # Update all Step Definition in the description
            j.jira.update_issue_field(jira_key[0], fields={"description": "\n".join([s[0] for s in step_definitions])})
            return jira_key[0]

    @staticmethod
    def scenarioid_to_tescasename(scenario_id: str, scenario_type: str):
        translation_table = str.maketrans('+-', '  ')

        if scenario_type == "Scenario Outline":
            featurename_testname, version = scenario_id.split(";;")
            feature_name, test_name = featurename_testname.split(";")
            feature_name = feature_name.translate(translation_table)
            test_name = test_name.translate(translation_table)

            return f"{feature_name} :: {test_name} {version}"
        else:
            feature_name, test_name = scenario_id.split(";")
            feature_name = feature_name.translate(translation_table)
            test_name = test_name.translate(translation_table)

            return f"{feature_name} :: {test_name}"
        