"""Error hierarchy and risk policy for pyfortis."""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Any


class RiskError(Exception):
    """Base error for all pyfortis errors."""

    def __init__(self, message: str, *, context: dict[str, Any] | None = None) -> None:
        super().__init__(message)
        self.context: dict[str, Any] = context or {}

    def to_dict(self) -> dict[str, Any]:
        return {
            "error": type(self).__name__,
            "message": str(self),
            "context": self.context,
        }


class ConfigurationError(RiskError):
    """Invalid or missing configuration."""


class LimitBreachError(RiskError):
    """A risk limit has been breached."""


class OrderRejectedError(RiskError):
    """An order was rejected by the risk gate."""


class CircuitBreakerTrippedError(RiskError):
    """A circuit breaker has been triggered."""


class CalculatorNotFoundError(RiskError):
    """No calculator registered for the requested metric type."""


class ValidatorNotFoundError(RiskError):
    """No validator registered for the requested limit type."""


class HandlerNotFoundError(RiskError):
    """No handler registered for the requested breach action."""


class StalePositionError(RiskError):
    """Position data is stale beyond the allowed threshold."""


@dataclass(frozen=True, slots=True)
class RiskPolicy:
    """Controls engine behaviour for edge cases."""

    strict_mode: bool = True
    fail_open: bool = False
    max_staleness_seconds: float = 300.0
    extra: dict[str, Any] = field(default_factory=dict)
