"""
Protected module contains job result class
"""

from dataclasses import dataclass
from typing import Optional

from ecmind_blue_client.rpc import JobError, JobParameter, JobResponseFile, JobValueT


@dataclass
class JobResult:
    """
    Represents the result of a job execution, including internal parameters, the parameters, the files and errors.

    Args:
        internal_parameters (list[JobParameter]): The list of internal parameters for the job.
        parameters (list[JobParameter]): The list of parameters for the job.
        files (list[JobResponseFile]): The list of files returned by the job.
        errors (list[JobError]): The list of errors during the job execution if something went wrong.
    """

    internal_parameters: list[JobParameter]
    parameters: list[JobParameter]
    files: list[JobResponseFile]
    errors: list[JobError]

    def get(self, name: str, value_type: type[JobValueT], default: Optional[JobValueT] = None) -> JobValueT:
        """
        Return a response parameter by name, cast to ``value_type``.

        Args:
            name (str): The parameter name to look up.
            value_type (type[JobValueT]): The expected Python type of the parameter value.
            default (JobValueT | None): Value to return when the parameter is not found.
                If omitted (``None``) and the parameter is missing, :exc:`KeyError` is raised.

        Returns:
            JobValueT: The parameter value cast to ``value_type``, or ``default`` if not found.

        Raises:
            KeyError: If the parameter is not found and no default was provided.
            ValueError: If the parameter exists but its value is not an instance of ``value_type``.
        """
        value = next((obj.value for obj in self.parameters if obj.name == name), None)

        if value is None:
            if default is not None:
                return default
            raise KeyError(f"Parameter '{name}' not found and no default provided")

        if not isinstance(value, value_type):
            raise ValueError(f"Parameter '{name}' has incorrect type, expected {value_type}, got {type(value)}")

        return value

    def get_internal(self, name: str, value_type: type[JobValueT], default: Optional[JobValueT] = None) -> JobValueT:
        """
        Return an internal response parameter by name, cast to ``value_type``.

        Args:
            name (str): The internal parameter name to look up.
            value_type (type[JobValueT]): The expected Python type of the parameter value.
            default (JobValueT | None): Value to return when the parameter is not found.
                If omitted (``None``) and the parameter is missing, :exc:`KeyError` is raised.

        Returns:
            JobValueT: The parameter value cast to ``value_type``, or ``default`` if not found.

        Raises:
            KeyError: If the internal parameter is not found and no default was provided.
            ValueError: If the parameter exists but its value is not an instance of ``value_type``.
        """
        value = next((obj.value for obj in self.internal_parameters if obj.name == name), None)

        if value is None:
            if default is not None:
                return default
            raise KeyError(f"Internal parameter '{name}' not found and no default provided")

        if not isinstance(value, value_type):
            raise ValueError(f"Internal parameter '{name}' has incorrect type, expected {value_type}, got {type(value)}")

        return value

    @property
    def return_code(self) -> int:
        """
        Return the server return code of the job (0 = success).
        """
        return self.get_internal("return", int)

    @property
    def error_message(self) -> str | None:
        """
        Return the concatenated error messages of the job, or ``None`` if no errors occurred.
        """
        return "\n".join([str(e) for e in self.errors]) if self.errors else None

    def __bool__(self):
        """
        Returns True if the job was successful, False otherwise.
        """
        return self.return_code == 0
