"""
Exit intelligence engine.

Multiple exit strategies running simultaneously:
trailing stop, regime change, profit target.
Whichever triggers first wins.
"""

import math


def _lerp(a: float, b: float, t: float) -> float:
    return a + (b - a) * max(0.0, min(1.0, t))


class ExitEngine:
    """Three exit mechanisms running simultaneously.

    Priority: regime_exit > trailing_stop > profit_target.
    """

    def check(self, position: dict, market_data: dict, regime: str) -> dict:
        """Check whether a position should be exited.

        Args:
            position: {entry_price, direction, current_stop, entry_regime,
                       regime_change_count, historical_mfe_pct}
            market_data: {price, atr, hurst}
            regime: current regime name

        Returns:
            {should_exit: bool, reason: str, detail: str, priority: int}
        """
        entry_price = position.get("entry_price", 0)
        direction = position.get("direction", "LONG")
        current_price = market_data.get("price", 0)
        atr = market_data.get("atr", 0)
        hurst = market_data.get("hurst", 0.5)
        historical_mfe_pct = position.get("historical_mfe_pct")

        if entry_price <= 0 or current_price <= 0:
            return {"should_exit": False, "reason": "insufficient_data"}

        # Unrealized P&L
        if direction == "LONG":
            unrealized_pct = (current_price - entry_price) / entry_price
        else:
            unrealized_pct = (entry_price - current_price) / entry_price

        signals = []

        # 1. Regime exit
        regime_sig = self._check_regime_exit(position, regime, hurst)
        if regime_sig:
            signals.append(regime_sig)

        # 2. Trailing stop
        if atr > 0:
            trailing_sig = self._check_trailing(
                position, current_price, atr, unrealized_pct, direction
            )
            if trailing_sig:
                signals.append(trailing_sig)

        # 3. Profit target
        profit_sig = self._check_profit_target(unrealized_pct, historical_mfe_pct)
        if profit_sig:
            signals.append(profit_sig)

        if not signals:
            return {"should_exit": False, "reason": "hold"}

        best = min(signals, key=lambda s: s.get("priority", 99))
        return {
            "should_exit": True,
            "reason": best["reason"],
            "detail": best.get("detail", ""),
            "priority": best["priority"],
        }

    def _check_trailing(self, position: dict, current_price: float,
                        atr: float, unrealized_pct: float,
                        direction: str) -> dict | None:
        if atr <= 0:
            return None

        atr_pct = atr / current_price if current_price > 0 else 0.01
        profit_in_atrs = unrealized_pct / atr_pct if atr_pct > 0 else 0

        # Trail distance narrows with profit
        if profit_in_atrs <= 0:
            trail_mult = 2.0
        elif profit_in_atrs < 1:
            trail_mult = _lerp(2.0, 1.5, profit_in_atrs)
        elif profit_in_atrs < 2:
            trail_mult = _lerp(1.5, 1.0, profit_in_atrs - 1)
        else:
            trail_mult = _lerp(1.0, 0.75, min(profit_in_atrs - 2, 1))

        trail_distance = trail_mult * atr
        current_stop = position.get("current_stop", 0)

        if direction == "LONG":
            new_stop = current_price - trail_distance
            if current_stop > 0:
                new_stop = max(new_stop, current_stop)
            if current_price <= new_stop and unrealized_pct > 0.005:
                return {"reason": "trailing_stop", "priority": 2,
                        "detail": f"trailing stop at ${new_stop:.4f}"}
        else:
            new_stop = current_price + trail_distance
            if current_stop > 0:
                new_stop = min(new_stop, current_stop)
            if current_price >= new_stop and unrealized_pct > 0.005:
                return {"reason": "trailing_stop", "priority": 2,
                        "detail": f"trailing stop at ${new_stop:.4f}"}

        return None

    def _check_regime_exit(self, position: dict, current_regime: str,
                           hurst: float) -> dict | None:
        entry_regime = position.get("entry_regime", "")
        if not entry_regime or not current_regime:
            return None

        if current_regime != entry_regime:
            change_count = position.get("regime_change_count", 0) + 1
            position["regime_change_count"] = change_count
            if change_count >= 2:
                return {"reason": "regime_change", "priority": 1,
                        "detail": f"{entry_regime} -> {current_regime}"}

        # Regime weakening: trend dying
        if entry_regime in ("trending",) and hurst < 0.55:
            return {"reason": "regime_weakening", "priority": 1,
                    "detail": f"hurst dropped to {hurst:.2f} (trend dying)"}

        return None

    def _check_profit_target(self, unrealized_pct: float,
                             historical_mfe_pct: float | None) -> dict | None:
        if historical_mfe_pct is None or historical_mfe_pct <= 0:
            return None

        if unrealized_pct > historical_mfe_pct * 0.80:
            return {"reason": "profit_target", "priority": 3,
                    "detail": f"reached {unrealized_pct*100:.1f}% of {historical_mfe_pct*100:.1f}% avg MFE"}

        return None
