"""RiskMonitor — stateful risk monitor (Layer 2)."""

from __future__ import annotations

import threading
from typing import Any

from pyfortis._engine import Engine
from pyfortis._types import (
    CircuitBreakerSpec,
    Exposure,
    GateResult,
    MetricResult,
    Order,
    Position,
)
from pyfortis.errors import CircuitBreakerTrippedError
from pyfortis.handlers import HandlerExecutor, HandlerRegistry


class RiskMonitor:
    """Stateful risk monitor — maintains positions, exposures, P&L.

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

    def __init__(
        self,
        engine: Engine,
        handler_registry: HandlerRegistry,
    ) -> None:
        self._engine = engine
        self._handler_executor = HandlerExecutor(handler_registry)
        self._lock = threading.Lock()

        # Mutable state
        self._positions: dict[str, Position] = {}
        self._exposures: dict[str, Exposure] = {}
        self._daily_pnl: float = 0.0
        self._peak_value: float = 0.0
        self._current_value: float = 0.0
        self._halted: bool = False
        self._halt_reason: str = ""

    # ── Position management ────────────────────────────────────────────

    def update_position(self, position: Position) -> None:
        with self._lock:
            self._positions[position.instrument] = position
            self._recompute_exposures()

    def update_positions(self, positions: list[Position]) -> None:
        with self._lock:
            for pos in positions:
                self._positions[pos.instrument] = pos
            self._recompute_exposures()

    def remove_position(self, instrument: str) -> None:
        with self._lock:
            self._positions.pop(instrument, None)
            self._exposures.pop(instrument, None)

    def get_positions(self) -> dict[str, Position]:
        with self._lock:
            return dict(self._positions)

    def get_exposures(self) -> dict[str, Exposure]:
        with self._lock:
            return dict(self._exposures)

    # ── P&L management ─────────────────────────────────────────────────

    def update_pnl(self, daily_pnl: float, current_value: float) -> None:
        with self._lock:
            self._daily_pnl = daily_pnl
            self._current_value = current_value
            if current_value > self._peak_value:
                self._peak_value = current_value

    def reset_daily(self) -> None:
        with self._lock:
            self._daily_pnl = 0.0
            self._halted = False
            self._halt_reason = ""

    # ── Risk evaluation ────────────────────────────────────────────────

    def validate_order(
        self,
        order: Order,
        context: dict[str, Any] | None = None,
    ) -> GateResult:
        """Validate an order using current positions/exposures."""
        with self._lock:
            if self._halted:
                return GateResult.rejected(
                    order.order_id,
                    message=f"Trading halted: {self._halt_reason}",
                )
            positions = dict(self._positions)
            exposures = dict(self._exposures)

        result = self._engine.evaluate_order(order, positions, exposures, context)

        # Execute handlers for breaches
        if result.breaches:
            self._handler_executor.execute_all(result.breaches, context)

        return result

    def compute_metrics(
        self,
        returns_data: list[float] | None = None,
        context: dict[str, Any] | None = None,
    ) -> list[MetricResult]:
        """Compute metrics using current positions."""
        with self._lock:
            positions = dict(self._positions)
        return self._engine.compute_all_metrics(
            positions, returns_data or [], context
        )

    def check_circuit_breakers(self) -> list[CircuitBreakerSpec]:
        """Check circuit breakers with current state, may halt trading."""
        with self._lock:
            daily_pnl = self._daily_pnl
            peak_value = self._peak_value
            current_value = self._current_value

        tripped = self._engine.evaluate_circuit_breakers(
            daily_pnl=daily_pnl,
            peak_value=peak_value,
            current_value=current_value,
        )

        if tripped:
            names = ", ".join(cb.name for cb in tripped)
            with self._lock:
                self._halted = True
                self._halt_reason = f"Circuit breakers tripped: {names}"

        return tripped

    # ── State queries ──────────────────────────────────────────────────

    @property
    def is_halted(self) -> bool:
        with self._lock:
            return self._halted

    @property
    def halt_reason(self) -> str:
        with self._lock:
            return self._halt_reason

    @property
    def daily_pnl(self) -> float:
        with self._lock:
            return self._daily_pnl

    @property
    def peak_value(self) -> float:
        with self._lock:
            return self._peak_value

    @property
    def current_value(self) -> float:
        with self._lock:
            return self._current_value

    # ── Internal ───────────────────────────────────────────────────────

    def _recompute_exposures(self) -> None:
        """Recompute exposures from current positions (must hold lock)."""
        self._exposures = {
            inst: Exposure.from_position(pos)
            for inst, pos in self._positions.items()
        }
