"""Core error hierarchy — structured exceptions for the agent runtime.

All errors inherit from AlbusError so callers can:
- Catch broadly: except AlbusError
- Catch narrowly: except LLMError
- Inspect type: error.error_type  (e.g., "LLMError")

No runtime/host deps — this is Layer 0.
"""


class AlbusError(Exception):
    """Base exception for all AlbusOS errors.

    Attributes
    ----------
        error_type: Machine-readable error category (class name).
        details: Optional structured data about the error.
    """

    def __init__(
        self,
        message: str = "",
        *,
        details: dict[str, object] | None = None,
    ) -> None:
        super().__init__(message)
        self.error_type: str = type(self).__name__
        self.details: dict[str, object] = details or {}


class ValidationError(ValueError):
    """Raised when a contract/DTO validation fails."""


# =============================================================================
# LLM ERRORS
# =============================================================================


class LLMError(AlbusError):
    """LLM call failed (provider error, timeout, invalid response)."""

    def __init__(
        self,
        message: str = "LLM call failed",
        *,
        model: str | None = None,
        provider: str | None = None,
        details: dict[str, object] | None = None,
    ) -> None:
        super().__init__(message, details=details)
        self.model = model
        self.provider = provider


# =============================================================================
# TOOL ERRORS
# =============================================================================


class ToolError(AlbusError):
    """Tool execution failed."""

    def __init__(
        self,
        message: str = "Tool execution failed",
        *,
        tool_name: str | None = None,
        details: dict[str, object] | None = None,
    ) -> None:
        super().__init__(message, details=details)
        self.tool_name = tool_name


# =============================================================================
# BUDGET ERRORS
# =============================================================================


class BudgetExceededError(AlbusError):
    """Execution budget exceeded (tokens, steps, time, or cost)."""

    def __init__(
        self,
        message: str = "Budget exceeded",
        *,
        budget_type: str | None = None,
        limit: float | None = None,
        used: float | None = None,
        details: dict[str, object] | None = None,
    ) -> None:
        super().__init__(message, details=details)
        self.budget_type = budget_type
        self.limit = limit
        self.used = used


# =============================================================================
# STATE ERRORS
# =============================================================================


class StateError(AlbusError):
    """State storage operation failed (load, save, delete)."""

    def __init__(
        self,
        message: str = "State operation failed",
        *,
        operation: str | None = None,
        details: dict[str, object] | None = None,
    ) -> None:
        super().__init__(message, details=details)
        self.operation = operation


__all__ = [
    "AlbusError",
    "BudgetExceededError",
    "LLMError",
    "StateError",
    "ToolError",
    "ValidationError",
]
