"""Error hierarchy for PyAuthz.

Follows "fail loud on load, fail safe on evaluate":
- Load-time errors (SchemaError, ConfigError, PolicyVersionError) raise immediately.
- Evaluation-time: never crash, always produce a Decision. Denied carries the Decision.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from pyauthz.core.decision import Decision


class PyAuthzError(Exception):
    """Base exception for all PyAuthz errors."""


class Denied(PyAuthzError):
    """Raised by authorize() on denial. Carries the full Decision object.

    Framework integrations convert this to a 403 with an actionable message.
    The ``suggestions`` attribute contains actionable hints for resolving the
    denial (e.g., which roles to add), populated automatically by the engine.

    Example:
        try:
            rp.authorize(user, "pii:read", record)
        except Denied as e:
            print(e.decision.reasoning)
            print(e.decision.explain())
            for suggestion in e.suggestions:
                print(suggestion)
    """

    def __init__(self, decision: Decision, suggestions: list[str] | None = None) -> None:
        self.decision = decision
        self.suggestions: list[str] = suggestions or []
        super().__init__(decision.explain())


class SchemaError(PyAuthzError):
    """Raised when a YAML policy file has invalid structure or unknown fields.

    Attributes:
        file_path: Path to the invalid file.
        message: Description of what's wrong.
    """

    def __init__(self, file_path: str, message: str) -> None:
        self.file_path = file_path
        self.message = message
        super().__init__(f"{file_path}: {message}")


class ConfigError(PyAuthzError):
    """Raised for configuration errors like referencing undefined roles.

    Attributes:
        message: Description of the configuration problem.
    """

    def __init__(self, message: str) -> None:
        self.message = message
        super().__init__(message)


class PolicyVersionError(PyAuthzError):
    """Raised when a YAML schema version is unsupported or missing.

    Attributes:
        file_path: Path to the file with the version problem.
        version: The version found (or None if missing).
        supported: The set of supported versions.
    """

    def __init__(self, file_path: str, version: int | None, supported: set[int]) -> None:
        self.file_path = file_path
        self.version = version
        self.supported = supported
        ver_str = str(version) if version is not None else "missing"
        super().__init__(f"{file_path}: schema version {ver_str}, supported: {sorted(supported)}")
