from __future__ import annotations

from decimal import Decimal

from src.execution.order_intent import OrderIntent
from src.execution.paper_lifecycle import PaperCandle, simulate_paper_lifecycle


def _intent(stop: str = "98", take_profit: str | None = "103") -> OrderIntent:
    return OrderIntent(
        strategy_id="candidate_v76_strict_live_candidate",
        symbol="BTC/USDC:USDC",
        coin="BTC",
        side="buy",
        reduce_only=False,
        order_type="market",
        tif="Ioc",
        size=Decimal("0.1"),
        price=None,
        trigger_price=None,
        stop_loss=Decimal(stop),
        take_profit=Decimal(take_profit) if take_profit is not None else None,
        client_order_id="paper-test",
        reason="confirmed_squeeze_breakout",
        risk_usd=Decimal("0.20"),
        estimated_notional_usd=Decimal("10"),
    )


def test_lifecycle_stop_loss_records_mfe_mae_and_never_live_flags() -> None:
    result = simulate_paper_lifecycle(
        _intent(stop="98", take_profit="105"),
        entry_price=Decimal("100"),
        candles=[
            PaperCandle(open=Decimal("100"), high=Decimal("101"), low=Decimal("99"), close=Decimal("100")),
            PaperCandle(open=Decimal("100"), high=Decimal("100.5"), low=Decimal("97.5"), close=Decimal("98")),
        ],
        fee_rate=Decimal("0.001"),
        slippage_pct=Decimal("0.05"),
    )

    assert result.exit_reason == "stop_loss"
    assert result.exit_price == Decimal("98")
    assert result.mfe_pct == Decimal("1.00")
    assert result.mae_pct == Decimal("-2.50")
    assert result.net_pnl_usd < 0
    assert result.live_order_allowed is False
    assert result.mainnet_signed_action is False


def test_lifecycle_take_profit_before_time_exit() -> None:
    result = simulate_paper_lifecycle(
        _intent(stop="98", take_profit="103"),
        entry_price=Decimal("100"),
        candles=[PaperCandle(open=Decimal("100"), high=Decimal("103.1"), low=Decimal("99.5"), close=Decimal("102"))],
        fee_rate=Decimal("0"),
        slippage_pct=Decimal("0"),
    )

    assert result.exit_reason == "take_profit"
    assert result.exit_price == Decimal("103")
    assert result.gross_pnl_usd == Decimal("0.3")


def test_lifecycle_trailing_stop_moves_up_and_exits() -> None:
    result = simulate_paper_lifecycle(
        _intent(stop="98", take_profit=None),
        entry_price=Decimal("100"),
        candles=[
            PaperCandle(open=Decimal("100"), high=Decimal("104"), low=Decimal("100"), close=Decimal("103")),
            PaperCandle(open=Decimal("103"), high=Decimal("103.5"), low=Decimal("101.5"), close=Decimal("102")),
        ],
        trailing_stop_pct=Decimal("2"),
        fee_rate=Decimal("0"),
        slippage_pct=Decimal("0"),
    )

    assert result.exit_reason == "trailing_stop"
    assert result.exit_price == Decimal("101.92")
    assert result.net_pnl_usd > 0


def test_lifecycle_time_exit_and_funding_cost() -> None:
    result = simulate_paper_lifecycle(
        _intent(stop="95", take_profit="110"),
        entry_price=Decimal("100"),
        candles=[PaperCandle(open=Decimal("100"), high=Decimal("101"), low=Decimal("99"), close=Decimal("100.5"))],
        hold_hours_per_candle=Decimal("2"),
        funding_rate_hourly_pct=Decimal("0.01"),
        fee_rate=Decimal("0"),
        slippage_pct=Decimal("0"),
    )

    assert result.exit_reason == "time_exit"
    assert result.exit_price == Decimal("100.5")
    assert result.funding_cost_usd == Decimal("0.0020")
    assert result.net_pnl_usd == result.gross_pnl_usd - result.funding_cost_usd


def test_lifecycle_partial_fill_caps_position_size_and_journal_fields() -> None:
    result = simulate_paper_lifecycle(
        _intent(stop="95", take_profit="110"),
        entry_price=Decimal("100"),
        candles=[PaperCandle(open=Decimal("100"), high=Decimal("101"), low=Decimal("99"), close=Decimal("100.5"))],
        max_fill_notional_usd=Decimal("4"),
        fee_rate=Decimal("0"),
        slippage_pct=Decimal("0"),
    )

    assert result.requested_size == Decimal("0.10000000")
    assert result.filled_size == Decimal("0.04000000")
    assert result.unfilled_size == Decimal("0.06000000")
    assert result.fill_ratio == Decimal("0.4000")
    row = result.to_journal_row()
    assert row["requested_size"] == "0.10000000"
    assert row["filled_size"] == "0.04000000"
    assert row["unfilled_size"] == "0.06000000"


def test_lifecycle_maker_taker_fee_classes_and_depth_slippage() -> None:
    result = simulate_paper_lifecycle(
        _intent(stop="95", take_profit="101"),
        entry_price=Decimal("100"),
        candles=[PaperCandle(open=Decimal("100"), high=Decimal("101.1"), low=Decimal("99"), close=Decimal("101"))],
        maker_fee_rate=Decimal("0.0001"),
        taker_fee_rate=Decimal("0.001"),
        entry_liquidity="maker",
        exit_liquidity="taker",
        depth_slippage_pct=Decimal("0.10"),
    )

    assert result.exit_reason == "take_profit"
    assert result.entry_fee_usd == Decimal("0.00100000")
    assert result.exit_fee_usd == Decimal("0.01010000")
    assert result.entry_slippage_pct == Decimal("0")
    assert result.exit_slippage_pct == Decimal("0.10")
    assert result.slippage_cost_usd == Decimal("0.01010000")


def test_lifecycle_stop_gap_slippage_exits_below_stop() -> None:
    result = simulate_paper_lifecycle(
        _intent(stop="98", take_profit="105"),
        entry_price=Decimal("100"),
        candles=[PaperCandle(open=Decimal("97"), high=Decimal("97.5"), low=Decimal("96"), close=Decimal("97.5"))],
        fee_rate=Decimal("0"),
        slippage_pct=Decimal("0"),
        stop_gap_slippage_pct=Decimal("0.50"),
    )

    assert result.exit_reason == "stop_gap_slippage"
    assert result.exit_price == Decimal("96.51500000")
    assert result.stop_gap_slippage_pct == Decimal("0.50")
    assert result.net_pnl_usd < Decimal("-0.30")
