"""Custom exception hierarchy for ieum.

Provides structured error handling with specific exception types
for different failure modes, enabling better error messages and recovery.
"""

from __future__ import annotations


class IeumError(Exception):
    """Base exception for all ieum errors."""

    def __init__(self, message: str, *, details: str | None = None) -> None:
        """Initialize the exception.

        Args:
            message: Human-readable error message
            details: Optional additional details for debugging
        """
        super().__init__(message)
        self.message = message
        self.details = details

    def __str__(self) -> str:
        if self.details:
            return f"{self.message}\n  Details: {self.details}"
        return self.message


# ============================================================================
# Configuration Errors
# ============================================================================


class ConfigurationError(IeumError):
    """Error in configuration or settings."""


class InvalidModelError(ConfigurationError):
    """Specified model is invalid or unavailable."""

    def __init__(self, model_name: str, *, available: list[str] | None = None) -> None:
        message = f"Invalid model: {model_name}"
        details = None
        if available:
            details = f"Available models: {', '.join(available[:5])}"
            if len(available) > 5:
                details += f" ... and {len(available) - 5} more"
        super().__init__(message, details=details)
        self.model_name = model_name
        self.available = available


class MissingAPIKeyError(ConfigurationError):
    """Required API key is missing."""

    def __init__(self, provider: str, env_var: str) -> None:
        message = f"Missing API key for {provider}"
        details = f"Set the {env_var} environment variable"
        super().__init__(message, details=details)
        self.provider = provider
        self.env_var = env_var


# ============================================================================
# Tool Execution Errors
# ============================================================================


class ToolError(IeumError):
    """Base class for tool execution errors."""

    def __init__(
        self,
        message: str,
        *,
        tool_name: str | None = None,
        details: str | None = None,
    ) -> None:
        super().__init__(message, details=details)
        self.tool_name = tool_name


class ToolExecutionError(ToolError):
    """Error during tool execution."""


class ToolTimeoutError(ToolError):
    """Tool execution timed out."""

    def __init__(
        self,
        tool_name: str,
        timeout_seconds: float,
        *,
        details: str | None = None,
    ) -> None:
        message = f"Tool '{tool_name}' timed out after {timeout_seconds}s"
        super().__init__(message, tool_name=tool_name, details=details)
        self.timeout_seconds = timeout_seconds


class ToolPermissionError(ToolError):
    """Tool execution denied due to permissions."""

    def __init__(
        self,
        tool_name: str,
        reason: str = "rejected by user",
    ) -> None:
        message = f"Tool '{tool_name}' execution denied: {reason}"
        super().__init__(message, tool_name=tool_name)
        self.reason = reason


# ============================================================================
# File Operation Errors
# ============================================================================


class FileOperationError(IeumError):
    """Error during file operations."""

    def __init__(
        self,
        message: str,
        *,
        path: str | None = None,
        details: str | None = None,
    ) -> None:
        super().__init__(message, details=details)
        self.path = path


class FileNotFoundError(FileOperationError):
    """Requested file does not exist."""

    def __init__(self, path: str) -> None:
        message = f"File not found: {path}"
        super().__init__(message, path=path)


class PathTraversalError(FileOperationError):
    """Attempted path traversal detected."""

    def __init__(self, path: str) -> None:
        message = f"Path traversal not allowed: {path}"
        super().__init__(message, path=path)


class FileTooLargeError(FileOperationError):
    """File exceeds size limit."""

    def __init__(self, path: str, size: int, max_size: int) -> None:
        message = f"File too large: {path} ({size} bytes, max {max_size})"
        super().__init__(message, path=path)
        self.size = size
        self.max_size = max_size


# ============================================================================
# Network Errors
# ============================================================================


class NetworkError(IeumError):
    """Network-related error."""


class ConnectionError(NetworkError):
    """Failed to establish connection."""

    def __init__(self, host: str, *, details: str | None = None) -> None:
        message = f"Failed to connect to {host}"
        super().__init__(message, details=details)
        self.host = host


class APIError(NetworkError):
    """Error from external API."""

    def __init__(
        self,
        provider: str,
        status_code: int | None = None,
        *,
        details: str | None = None,
    ) -> None:
        message = f"API error from {provider}"
        if status_code:
            message += f" (HTTP {status_code})"
        super().__init__(message, details=details)
        self.provider = provider
        self.status_code = status_code


class RateLimitError(APIError):
    """Rate limit exceeded."""

    def __init__(
        self,
        provider: str,
        retry_after: float | None = None,
    ) -> None:
        details = None
        if retry_after:
            details = f"Retry after {retry_after}s"
        super().__init__(provider, status_code=429, details=details)
        self.retry_after = retry_after


# ============================================================================
# Session Errors
# ============================================================================


class SessionError(IeumError):
    """Error related to session management."""


class SessionNotFoundError(SessionError):
    """Requested session does not exist."""

    def __init__(self, thread_id: str) -> None:
        message = f"Session not found: {thread_id}"
        super().__init__(message)
        self.thread_id = thread_id


class SessionCorruptedError(SessionError):
    """Session data is corrupted."""

    def __init__(self, thread_id: str, *, details: str | None = None) -> None:
        message = f"Session corrupted: {thread_id}"
        super().__init__(message, details=details)
        self.thread_id = thread_id


# ============================================================================
# Agent Errors
# ============================================================================


class AgentError(IeumError):
    """Error during agent execution."""


class AgentInterruptedError(AgentError):
    """Agent execution was interrupted."""

    def __init__(self, reason: str = "user interrupt") -> None:
        message = f"Agent interrupted: {reason}"
        super().__init__(message)
        self.reason = reason


class ContextLimitError(AgentError):
    """Context window limit exceeded."""

    def __init__(
        self,
        current_tokens: int,
        max_tokens: int,
    ) -> None:
        pct = (current_tokens / max_tokens) * 100 if max_tokens else 0
        message = f"Context limit exceeded: {current_tokens}/{max_tokens} tokens ({pct:.0f}%)"
        super().__init__(message)
        self.current_tokens = current_tokens
        self.max_tokens = max_tokens
