from __future__ import annotations

from config import BotConfig
from strategy import (
    should_enter_by_strategy_family,
    should_enter_bollinger_rsi_mean_reversion,
    should_enter_confirmed_squeeze_breakout,
    should_enter_hybrid_survival_squeeze,
    should_enter_multi_day_trend_investment,
    should_enter_relative_strength_rotation,
    should_enter_trend_pullback_sma_vwap,
)
from strategy_registry import load_strategy_presets, preset_to_config


def _history(values: list[float]) -> list[tuple[float, float]]:
    return [(1_700_000_000 + i * 60, value) for i, value in enumerate(values)]


def test_trend_pullback_enters_uptrend_reclaim_near_vwap() -> None:
    cfg = BotConfig(
        strategy_family="trend_pullback_sma_vwap",
        min_volume_24h=1,
        trend_pullback_min_rsi=35.0,
        trend_pullback_max_rsi=70.0,
        trend_pullback_max_distance_pct=1.5,
    )

    decision = should_enter_trend_pullback_sma_vwap(
        coin="SOL",
        history=_history([100, 102, 104, 106, 108, 110, 106, 104, 105]),
        current_price=105,
        volume_24h=100_000_000,
        open_positions=set(),
        cooldowns={},
        now_ts=1_700_001_000,
        cfg=cfg,
    )

    assert decision.enter is True
    assert decision.reason == "trend_pullback_sma_vwap"


def test_bollinger_rsi_mean_reversion_enters_lower_band_rebound() -> None:
    cfg = BotConfig(
        strategy_family="bollinger_rsi_mean_reversion",
        min_volume_24h=1,
        mean_reversion_max_rsi=35.0,
        mean_reversion_band_tolerance_pct=0.3,
    )

    decision = should_enter_bollinger_rsi_mean_reversion(
        coin="ETH",
        history=_history([100, 100, 99.8, 100.1, 99.9, 100, 99.7, 96.0, 96.8]),
        current_price=96.8,
        volume_24h=100_000_000,
        open_positions=set(),
        cooldowns={},
        now_ts=1_700_001_000,
        cfg=cfg,
    )

    assert decision.enter is True
    assert decision.reason == "bollinger_rsi_mean_reversion"


def test_confirmed_squeeze_breakout_requires_breakout_and_trend_confirmation() -> None:
    cfg = BotConfig(
        strategy_family="confirmed_squeeze_breakout",
        min_volume_24h=1,
        squeeze_lookback_ticks=5,
        squeeze_max_band_width_pct=1.5,
        squeeze_breakout_pct=0.35,
    )

    decision = should_enter_confirmed_squeeze_breakout(
        coin="HYPE",
        history=_history([100.0, 100.1, 99.95, 100.05, 100.0, 100.7, 101.2]),
        current_price=101.2,
        volume_24h=100_000_000,
        open_positions=set(),
        cooldowns={},
        now_ts=1_700_001_000,
        cfg=cfg,
    )

    assert decision.enter is True
    assert decision.reason == "confirmed_squeeze_breakout"


def test_hybrid_survival_squeeze_prefers_confirmed_squeeze_entry() -> None:
    cfg = BotConfig(
        strategy_family="hybrid_survival_squeeze",
        min_volume_24h=1,
        squeeze_lookback_ticks=5,
        squeeze_max_band_width_pct=1.5,
        squeeze_breakout_pct=0.35,
        allowed_coins=("WLD", "SUI", "ENA", "BTC", "ETH"),
    )

    decision = should_enter_hybrid_survival_squeeze(
        coin="WLD",
        history=_history([100.0, 100.1, 99.95, 100.05, 100.0, 100.7, 101.2]),
        current_price=101.2,
        volume_24h=100_000_000,
        baseline_price=100.0,
        open_positions=set(),
        cooldowns={},
        now_ts=1_700_001_000,
        cfg=cfg,
    )

    assert decision.enter is True
    assert decision.reason == "hybrid_squeeze_breakout"


def test_hybrid_survival_squeeze_rejects_flash_crash_without_rebound_reclaim() -> None:
    cfg = BotConfig(
        strategy_family="hybrid_survival_squeeze",
        min_volume_24h=1,
        flash_crash_trigger_pct=4.0,
        crash_roe_trigger_pct=8.0,
        default_leverage=3,
        allowed_coins=("WLD", "SUI", "ENA", "BTC", "ETH"),
    )

    decision = should_enter_hybrid_survival_squeeze(
        coin="ENA",
        history=_history([110, 108, 106, 104, 102, 100, 98, 96, 94, 93]),
        current_price=93,
        volume_24h=100_000_000,
        baseline_price=120.0,
        open_positions=set(),
        cooldowns={},
        now_ts=1_700_001_000,
        cfg=cfg,
    )

    assert decision.enter is False
    assert decision.reason == "hybrid_flash_not_reclaiming_sma"


