"""
Risk management — 6-layer pipeline with V1-calibrated coin parameters.
"""
from __future__ import annotations
import logging
import math
from typing import Optional
from .models import Setup, TradePlan, OrderflowSnapshot

log = logging.getLogger(__name__)

# === Config Defaults ===
DEFAULT_MIN_RR = 1.5
DEFAULT_MAX_RISK_PCT = 0.02       # 2% of equity per trade
DEFAULT_MAX_DAILY_LOSSES = 2
DEFAULT_MAX_DAILY_TRADES = 6
DEFAULT_MIN_CONFIDENCE = 0.55
DEFAULT_SPREAD_MAX_BPS = 5.0
DEFAULT_MIN_NOTIONAL = 10.0       # min USD notional


class RiskManager:
    def __init__(
        self,
        equity_usd: float = 100.0,
        max_risk_pct: float = DEFAULT_MAX_RISK_PCT,
        min_rr: float = DEFAULT_MIN_RR,
        max_daily_losses: int = DEFAULT_MAX_DAILY_LOSSES,
        max_daily_trades: int = DEFAULT_MAX_DAILY_TRADES,
        min_confidence: float = DEFAULT_MIN_CONFIDENCE,
        spread_max_bps: float = DEFAULT_SPREAD_MAX_BPS,
        min_notional: float = DEFAULT_MIN_NOTIONAL,
        max_leverage: int = 20,
        fixed_margin_usd: float = 30.0,
        stop_atr_multiplier: float = 1.2,
    ):
        self.equity = equity_usd
        self.max_risk_pct = max_risk_pct
        self.min_rr = min_rr
        self.max_daily_losses = max_daily_losses
        self.max_daily_trades = max_daily_trades
        self.min_confidence = min_confidence
        self.spread_max_bps = spread_max_bps
        self.min_notional = min_notional
        self.max_leverage = max_leverage
        self.fixed_margin = fixed_margin_usd
        self.stop_atr_multiplier = stop_atr_multiplier

        # Daily counters
        self.daily_trades = 0
        self.daily_losses = 0
        self.daily_pnl = 0.0

    def check(
        self,
        setup: Setup,
        of: OrderflowSnapshot,
    ) -> Optional[str]:
        """
        6-layer risk check. Returns reject reason or None (pass).

        Layer 1: Spread & liquidity
        Layer 2: Confidence gate
        Layer 3: Reward/Risk ratio
        Layer 4: Stop distance (noise floor)
        Layer 5: Daily limits
        Layer 6: Fee-adjusted edge
        """
        # Layer 1: Spread
        if of.spread_bps > self.spread_max_bps:
            return f"spread {of.spread_bps:.1f} bps > max {self.spread_max_bps}"

        # Layer 2: Confidence
        if setup.confidence < self.min_confidence:
            return f"confidence {setup.confidence:.2f} < min {self.min_confidence}"

        # Layer 3: R:R
        if setup.rr < self.min_rr:
            return f"R:R {setup.rr:.2f} < min {self.min_rr}"

        # Layer 4: Noise floor — stop must be beyond random noise.
        if of.atr_14 > 0 and setup.risk < of.atr_14 * self.stop_atr_multiplier:
            required = of.atr_14 * self.stop_atr_multiplier
            return f"stop {setup.risk:.4f} too tight (ATR={of.atr_14:.4f}, need {self.stop_atr_multiplier:.2f}x={required:.4f})"
        # Coin-specific params are averages, not hard limits — LLM decides

        # Layer 5: Daily limits
        if self.daily_losses >= self.max_daily_losses:
            return f"daily loss limit ({self.daily_losses}/{self.max_daily_losses})"
        if self.daily_trades >= self.max_daily_trades:
            return f"daily trade limit ({self.daily_trades}/{self.max_daily_trades})"

        # Layer 6: Fee-adjusted edge
        # Assume 4.5 bps taker fee each way = 9 bps round trip
        fee_pct = 0.0009  # 9 bps
        fee_cost = setup.entry_ref * fee_pct
        expected_move = setup.reward
        if expected_move > 0 and fee_cost / expected_move > 0.5:
            return f"fees eat {fee_cost/expected_move*100:.0f}% of target"

        return None

    def build_plan(
        self,
        setup: Setup,
        of: OrderflowSnapshot,
        *,
        sz_decimals: int | None = None,
        leverage: int | None = None,
    ) -> TradePlan:
        """Convert setup into a concrete fixed-margin trade plan.

        V2's prompt and config define fixed_margin_usd as actual margin per
        trade. Do not shrink order size from risk first; that made live orders
        use much less real margin than configured.
        """
        leverage = min(int(leverage or self.max_leverage), self.max_leverage)
        margin = max(0.0, self.fixed_margin)
        notional = margin * leverage
        qty = notional / setup.entry_ref
        if sz_decimals is not None:
            factor = 10 ** max(int(sz_decimals), 0)
            qty = math.floor(qty * factor) / factor
            if qty <= 0:
                raise ValueError(f"{setup.coin} size rounds to zero at szDecimals={sz_decimals}")
            notional = qty * setup.entry_ref
            margin = notional / max(float(leverage), 1.0)

        return TradePlan(
            coin=setup.coin,
            side=setup.side,
            entry=setup.entry_ref,
            stop=setup.invalidation,
            target=setup.target,
            qty=qty,
            leverage=leverage,
            margin_usd=margin,
            setup=setup,
        )

    def record_trade(self, pnl: float = 0):
        """Record a completed trade for daily limits."""
        self.daily_trades += 1
        self.daily_pnl += pnl
        if pnl < 0:
            self.daily_losses += 1

    def reset_daily(self):
        self.daily_trades = 0
        self.daily_losses = 0
        self.daily_pnl = 0.0
