from logging import Logger
from suds.xsd.doctor import Import, ImportDoctor
import suds
from SoapRequestor import SoapRequestor
from warnings import warn
from html2text import HTML2Text
from tlib.conftest import prj_config


class SpiraModerator(object):
    """
    Helper class to connect and manipulate the data in SpiraTest
    """

    client = None                        #: Soap client to connect to Spira Soap API
    my_soap = None
    _tests = None                        #: Holds retrieved tests from Automation test set in Spiratest
    _logger = None                #: logger to send loggin information to.  Logger comes from pytest test definitions

    _username = prj_config.get("spira", "username")
    _password = prj_config.get("spira", "password")

    def __init__(self, logger):
        """
        Constructor for class

        @param logger: instance of a logging object configured in testing project
        @type logger: Logger
        """
        self._logger = logger
        wsdl_file = "http://spirateam.ypg.com/Services/v4_0/ImportExport.svc?wsdl"
        schema_url = 'http://schemas.microsoft.com/2003/10/Serialization/Arrays'
        schema_import = Import(schema_url)
        schema_doctor = ImportDoctor(schema_import)

        self.my_soap = SoapRequestor(self._logger, wsdl_file, schema_doctor)

    def test_connect(self):
        """
        Authenticates client with SpiraTest.

        @return: if connection was successful
        @rtype: bool
        """
        warn("This method is Deprecated; you may want to update it use connect() instead.", DeprecationWarning)
        self.connect()

    def connect(self):
        """
        Authenticates client with SpiraTest.

        @return:if connection was successful
        @rtype: bool
        """

        result = False
        try:
            result = self.my_soap.client.service.Connection_Authenticate(self._username, self._password)
        except suds.WebFault, e:
            self._logger.error(str(e))

        self._logger.info("The connection to Spira returned :" + str(result))

        return result

    def update_test_case(self, project_id, test_case_id, test_start, test_end, test_status_id, runner_msg,
                         stack_trace, test_set_id):
        """
        Updates a specified test cases with a specified projects Automation test set with the results of the test execution

        @param project_id: Unique project id from within SpiraTest
        @type project_id: int
        @param test_case_id: Unique test case id from within SpiraTest
        @type test_case_id: int
        @param test_start: Time at which the test case started its execution. Doesn't seem to affect Spiratest.
        @type test_start: datetime
        @param test_end: Time at which the test case ended its execution. Doesn't seem to affect Spiratest.
        @type test_end: datetime
        @param runner_msg: Parameters that were used in test execution
        @type runner_msg: str
        @param stack_trace: Error message returned from failed test cases
        @type stack_trace: str

        """

        self.my_soap.client.service.TestRun_RecordAutomated2(self._username, self._password, project_id, None,
                                                             test_case_id, None, test_set_id, None, None, test_start,
                                                             test_end, test_status_id, "None", None, 1,
                                                             runner_msg, stack_trace, 1)

    def get_project_id(self, proj_name):
        """
        Gets the Unique prject id from spiratest for a given project name.  Name comes from project's config file

        @param proj_name: Name of project under test
        @type proj_name: str

        @return: project id
        @rtype: int
        """

        self.my_soap.client.service.Connection_Authenticate(self._username, self._password)
        project_list = self.my_soap.client.service.Project_Retrieve()

        project = (proj for proj in project_list[0] if proj["Name"] == proj_name).next()
        self.logger.info("Project Id returned is " + str(project.ProjectId))

        return project.ProjectId

    def get_tests_from_set(self, test_set_id, proj_id):
        """
        Fetches a list of test cases for a given test set id in SpiraTest and stores them in class var "tests"

        @param test_set_id: Automation test set id from Spiratest for project under test
        @type test_set_id: int
        @param proj_id: project id from Spiratest for project under test
        @type proj_id: int
        """

        self.my_soap.client.service.Connection_Authenticate(self._username, self._password)
        self.my_soap.client.service.Connection_ConnectToProject(proj_id)
        test_set = self.my_soap.client.service.TestCase_RetrieveByTestSetId(test_set_id)
        self._tests = test_set[0]

    def get_test_case_id(self, test_name):
        """
        Gets the unique test case id from SpiraTest for the test case name supplied

        @param test_name: Test case name found in SpiraTest and used in test case decorated to label
        automated test_support
        @type test_name: str

        @return: test case id
        @rtype: int
        """

        my_test = (test for test in self._tests if test["Name"] == test_name).next()

        return my_test.TestCaseId

    def get_test_steps(self,test_case_id):
        """
        Gets the Test Steps for a Test Case

        @param test_case_id: Test Case ID from the SpiraTest
        @type test_case_id: int

        @return: test steps including Test Description. Expected Results and Test Data
        @rtype: list
        """
        arr_test_steps = []

        test_steps = self.my_soap.client.service.TestCase_RetrieveById(test_case_id)

        #Calculates index of the last step in ArrayOfRemoteTestStep.Array of RemoteTestStep
        last_test_step = len(test_steps.TestSteps[0])

        for i in range(last_test_step):
            arr_test_steps.extend([test_steps.TestSteps[0][i].Description, test_steps.TestSteps[0][i].ExpectedResult,
                       test_steps.TestSteps[0][i].SampleData])

        return arr_test_steps

    def get_expected_result(self, test_case_id):
        """
        Gets the Expected Result of the last Test Step for a test case

        @param test_case_id: Test Case ID from the SpiraTest
        @type test_case_id: int

        @return: expected test step result in plain text
        @rtype: str
        """

        test_steps = self.get_test_steps(test_case_id)

        expected_result_of_last_step = test_steps[-2]

        # Converts Expected Result in HTML form to plain text
        handler = HTML2Text()
        expected_result = handler.handle(expected_result_of_last_step)

        return expected_result