"""Shared error taxonomy and helpers for runtime boundaries."""

from __future__ import annotations

from dataclasses import dataclass
from datetime import UTC
from datetime import datetime
from enum import StrEnum
from uuid import uuid4


class ErrorCategory(StrEnum):
    """Stable error categories defined by docs/error_handling.md."""

    VALIDATION = "validation_error"
    USER_INPUT = "user_input_error"
    NOT_FOUND = "not_found_error"
    CONFLICT = "conflict_error"
    EXTERNAL_PROVIDER = "external_provider_error"
    PROCESSING = "processing_error"
    INFRA_TRANSIENT = "infrastructure_transient_error"
    INFRA_PERSISTENT = "infrastructure_persistent_error"
    INTERNAL_UNEXPECTED = "internal_unexpected_error"


def new_error_id() -> str:
    """Return a short, user-shareable error reference id."""
    return uuid4().hex[:8]


class AppError(RuntimeError):
    """Base application error carrying user-safe handling metadata."""

    def __init__(
        self,
        message: str,
        *,
        category: ErrorCategory = ErrorCategory.INTERNAL_UNEXPECTED,
        suggestion: str = "Retry once. If it persists, review logs and report the error reference id.",
        retriable: bool = False,
        error_id: str | None = None,
    ) -> None:
        super().__init__(message)
        self.message = message
        self.category = category
        self.suggestion = suggestion
        self.retriable = retriable
        self.error_id = error_id or new_error_id()


@dataclass(frozen=True)
class ErrorEnvelope:
    """Serializable API/UI error payload."""

    error_id: str
    category: str
    message: str
    suggestion: str
    timestamp: str


def build_error_envelope(error: AppError) -> ErrorEnvelope:
    """Build an API-safe response envelope from an AppError."""
    return ErrorEnvelope(
        error_id=error.error_id,
        category=error.category.value,
        message=error.message,
        suggestion=error.suggestion,
        timestamp=datetime.now(UTC).isoformat(),
    )


def classify_unexpected_error(exc: Exception, *, operation: str) -> AppError:
    """Normalize unknown exceptions into internal_unexpected_error."""
    return AppError(
        f"Unexpected error during {operation}: {exc}",
        category=ErrorCategory.INTERNAL_UNEXPECTED,
        suggestion="Retry once. If it persists, review logs and report the error reference id.",
        retriable=False,
    )


def format_error_detail(error: AppError) -> str:
    """Return a compact persisted failure string for transcript.error_detail."""
    return f"[{error.category.value}] {error.message} | suggestion={error.suggestion} | error_id={error.error_id}"
