"""RiskEngine — main user-facing class (Layer 0)."""

from __future__ import annotations

from pathlib import Path
from typing import Any, Callable, TYPE_CHECKING

from pyfortis._calculators import BUILTIN_CALCULATORS
from pyfortis._engine import Engine, _CalculatorChecker, _ValidatorChecker
from pyfortis._types import (
    CircuitBreakerSpec,
    EscalationPolicy,
    Exposure,
    GateResult,
    LimitSpec,
    MetricResult,
    MetricSpec,
    Order,
    Position,
)
from pyfortis._validators import BUILTIN_VALIDATORS
from pyfortis.calculators import CalculatorFn, CalculatorRegistry
from pyfortis.config._raw_parser import raw_config_to_core
from pyfortis.config.loader import ConfigLoader
from pyfortis.errors import RiskPolicy
from pyfortis.handlers import BUILTIN_HANDLERS, HandlerExecutor, HandlerFn, HandlerRegistry
from pyfortis.validators import ValidatorFn, ValidatorRegistry

if TYPE_CHECKING:
    from pyfortis.monitor import RiskMonitor
    from pyfortis.orchestrator import RiskOrchestrator
    from pyfortis.stores.base import BreachStore, PositionStore


class RiskEngine:
    """User-facing risk engine — owns registries, creates monitors/orchestrators.

    Analogous to pystator's ``StateMachine``.
    """

    def __init__(
        self,
        limits: list[LimitSpec],
        metrics: list[MetricSpec],
        circuit_breakers: list[CircuitBreakerSpec],
        escalation_policy: EscalationPolicy,
        meta: dict[str, Any] | None = None,
        risk_policy: RiskPolicy | None = None,
    ) -> None:
        self._limits = limits
        self._metrics = metrics
        self._circuit_breakers = circuit_breakers
        self._escalation_policy = escalation_policy
        self._meta = meta or {}
        self._risk_policy = risk_policy or RiskPolicy()

        # Registries
        self._validators = ValidatorRegistry()
        self._calculators = CalculatorRegistry()
        self._handlers = HandlerRegistry()

        self._register_builtins()

        # Build engine
        self._engine = self._build_engine()

    # ── Factory classmethods ───────────────────────────────────────────

    @classmethod
    def from_yaml(cls, path: str | Path, **kwargs: Any) -> RiskEngine:
        raw = ConfigLoader.load(path)
        return cls.from_dict(raw, **kwargs)

    @classmethod
    def from_dict(cls, config: dict[str, Any], **kwargs: Any) -> RiskEngine:
        limits, metrics, cbs, esc, meta = raw_config_to_core(config)

        # Extract risk policy from meta
        risk_policy = kwargs.pop("risk_policy", None)
        if risk_policy is None:
            risk_policy = RiskPolicy(
                strict_mode=meta.get("strict_mode", True),
                fail_open=meta.get("fail_open", False),
                max_staleness_seconds=meta.get("max_staleness_seconds", 300.0),
            )

        return cls(
            limits=limits,
            metrics=metrics,
            circuit_breakers=cbs,
            escalation_policy=esc,
            meta=meta,
            risk_policy=risk_policy,
            **kwargs,
        )

    # ── Decorator registrations ────────────────────────────────────────

    def validator(self, name: str) -> Callable[[ValidatorFn], ValidatorFn]:
        def wrapper(fn: ValidatorFn) -> ValidatorFn:
            self._validators.register(name, fn)
            self._engine = self._build_engine()
            return fn
        return wrapper

    def calculator(self, name: str) -> Callable[[CalculatorFn], CalculatorFn]:
        def wrapper(fn: CalculatorFn) -> CalculatorFn:
            self._calculators.register(name, fn)
            self._engine = self._build_engine()
            return fn
        return wrapper

    def handler(self, name: str) -> Callable[[HandlerFn], HandlerFn]:
        def wrapper(fn: HandlerFn) -> HandlerFn:
            self._handlers.register(name, fn)
            return fn
        return wrapper

    # ── Stateless operations ───────────────────────────────────────────

    def validate_order(
        self,
        order: Order,
        positions: dict[str, Position] | None = None,
        exposures: dict[str, Exposure] | None = None,
        context: dict[str, Any] | None = None,
    ) -> GateResult:
        """Stateless order validation against limits."""
        return self._engine.evaluate_order(
            order,
            positions or {},
            exposures or {},
            context,
        )

    def compute_metrics(
        self,
        positions: dict[str, Position] | None = None,
        returns_data: list[float] | None = None,
        context: dict[str, Any] | None = None,
    ) -> list[MetricResult]:
        """Compute all configured metrics (stateless)."""
        return self._engine.compute_all_metrics(
            positions or {},
            returns_data or [],
            context,
        )

    def evaluate_circuit_breakers(
        self,
        daily_pnl: float = 0.0,
        peak_value: float = 0.0,
        current_value: float = 0.0,
        volatility: float = 0.0,
    ) -> list[CircuitBreakerSpec]:
        """Check circuit breakers (stateless)."""
        return self._engine.evaluate_circuit_breakers(
            daily_pnl, peak_value, current_value, volatility
        )

    # ── Factory methods for stateful layers ────────────────────────────

    def create_monitor(self) -> RiskMonitor:
        from pyfortis.monitor import RiskMonitor

        return RiskMonitor(self._engine, self._handlers)

    def create_orchestrator(
        self,
        position_store: PositionStore | None = None,
        breach_store: BreachStore | None = None,
    ) -> RiskOrchestrator:
        from pyfortis.orchestrator import RiskOrchestrator
        from pyfortis.stores.memory import InMemoryBreachStore, InMemoryPositionStore

        return RiskOrchestrator(
            engine=self._engine,
            handler_registry=self._handlers,
            position_store=position_store or InMemoryPositionStore(),
            breach_store=breach_store or InMemoryBreachStore(),
        )

    # ── Properties ─────────────────────────────────────────────────────

    @property
    def limits(self) -> list[LimitSpec]:
        return list(self._limits)

    @property
    def metrics(self) -> list[MetricSpec]:
        return list(self._metrics)

    @property
    def circuit_breakers(self) -> list[CircuitBreakerSpec]:
        return list(self._circuit_breakers)

    @property
    def meta(self) -> dict[str, Any]:
        return dict(self._meta)

    @property
    def validator_names(self) -> list[str]:
        return self._validators.names()

    @property
    def calculator_names(self) -> list[str]:
        return self._calculators.names()

    @property
    def handler_names(self) -> list[str]:
        return self._handlers.names()

    # ── Internals ──────────────────────────────────────────────────────

    def _register_builtins(self) -> None:
        for name, fn in BUILTIN_VALIDATORS.items():
            self._validators.register(name, fn)
        for name, fn in BUILTIN_CALCULATORS.items():
            self._calculators.register(name, fn)
        for name, fn in BUILTIN_HANDLERS.items():
            self._handlers.register(name, fn)

    def _build_engine(self) -> Engine:
        return Engine(
            limits=self._limits,
            metrics=self._metrics,
            circuit_breakers=self._circuit_breakers,
            escalation_policy=self._escalation_policy,
            meta=self._meta,
            risk_policy=self._risk_policy,
            validator_checker=_ValidatorChecker(self._validators.get),
            calculator_checker=_CalculatorChecker(self._calculators.get),
        )

    def __repr__(self) -> str:
        name = self._meta.get("engine_name", "RiskEngine")
        return (
            f"<{name} limits={len(self._limits)} "
            f"metrics={len(self._metrics)} "
            f"circuit_breakers={len(self._circuit_breakers)}>"
        )
