"""
규칙 발화 기반 GT 모델 복리 포트폴리오 시뮬.
"""

from __future__ import annotations

from typing import Any

import pandas as pd

from config import (
    GT_INITIAL_CASH_KRW,
    GT_SIGNAL_CAUSAL,
    LIVE_ORDER_KRW,
    TRADING_FEE_RATE,
)
from deepcoin.ground_truth.gt_allocation import simulate_portfolio_summary
from deepcoin.matching.position_sizing import (
    attach_gt_model_amounts,
)


def _planned_order_krw(
    t: dict[str, Any],
    order_krw: float,
    sizing_mode: str,
) -> float:
    """
    체결 계획 원화: amount_krw 우선 또는 고정.

    Args:
        t: trade dict.
        order_krw: 고정 1회 금액.
        sizing_mode: fixed | amount_krw.

    Returns:
        계획 원화.
    """
    ak = t.get("amount_krw")
    if sizing_mode == "amount_krw" or (ak is not None and float(ak) > 0):
        return float(ak or 0)
    return float(order_krw)


def sort_fires_chronological(fires: pd.DataFrame) -> pd.DataFrame:
    """
    발화를 시간순 정렬 (일·금액 한도 없음).

    Args:
        fires: fire_outcomes.

    Returns:
        정렬된 DataFrame.
    """
    if fires.empty:
        return fires
    return fires.sort_values("dt").copy()


def simulate_fires_compound(
    fires: pd.DataFrame,
    *,
    initial_cash: float = GT_INITIAL_CASH_KRW,
    fee_rate: float = TRADING_FEE_RATE,
) -> tuple[list[dict[str, Any]], dict[str, Any]]:
    """
    발화 → GT tier 복리 amount_krw 배분 (allocate_order_amounts_chronological).

    Args:
        fires: fire_outcomes.
        initial_cash: 시작 현금 (이후 체결마다 누적).
        fee_rate: 수수료율.

    Returns:
        (amount_krw 채워진 trade dict, stats).
    """
    trades = fires_to_trade_list(
        fires,
        apply_dynamic_sizing=True,
        initial_cash=initial_cash,
        fee_rate=fee_rate,
    )
    n_in = int(len(fires))
    n_out = sum(1 for t in trades if float(t.get("amount_krw") or 0) > 0)
    return trades, {
        "input_fires": n_in,
        "executed": n_out,
        "skipped": max(n_in - n_out, 0),
    }


def select_capped_fires(
    fires: pd.DataFrame,
    *,
    use_dynamic_sizing: bool = True,
) -> pd.DataFrame:
    """
    시각순 발화 반환 (레거시명; 일·금액 한도 미적용).

    Args:
        fires: fire_outcomes.
        use_dynamic_sizing: 미사용 (하위 호환).

    Returns:
        정렬된 발화 DataFrame.
    """
    _ = use_dynamic_sizing
    return sort_fires_chronological(fires)


def fires_to_trade_list(
    fires: pd.DataFrame,
    *,
    apply_dynamic_sizing: bool = True,
    initial_cash: float = GT_INITIAL_CASH_KRW,
    fee_rate: float = TRADING_FEE_RATE,
) -> list[dict[str, Any]]:
    """
    발화 → GT 모델 amount_krw가 채워진 trade dict (복리 배분).

    Args:
        fires: 체결 대상 발화.
        apply_dynamic_sizing: True면 GT tier 복리 배분.
        initial_cash: 시작 현금 (누적 복리).
        fee_rate: 수수료율.

    Returns:
        dt, action, price, amount_krw 키 dict 리스트.
    """
    if fires.empty:
        return []
    rows: list[dict[str, Any]] = []
    for _, r in sort_fires_chronological(fires).iterrows():
        rows.append(
            {
                "dt": str(r["dt"]),
                "action": r["side"],
                "price": float(r["close"]),
                "rule_id": r.get("rule_id", ""),
                "forward_ret_pct": float(r.get("forward_ret_pct", 0)),
            }
        )
    if apply_dynamic_sizing:
        attach_gt_model_amounts(
            rows,
            initial_cash=initial_cash,
            fee_rate=fee_rate,
        )
    return rows


def simulate_sized_portfolio(
    trades: list[dict[str, Any]],
    initial_cash: float = GT_INITIAL_CASH_KRW,
    fee_rate: float = TRADING_FEE_RATE,
    last_price: float | None = None,
    fallback_order_krw: float = LIVE_ORDER_KRW,
) -> dict[str, Any]:
    """
    trade.amount_krw(GT 모델·복리 배분) 기준 포트폴리오 시뮬 + MDD.

    Args:
        trades: 시간순 trade dict (amount_krw 권장).
        initial_cash: 시작 현금.
        fee_rate: 수수료율.
        last_price: 미청산 평가 종가.
        fallback_order_krw: amount_krw 없을 때 1회 금액.

    Returns:
        simulate_truth_portfolio와 동일 키 + max_drawdown_pct.
    """
    if trades and not any(float(t.get("amount_krw") or 0) > 0 for t in trades):
        attach_gt_model_amounts(trades, initial_cash=initial_cash, fee_rate=fee_rate)
    result = simulate_portfolio_summary(
        trades,
        initial_cash=initial_cash,
        fee_rate=fee_rate,
        last_price=last_price,
        use_amount_krw=True,
    )
    result["sizing_mode"] = (
        "gt_model_compound_causal" if GT_SIGNAL_CAUSAL else "gt_model_compound"
    )
    result["sizing_note"] = (
        "전기간 복리·GT tier·총자산×비중, 보유현금 한도; "
        + ("인과적 신호·tier(미래 미사용)" if GT_SIGNAL_CAUSAL else "상한 없음")
    )
    return result


