from decimal import Decimal
from typing import Literal

from src.execution.order_intent import OrderIntent
from src.strategies.global_strategy_policy import (
    CopyObservationEvidence,
    GlobalStrategyContext,
    MarketRegimeContext,
    PromotionEvidence,
    StrategyCandidate,
    evaluate_global_strategy,
)


def make_intent(side: Literal["buy", "sell"] = "buy", strategy_id="candidate_v76_strict_live_candidate"):
    return OrderIntent(
        strategy_id=strategy_id,
        symbol="BTC/USDC:USDC",
        coin="BTC",
        side=side,
        reduce_only=False,
        order_type="market",
        tif="Ioc",
        size=Decimal("0.001"),
        price=None,
        trigger_price=None,
        stop_loss=Decimal("99000"),
        take_profit=None,
        client_order_id="test-1",
        reason="confirmed_retest",
        risk_usd=Decimal("0.10"),
        estimated_notional_usd=Decimal("10"),
    )


def passing_promotion(**overrides):
    data = dict(
        closed_trades=35,
        total_net_pnl=Decimal("4.2"),
        last_20_net_pnl=Decimal("1.1"),
        win_rate_pct=Decimal("48"),
        profit_factor=Decimal("1.31"),
        top_coin_share_pct=Decimal("25"),
        short_scalp_share_pct=Decimal("10"),
        max_drawdown_usd=Decimal("1.2"),
        replay_only=False,
        simulator_sanity_ok=True,
    )
    data.update(overrides)
    return PromotionEvidence(**data)


def base_context(**overrides):
    data = dict(
        market_regime=MarketRegimeContext(btc_trend="bullish", eth_trend="bullish", choppy=False),
        copy_observation=CopyObservationEvidence(cycles_completed=69, expected_cycles=84, allowed_signals=0, shadow_decisions=2157, data_quality_ok=True),
        reconcile_clean=True,
        stops_confirmed=True,
        alerts_confirmed=True,
        kill_switch_active=False,
        live_entries_blocked=True,
    )
    data.update(overrides)
    return GlobalStrategyContext(**data)


def test_global_policy_blocks_copy_executor_when_observation_has_zero_allowed_signals():
    candidate = StrategyCandidate(
        strategy_id="copy_direct_executor",
        family="copy",
        intent=make_intent(strategy_id="copy_direct_executor"),
        promotion=passing_promotion(),
    )

    decision = evaluate_global_strategy(candidate, base_context())

    assert decision.allowed is False
    assert decision.status == "research"
    assert "copy_observation_has_no_allowed_signals" in decision.reasons
    assert decision.recommended_mode == "copy_research_only"


def test_global_policy_blocks_long_trade_in_bearish_or_choppy_regime():
    candidate = StrategyCandidate(
        strategy_id="candidate_v76_strict_live_candidate",
        family="v76_anti_chase",
        intent=make_intent(side="buy"),
        promotion=passing_promotion(),
    )

    bearish = base_context(market_regime=MarketRegimeContext(btc_trend="bearish", eth_trend="bearish", choppy=False))
    choppy = base_context(market_regime=MarketRegimeContext(btc_trend="mixed", eth_trend="bullish", choppy=True))

    assert "market_regime_blocks_long" in evaluate_global_strategy(candidate, bearish).reasons
    assert "market_regime_choppy_no_trade" in evaluate_global_strategy(candidate, choppy).reasons


def test_global_policy_requires_cost_hurdle_and_anti_chase_confirmation():
    candidate = StrategyCandidate(
        strategy_id="candidate_v76_strict_live_candidate",
        family="v76_anti_chase",
        intent=make_intent(),
        promotion=passing_promotion(),
        expected_move_vs_cost=Decimal("2.9"),
        anti_chase_ok=False,
        retest_confirmed=False,
    )

    decision = evaluate_global_strategy(candidate, base_context())

    assert decision.allowed is False
    assert "expected_move_below_cost_hurdle" in decision.reasons
    assert "anti_chase_not_confirmed" in decision.reasons
    assert "retest_not_confirmed" in decision.reasons


def test_global_policy_blocks_live_until_promotion_and_safety_gates_are_green():
    candidate = StrategyCandidate(
        strategy_id="candidate_v76_strict_live_candidate",
        family="v76_anti_chase",
        intent=make_intent(),
        promotion=passing_promotion(closed_trades=12, total_net_pnl=Decimal("-0.1"), replay_only=True),
        expected_move_vs_cost=Decimal("4.5"),
        anti_chase_ok=True,
        retest_confirmed=True,
    )
    context = base_context(reconcile_clean=False, stops_confirmed=False, alerts_confirmed=False)

    decision = evaluate_global_strategy(candidate, context)

    assert decision.allowed is False
    assert "sample_too_small" in decision.reasons
    assert "paper_total_pnl_not_positive" in decision.reasons
    assert "replay_only_not_promotable" in decision.reasons
    assert "reconcile_not_clean" in decision.reasons
    assert "stops_not_confirmed" in decision.reasons
    assert "alerts_not_confirmed" in decision.reasons


def test_global_policy_allows_tiny_live_preview_only_when_every_gate_is_green():
    candidate = StrategyCandidate(
        strategy_id="candidate_v76_strict_live_candidate",
        family="v76_anti_chase",
        intent=make_intent(),
        promotion=passing_promotion(),
        expected_move_vs_cost=Decimal("4.5"),
        anti_chase_ok=True,
        retest_confirmed=True,
        max_effective_leverage=Decimal("1"),
        max_open_positions=1,
    )

    decision = evaluate_global_strategy(candidate, base_context())

    assert decision.allowed is True
    assert decision.status == "tiny_live_preview_ready"
    assert decision.recommended_mode == "tiny_live_preview"
    assert decision.risk_limits["max_effective_leverage"] == "1"
    assert decision.risk_limits["max_open_positions"] == 1
