"""
出场模块 — 结构trailing止损

规则:
- 浮盈>5%后激活trailing
- BTC trailing 15% / ETH 20% / 山寨 25%
- 做空从最低点反弹12%
- 最长持仓：做多500根4H / 做空200根4H
- 止损触发直接出场
"""

import numpy as np
import pandas as pd
from dataclasses import dataclass
from typing import Optional

from ..config import settings as S
from ..config.coins import get_coin


@dataclass
class ExitResult:
    """出场结果"""
    exit_time: pd.Timestamp
    exit_price: float
    exit_reason: str          # 'trailing', 'stop_loss', 'max_hold', 'extreme_slippage'
    pnl_pct: float            # 盈亏百分比
    pnl_dollar: float         # 盈亏金额
    max_unrealized_pct: float # 最大浮盈
    hold_bars: int            # 持仓K线数
    exit_efficiency: float    # 出场效率 = 实际盈亏/最大浮盈
    fees: float               # 总手续费


def simulate_exit(
    direction: str,
    entry_price: float,
    stop_loss: float,
    coin: str,
    entry_method: str,
    notional: float,
    df_4h: pd.DataFrame,
    entry_time: pd.Timestamp,
) -> Optional[ExitResult]:
    """
    模拟持仓过程（逐根4H bar）

    Args:
        direction: 'long' or 'short'
        entry_price: 入场价
        stop_loss: 止损价
        coin: 币种
        entry_method: '1H回踩' or '4H直接'
        notional: 名义持仓
        df_4h: 4H K线数据
        entry_time: 入场时间

    Returns:
        ExitResult or None if no exit found
    """
    cfg = get_coin(coin)
    trailing_pct = cfg.trailing_pct if direction == 'long' else 0.12
    max_hold = S.MAX_HOLD_LONG_4H_BARS if direction == 'long' else S.MAX_HOLD_SHORT_4H_BARS

    # 从入场时间后的4H K线开始
    mask = df_4h['timestamp'] > entry_time
    bars = df_4h[mask]

    if len(bars) == 0:
        return None

    # 手续费计算
    if entry_method == '1H回踩':
        entry_fee = notional * S.FEE_MAKER   # 限价单
    else:
        entry_fee = notional * S.FEE_TAKER   # 市价单

    # 追踪变量
    if direction == 'long':
        peak_price = entry_price
    else:
        trough_price = entry_price

    max_unrealized_pct = 0.0
    trailing_active = False
    hold_bars = 0

    for _, bar in bars.iterrows():
        hold_bars += 1
        hi = bar['high']
        lo = bar['low']
        cl = bar['close']

        if direction == 'long':
            # 更新最高价
            peak_price = max(peak_price, hi)
            unrealized = (peak_price - entry_price) / entry_price
            max_unrealized_pct = max(max_unrealized_pct, unrealized)

            # 检查止损（先于trailing，用low）
            if lo <= stop_loss:
                exit_price = stop_loss
                reason = 'stop_loss'

                # 极端行情滑点
                bar_drop = (bar['open'] - lo) / bar['open'] if bar['open'] > 0 else 0
                if bar_drop > 0.10:
                    exit_price = stop_loss * (1 - S.SLIPPAGE_EXTREME)
                    reason = 'extreme_slippage'

                return _build_result(
                    direction, entry_price, exit_price, reason,
                    notional, entry_fee, max_unrealized_pct, hold_bars,
                    bar['timestamp']
                )

            # trailing激活和触发
            current_unrealized = (cl - entry_price) / entry_price
            if current_unrealized >= S.TRAILING_ACTIVATE_PCT:
                trailing_active = True

            if trailing_active:
                drawdown_from_peak = (peak_price - lo) / peak_price
                if drawdown_from_peak >= trailing_pct:
                    exit_price = peak_price * (1 - trailing_pct)
                    return _build_result(
                        direction, entry_price, exit_price, 'trailing',
                        notional, entry_fee, max_unrealized_pct, hold_bars,
                        bar['timestamp']
                    )

        else:  # short
            trough_price = min(trough_price, lo)
            unrealized = (entry_price - trough_price) / entry_price
            max_unrealized_pct = max(max_unrealized_pct, unrealized)

            # 检查止损（用high）
            if hi >= stop_loss:
                exit_price = stop_loss
                reason = 'stop_loss'
                return _build_result(
                    direction, entry_price, exit_price, reason,
                    notional, entry_fee, max_unrealized_pct, hold_bars,
                    bar['timestamp']
                )

            # trailing
            current_unrealized = (entry_price - cl) / entry_price
            if current_unrealized >= S.TRAILING_ACTIVATE_PCT:
                trailing_active = True

            if trailing_active:
                bounce_from_trough = (hi - trough_price) / trough_price
                if bounce_from_trough >= trailing_pct:
                    exit_price = trough_price * (1 + trailing_pct)
                    return _build_result(
                        direction, entry_price, exit_price, 'trailing',
                        notional, entry_fee, max_unrealized_pct, hold_bars,
                        bar['timestamp']
                    )

        # 最长持仓
        if hold_bars >= max_hold:
            return _build_result(
                direction, entry_price, cl, 'max_hold',
                notional, entry_fee, max_unrealized_pct, hold_bars,
                bar['timestamp']
            )

    # 数据结束仍未出场 → 按最后收盘价出场
    if hold_bars > 0:
        last_bar = bars.iloc[-1]
        return _build_result(
            direction, entry_price, last_bar['close'], 'data_end',
            notional, entry_fee, max_unrealized_pct, hold_bars,
            last_bar['timestamp']
        )

    return None


def _build_result(
    direction: str,
    entry_price: float,
    exit_price: float,
    reason: str,
    notional: float,
    entry_fee: float,
    max_unrealized_pct: float,
    hold_bars: int,
    exit_time: pd.Timestamp,
) -> ExitResult:
    """构建出场结果"""
    # 盈亏百分比
    if direction == 'long':
        pnl_pct = (exit_price - entry_price) / entry_price
    else:
        pnl_pct = (entry_price - exit_price) / entry_price

    # 出场手续费
    exit_fee = notional * S.FEE_EXIT
    total_fees = entry_fee + exit_fee

    # 盈亏金额（名义持仓 × 盈亏% - 手续费）
    pnl_dollar = notional * pnl_pct - total_fees

    # 出场效率
    if max_unrealized_pct > 0:
        exit_efficiency = pnl_pct / max_unrealized_pct
    else:
        exit_efficiency = 0.0

    return ExitResult(
        exit_time=exit_time,
        exit_price=exit_price,
        exit_reason=reason,
        pnl_pct=pnl_pct,
        pnl_dollar=pnl_dollar,
        max_unrealized_pct=max_unrealized_pct,
        hold_bars=hold_bars,
        exit_efficiency=exit_efficiency,
        fees=total_fees,
    )