def simulate_fixed_order_portfolio(
    trades: list[dict[str, Any]],
    order_krw: float = LIVE_ORDER_KRW,
    initial_cash: float = GT_INITIAL_CASH_KRW,
    fee_rate: float = TRADING_FEE_RATE,
    last_price: float | None = None,
    sizing_mode: str = "fixed",
) -> dict[str, Any]:
    """
    포트폴리오 시뮬 (고정 원화 또는 trade.amount_krw).

    Args:
        trades: 시간순 {dt, action, price, amount_krw?}.
        order_krw: sizing_mode=fixed 일 때 1회 금액(원).
        initial_cash: 시작 현금.
        fee_rate: 수수료율.
        last_price: 미청산 평가 종가.
        sizing_mode: 'fixed' | 'amount_krw'.

    Returns:
        simulate_truth_portfolio와 동일 키 구조.
    """
    cash = float(initial_cash)
    qty = 0.0
    total_fees = 0.0
    last_trade_price = last_price
    order = float(order_krw)

    for t in sorted(trades, key=lambda x: x["dt"]):
        action = t["action"]
        price = float(t["price"])
        if price <= 0:
            continue
        last_trade_price = price

        if action == "buy":
            planned = _planned_order_krw(t, order, sizing_mode)
            amount = min(planned, max(cash / (1.0 + fee_rate), 0.0))
            if amount <= 0:
                continue
            fee = amount * fee_rate
            cash -= amount + fee
            total_fees += fee
            qty += amount / price

        elif action == "sell" and qty > 0:
            planned = _planned_order_krw(t, order, sizing_mode)
            if planned >= qty * price * 0.999:
                sell_qty = qty
            else:
                sell_qty = min(qty, planned / price)
            if sell_qty <= 0:
                continue
            gross = sell_qty * price
            fee = gross * fee_rate
            cash += gross - fee
            total_fees += fee
            qty -= sell_qty
            if qty < 1e-12:
                qty = 0.0

    mark_price = float(last_price if last_price is not None else last_trade_price or 0)
    holding_value = qty * mark_price
    final_asset = cash + holding_value
    pnl_krw = final_asset - initial_cash
    pnl_pct = pnl_krw / initial_cash * 100.0 if initial_cash else 0.0

    return {
        "initial_cash_krw": round(initial_cash, 0),
        "final_asset_krw": round(final_asset, 0),
        "pnl_krw": round(pnl_krw, 0),
        "pnl_pct": round(pnl_pct, 2),
        "total_fees_krw": round(total_fees, 0),
        "cash_krw": round(cash, 0),
        "holding_qty": round(qty, 6),
        "holding_value_krw": round(holding_value, 0),
        "mark_price": round(mark_price, 2),
        "fee_rate": fee_rate,
        "order_krw": round(order, 0),
        "sizing_mode": sizing_mode,
        "trade_count": len(trades),
    }


def simulate_fixed_order_portfolio_steps(
    trades: list[dict[str, Any]],
    order_krw: float = LIVE_ORDER_KRW,
    initial_cash: float = GT_INITIAL_CASH_KRW,
    fee_rate: float = TRADING_FEE_RATE,
    sizing_mode: str = "fixed",
) -> list[dict[str, Any]]:
    """
    체결마다 현금·보유·총평가 스냅샷 (GT 테이블용).

    Args:
        trades: 시간순 trade dict.
        order_krw: 1회 체결 원화.
        initial_cash: 시작 현금.
        fee_rate: 수수료율.
        sizing_mode: fixed | amount_krw.

    Returns:
        step dict 리스트.
    """
    cash = float(initial_cash)
    qty = 0.0
    order = float(order_krw)
    steps: list[dict[str, Any]] = []

    for t in sorted(trades, key=lambda x: x["dt"]):
        action = t["action"]
        price = float(t["price"])
        if price <= 0:
            continue

        if action == "buy":
            planned = _planned_order_krw(t, order, sizing_mode)
            amount = min(planned, max(cash / (1.0 + fee_rate), 0.0))
            if amount <= 0:
                continue
            fee = amount * fee_rate
            cash -= amount + fee
            qty += amount / price

        elif action == "sell" and qty > 0:
            planned = _planned_order_krw(t, order, sizing_mode)
            if planned >= qty * price * 0.999:
                sell_qty = qty
            else:
                sell_qty = min(qty, planned / price)
            if sell_qty <= 0:
                continue
            gross = sell_qty * price
            fee = gross * fee_rate
            cash += gross - fee
            qty -= sell_qty
            if qty < 1e-12:
                qty = 0.0

        steps.append(
            {
                "dt": t["dt"],
                "action": action,
                "price": price,
                "rule_id": t.get("rule_id", ""),
                "forward_ret_pct": t.get("forward_ret_pct"),
                "amount_krw": t.get("amount_krw"),
                "cash_krw": round(cash, 0),
                "holding_qty": round(qty, 4),
                "total_asset_krw": round(cash + qty * price, 0),
            }
        )
    return steps
