"""Canonical domain error hierarchy for Aegis.

Every service-layer error should be a subclass of :class:`DomainError`.
The global exception handler in ``app.middleware.error_handler`` maps
each concrete subclass to an appropriate HTTP status code so that
services never depend on FastAPI.

Existing code that imports from ``app.domain.exceptions`` continues to
work — that module re-exports everything defined here.
"""

from __future__ import annotations


class DomainError(Exception):
    """Base for all domain errors."""

    def __init__(self, message: str, *, code: str = "DOMAIN_ERROR") -> None:
        self.message = message
        self.code = code
        super().__init__(message)


# ── Entity lifecycle ──────────────────────────────────────────────────


class EntityNotFoundError(DomainError):
    """A requested entity does not exist."""

    def __init__(self, entity: str, identifier: str) -> None:
        super().__init__(f"{entity} not found: {identifier}", code="NOT_FOUND")
        self.entity = entity
        self.identifier = identifier


class DuplicateEntityError(DomainError):
    """Creating an entity that already exists."""

    def __init__(self, entity: str, field: str, value: str) -> None:
        super().__init__(
            f"{entity} with {field}='{value}' already exists",
            code="DUPLICATE",
        )


# ── State machine ────────────────────────────────────────────────────


class InvalidStateTransition(DomainError):
    """A state-machine transition is not allowed."""

    def __init__(
        self,
        current_state: str,
        target_state: str,
        valid_transitions: list[str] | None = None,
    ) -> None:
        msg = f"Cannot transition from '{current_state}' to '{target_state}'"
        if valid_transitions:
            msg += f". Valid transitions: {valid_transitions}"
        super().__init__(msg, code="INVALID_TRANSITION")
        self.current_state = current_state
        self.target_state = target_state
        self.valid_transitions = valid_transitions or []


# ── Business rules ────────────────────────────────────────────────────


class BusinessRuleViolation(DomainError):
    """An operation violates a business invariant."""

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


class InvalidOperationError(BusinessRuleViolation):
    """An operation is invalid in the current context.

    Kept for backward compatibility; new code should prefer
    :class:`BusinessRuleViolation` directly.
    """

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


# ── Authorization ────────────────────────────────────────────────────


class PermissionViolation(DomainError):
    """The user lacks permissions for an action."""

    def __init__(self, message: str = "Insufficient permissions") -> None:
        super().__init__(message, code="FORBIDDEN")
