"""Module for a asteex static result."""

import inspect

from asteex.abstract.result import Result


class StaticResult(Result):
    """Class for a static test result."""

    def __init__(
        self,
        instance,
        status,
        reason="",
        case=None,
        context=None,
        exception_information=(None, None, None),
    ):  # pylint: disable=too-many-arguments,too-many-positional-arguments
        self.__instance = instance
        self.__status = str(status)
        self.__reason = str(reason)
        self.__case = case

        if context is not None:
            context = _validate_context(context)
        self.__context = context

        self.__exception_information = _validate_exception_information(exception_information)

    def get_instance(self):
        """Get the executed test instance.

        :returns: executed :class:`Instance <asteex.abstract.instance.Instance>`.
        """
        return self.__instance

    def get_status(self):
        """Get the status of the test execution.

        :returns: string status.
        """
        return self.__status

    def get_reason(self):
        """Get the reason for the status.

        :returns: string reason.
        """
        return self.__reason

    def get_case(self):
        """Get the executed case.

        :returns: executed :class:`Case <asteex.abstract.case.Case>` or None,
            if no case was retrieved.
        """
        return self.__case

    def get_context(self):
        """Get the execution context.

        :returns: dictionary with string names and arbitrary values, or None, if no context
            was created.
        """
        context = self.__context
        if context is not None:
            context = self.__context.items().mapping

        return context

    def get_exception_information(self):
        """Get information about the exception raised during the execution.

        :returns: a tuple (type, value, traceback) similar to one returned by :meth:`sys.exc_info`.
        """
        return self.__exception_information


def _validate_context(context):
    """Validate a context.

    :param context: dictionary with string keys and arbitrary values.
    :returns: a validated context of the same type.
    :raises: :class:`ValueError` if context is invalid.
    """
    try:
        context_dict = dict(context)
    except (TypeError, ValueError) as error:
        raise ValueError("Context is not a valid dictionary") from error

    refined_context = {str(name): value for name, value in context_dict.items()}

    if len(context) > len(refined_context):
        raise ValueError("Several values in the context use the same name")

    return refined_context


def _validate_exception_information(exception_information):
    """Validate and create exception information.

    :param exception_information:
        a tuple (type, value, traceback) similar to one returned by :meth:`sys.exc_info`.
    :returns: a validated tuple.
    :raises: :class:`ValueError` if exception information is invalid.
    """
    try:
        exception_type, value, traceback = exception_information
    except Exception as error:
        raise ValueError("Exception information is not a valid triplet") from error

    if any(item is not None for item in (exception_type, value, traceback)):
        if not inspect.isclass(exception_type):
            raise ValueError("Exception type is not a class")

        if not issubclass(exception_type, BaseException):
            raise ValueError("Exception type is not a subclass of BaseException")

        if not isinstance(value, exception_type):
            raise ValueError("Exception value is not an instance of the exception type")

        if not inspect.istraceback(traceback):
            raise ValueError("Exception traceback is invalid")

    return (exception_type, value, traceback)
