from __future__ import annotations

import json
from pathlib import Path

from config import BotConfig
from strategy_registry import StrategyPreset, build_candidate_presets, load_strategy_presets, preset_to_config
from strategy_tournament import (
    TournamentScore,
    build_experiment_paths,
    rank_strategies_from_journals,
    render_tournament_report,
)


def _write_jsonl(path: Path, rows: list[dict]) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text("\n".join(json.dumps(row) for row in rows) + "\n", encoding="utf-8")


def test_presets_load_git_history_and_new_candidates() -> None:
    presets = load_strategy_presets(Path("strategy_presets.json"))

    assert "v61_tight_survival" in presets
    assert "v60_survival" in presets
    assert "candidate_v62_anti_chop_context" in presets
    assert "candidate_v62_scalp_fast_rebound" in presets
    assert presets["v61_tight_survival"].parameters["dead_fish_time_limit_mins"] == 15
    assert presets["candidate_v62_anti_chop_context"].tags == ("candidate", "context-aware", "anti-chop")


def test_preset_to_config_overrides_base_without_mutating_base() -> None:
    base = BotConfig(flash_crash_trigger_pct=4.5, dead_fish_time_limit_mins=15)
    preset = StrategyPreset(
        strategy_id="test_strategy",
        label="Test Strategy",
        source="unit-test",
        parameters={"flash_crash_trigger_pct": 5.5, "dead_fish_time_limit_mins": 45},
    )

    cfg = preset_to_config(preset, base)

    assert cfg.flash_crash_trigger_pct == 5.5
    assert cfg.dead_fish_time_limit_mins == 45
    assert base.flash_crash_trigger_pct == 4.5
    assert base.dead_fish_time_limit_mins == 15


def test_build_candidate_presets_are_unique_and_bounded() -> None:
    candidates = build_candidate_presets()

    assert len(candidates) >= 6
    assert len({p.strategy_id for p in candidates}) == len(candidates)
    assert all(2.0 <= float(p.parameters["max_hard_stop_pct"]) <= 10.0 for p in candidates)
    assert all(float(p.parameters["trade_size_usd"]) <= 50.0 for p in candidates)


def test_build_experiment_paths_isolates_each_strategy(tmp_path: Path) -> None:
    paths = build_experiment_paths(tmp_path, "v61_tight_survival")

    assert paths.strategy_id == "v61_tight_survival"
    assert paths.root == tmp_path / "experiments" / "v61_tight_survival"
    assert paths.paper_state.name == "paper_state.json"
    assert paths.trade_journal == tmp_path / "experiments" / "v61_tight_survival" / "Tradeanalyse" / "trade_journal.jsonl"
    assert paths.metrics.name == "metrics.json"


def test_rank_strategies_scores_expectancy_and_drawdown(tmp_path: Path) -> None:
    good = tmp_path / "experiments" / "good" / "Tradeanalyse" / "trade_journal.jsonl"
    bad = tmp_path / "experiments" / "bad" / "Tradeanalyse" / "trade_journal.jsonl"
    _write_jsonl(good, [
        {"ts": "2026-01-01T00:00:00+00:00", "event_type": "entry", "coin": "BTC", "dry_run": False},
        {"ts": "2026-01-01T00:10:00+00:00", "event_type": "exit", "coin": "BTC", "dry_run": False, "realized_pnl_usd": 2.0, "reason": "trail"},
        {"ts": "2026-01-01T01:00:00+00:00", "event_type": "entry", "coin": "ETH", "dry_run": False},
        {"ts": "2026-01-01T01:10:00+00:00", "event_type": "exit", "coin": "ETH", "dry_run": False, "realized_pnl_usd": 1.0, "reason": "trail"},
    ])
    _write_jsonl(bad, [
        {"ts": "2026-01-01T00:00:00+00:00", "event_type": "entry", "coin": "BTC", "dry_run": False},
        {"ts": "2026-01-01T00:10:00+00:00", "event_type": "exit", "coin": "BTC", "dry_run": False, "realized_pnl_usd": -4.0, "reason": "SL"},
    ])

    rankings = rank_strategies_from_journals(tmp_path, ["bad", "good"], min_samples=2)

    assert [r.strategy_id for r in rankings] == ["good", "bad"]
    assert isinstance(rankings[0], TournamentScore)
    assert rankings[0].closed_trades == 2
    assert rankings[0].win_rate == 1.0
    assert rankings[0].total_pnl_usd == 3.0
    assert rankings[0].sample_warning is False
    assert rankings[1].sample_warning is True


def test_render_tournament_report_marks_small_samples(tmp_path: Path) -> None:
    journal = tmp_path / "experiments" / "tiny" / "Tradeanalyse" / "trade_journal.jsonl"
    _write_jsonl(journal, [
        {"ts": "2026-01-01T00:00:00+00:00", "event_type": "entry", "coin": "BTC", "dry_run": False},
        {"ts": "2026-01-01T00:10:00+00:00", "event_type": "exit", "coin": "BTC", "dry_run": False, "realized_pnl_usd": 1.0, "reason": "trail"},
    ])

    report = render_tournament_report(rank_strategies_from_journals(tmp_path, ["tiny"], min_samples=5))

    assert "Strategy Tournament Report" in report
    assert "tiny" in report
    assert "observation-only" in report