def test_hybrid_survival_squeeze_allows_flash_crash_after_rebound_reclaim() -> None:
    cfg = BotConfig(
        strategy_family="hybrid_survival_squeeze",
        min_volume_24h=1,
        flash_crash_trigger_pct=4.0,
        crash_roe_trigger_pct=8.0,
        default_leverage=3,
        allowed_coins=("WLD", "SUI", "ENA", "BTC", "ETH"),
    )

    decision = should_enter_hybrid_survival_squeeze(
        coin="SUI",
        history=_history([110, 109, 108, 107, 106, 100, 96, 94, 95, 97]),
        current_price=97,
        volume_24h=100_000_000,
        baseline_price=120.0,
        open_positions=set(),
        cooldowns={},
        now_ts=1_700_001_000,
        cfg=cfg,
    )

    assert decision.enter is True
    assert decision.reason == "hybrid_survival_rebound"


def test_hybrid_survival_squeeze_rejects_coins_outside_quality_whitelist() -> None:
    cfg = BotConfig(
        strategy_family="hybrid_survival_squeeze",
        min_volume_24h=1,
        allowed_coins=("WLD", "SUI", "ENA", "BTC", "ETH"),
    )

    decision = should_enter_hybrid_survival_squeeze(
        coin="DOGE",
        history=_history([100.0, 100.1, 99.95, 100.05, 100.0, 100.7, 101.2]),
        current_price=101.2,
        volume_24h=100_000_000,
        baseline_price=100.0,
        open_positions=set(),
        cooldowns={},
        now_ts=1_700_001_000,
        cfg=cfg,
    )

    assert decision.enter is False
    assert decision.reason == "not_allowed_coin"


def test_multi_day_trend_investment_enters_slow_trend_pullback() -> None:
    cfg = BotConfig(
        strategy_family="multi_day_trend_investment",
        min_volume_24h=1,
        multi_day_min_trend_pct=2.0,
        multi_day_max_pullback_distance_pct=3.0,
    )

    decision = should_enter_multi_day_trend_investment(
        coin="BTC",
        history=_history([90, 92, 94, 96, 98, 100, 103, 106, 109, 112, 110, 111]),
        current_price=111,
        volume_24h=100_000_000,
        open_positions=set(),
        cooldowns={},
        now_ts=1_700_001_000,
        cfg=cfg,
    )

    assert decision.enter is True
    assert decision.reason == "multi_day_trend_investment"


def test_relative_strength_rotation_enters_top_ranked_coin_when_market_breadth_is_positive() -> None:
    cfg = BotConfig(
        strategy_family="relative_strength_rotation",
        min_volume_24h=1,
        relative_strength_lookback_ticks=4,
        relative_strength_top_n=2,
        relative_strength_min_momentum_pct=3.0,
        relative_strength_min_positive_candidates=2,
    )
    histories = {
        "SOL": _history([100, 102, 104, 106, 108]),
        "FET": _history([100, 101, 103, 105, 106]),
        "BTC": _history([100, 100, 101, 100, 101]),
    }

    decision = should_enter_relative_strength_rotation(
        coin="SOL",
        history=histories["SOL"],
        current_price=108,
        volume_24h=100_000_000,
        open_positions=set(),
        cooldowns={},
        now_ts=1_700_001_000,
        cfg=cfg,
        market_histories=histories,
    )

    assert decision.enter is True
    assert decision.reason == "relative_strength_rotation"


def test_relative_strength_rotation_rejects_when_market_breadth_is_thin() -> None:
    cfg = BotConfig(
        strategy_family="relative_strength_rotation",
        min_volume_24h=1,
        relative_strength_lookback_ticks=4,
        relative_strength_top_n=2,
        relative_strength_min_momentum_pct=3.0,
        relative_strength_min_positive_candidates=2,
    )
    histories = {
        "SOL": _history([100, 102, 104, 106, 108]),
        "BTC": _history([100, 99, 100, 99, 100]),
        "ETH": _history([100, 100, 99, 100, 99]),
    }

    decision = should_enter_by_strategy_family(
        coin="SOL",
        history=histories["SOL"],
        current_price=108,
        volume_24h=100_000_000,
        open_positions=set(),
        cooldowns={},
        now_ts=1_700_001_000,
        cfg=cfg,
        market_histories=histories,
    )

    assert decision.enter is False
    assert decision.reason == "market_breadth_too_thin"


def test_strategy_expansion_presets_are_registered_as_research_only() -> None:
    presets = load_strategy_presets()
    expected = {
        "candidate_v67_trend_pullback_sma_vwap": "trend_pullback_sma_vwap",
        "candidate_v68_bollinger_rsi_mean_reversion": "bollinger_rsi_mean_reversion",
        "candidate_v69_squeeze_breakout_confirmed": "confirmed_squeeze_breakout",
        "candidate_v73_multi_day_trend_investment": "multi_day_trend_investment",
        "candidate_v74_relative_strength_breadth_guard": "relative_strength_rotation",
        "candidate_v75_hybrid_survival_squeeze": "hybrid_survival_squeeze",
    }

    for strategy_id, family in expected.items():
        preset = presets[strategy_id]
        cfg = preset_to_config(preset)
        assert cfg.strategy_family == family
        assert "paper-only" in preset.tags
        assert "research-sampler" in preset.tags
        assert "not-champion" in preset.tags
        assert cfg.position_sizing_mode == "risk"
        assert cfg.wallet_equity_usdc == 500.0
        assert cfg.max_total_trades <= 2
