"""Regulatory position and exposure limits (distinct from RiskConfig)."""

from __future__ import annotations

import logging
import time
from typing import Any

from horizon.compliance._audit import AuditTrail
from horizon.compliance._store import ComplianceStore
from horizon.compliance._types import (
    AuditEventType, ComplianceAction, RegulatoryLimitConfig,
)

logger = logging.getLogger("horizon.compliance.limits")


class RegulatoryLimits:
    """Firm-wide regulatory limits enforced before order submission.

    These are separate from the engine's RiskConfig:
    - RiskConfig: strategy-level limits, configurable by the trader
    - RegulatoryLimits: firm-wide limits, set by compliance officer, cannot be overridden

    Checks:
    1. Restricted market blacklist
    2. Per-market position limits
    3. Total portfolio notional cap
    4. Concentration limit (max % in one market)
    5. Daily volume limit
    """

    def __init__(
        self,
        config: RegulatoryLimitConfig,
        store: ComplianceStore,
        audit: AuditTrail,
    ) -> None:
        self._config = config
        self._store = store
        self._audit = audit
        self._daily_volume: float = 0.0
        self._daily_reset_date: str = ""

    def check_order(
        self,
        market_id: str,
        side: str,
        price: float,
        size: float,
        engine: Any,
    ) -> ComplianceAction:
        """Check an order against all regulatory limits.

        Returns APPROVED or REJECTED with audit trail.
        """
        notional = price * size

        # 1. Restricted market
        if market_id in self._config.restricted_markets:
            self._log_breach(market_id, "restricted_market",
                             f"Market {market_id} is restricted")
            return ComplianceAction.REJECTED

        # 2. Per-market position limit
        if market_id in self._config.max_position_per_market:
            max_pos = self._config.max_position_per_market[market_id]
            current = self._current_position_size(engine, market_id)
            if current + size > max_pos:
                self._log_breach(market_id, "position_limit",
                                 f"Position {current + size:.2f} would exceed limit {max_pos:.2f}")
                return ComplianceAction.REJECTED

        # 3. Total notional limit
        if self._config.max_total_notional < float("inf"):
            total = self._total_notional(engine)
            if total + notional > self._config.max_total_notional:
                self._log_breach(market_id, "total_notional",
                                 f"Total notional ${total + notional:,.2f} would exceed ${self._config.max_total_notional:,.2f}")
                return ComplianceAction.REJECTED

        # 4. Concentration limit
        if self._config.max_concentration_pct < 100.0:
            total = self._total_notional(engine)
            if total > 0:
                market_notional = self._market_notional(engine, market_id) + notional
                pct = (market_notional / (total + notional)) * 100.0
                if pct > self._config.max_concentration_pct:
                    self._log_breach(market_id, "concentration",
                                     f"Concentration {pct:.1f}% would exceed {self._config.max_concentration_pct:.1f}%")
                    return ComplianceAction.REJECTED

        # 5. Daily volume limit
        self._reset_daily_if_needed()
        if self._daily_volume + notional > self._config.max_daily_volume:
            self._log_breach(market_id, "daily_volume",
                             f"Daily volume ${self._daily_volume + notional:,.2f} would exceed ${self._config.max_daily_volume:,.2f}")
            return ComplianceAction.REJECTED

        self._daily_volume += notional
        return ComplianceAction.APPROVED

    def current_utilization(self, engine: Any) -> dict[str, float]:
        """Return current limit utilization as percentages."""
        util: dict[str, float] = {}
        total = self._total_notional(engine)

        if self._config.max_total_notional < float("inf"):
            util["total_notional"] = (total / self._config.max_total_notional) * 100.0

        if self._config.max_daily_volume < float("inf"):
            util["daily_volume"] = (self._daily_volume / self._config.max_daily_volume) * 100.0

        for market_id, max_pos in self._config.max_position_per_market.items():
            current = self._current_position_size(engine, market_id)
            util[f"position_{market_id}"] = (current / max_pos) * 100.0 if max_pos > 0 else 0.0

        return util

    def _current_position_size(self, engine: Any, market_id: str) -> float:
        try:
            positions = engine.positions_for_market(market_id)
            return sum(p.size for p in positions)
        except Exception:
            return 0.0

    def _total_notional(self, engine: Any) -> float:
        try:
            positions = engine.positions()
            return sum(p.size * p.avg_entry_price for p in positions)
        except Exception:
            return 0.0

    def _market_notional(self, engine: Any, market_id: str) -> float:
        try:
            positions = engine.positions_for_market(market_id)
            return sum(p.size * p.avg_entry_price for p in positions)
        except Exception:
            return 0.0

    def _reset_daily_if_needed(self) -> None:
        from datetime import datetime, timezone
        today = datetime.now(timezone.utc).strftime("%Y-%m-%d")
        if today != self._daily_reset_date:
            self._daily_volume = 0.0
            self._daily_reset_date = today

    def _log_breach(self, market_id: str, limit_type: str, description: str) -> None:
        self._audit.record(
            AuditEventType.LIMIT_BREACH, "system",
            {"limit_type": limit_type, "description": description},
            market_id=market_id,
        )
        logger.warning("Regulatory limit breach: %s", description)
