"""CLI tests for the discovery review flow."""

from __future__ import annotations

from pathlib import Path

import pytest
import yaml
from compiler.sources.discover import SourceCandidate
from typer.testing import CliRunner

from compiler import cli

runner = CliRunner()


def test_cli_repo_root_points_at_project_root():
    assert cli.REPO_ROOT.name == "MentorCore"
    assert (cli.REPO_ROOT / "sources").is_dir()


def test_compile_propose_writes_review_yaml(monkeypatch, tmp_path):
    async def fake_discover(name: str):
        assert name == "Ada Lovelace"
        return [
            SourceCandidate(
                source_type="article",
                title="Wikipedia: Ada Lovelace",
                origin_url="https://en.wikipedia.org/wiki/Ada_Lovelace",
                fetch_type="wikipedia",
                source_id="wikipedia-ada-lovelace",
                fetch_config={"wikipedia_title": "Ada Lovelace"},
            )
        ]

    monkeypatch.setattr("compiler.sources.discover.discover", fake_discover)
    sources_path = tmp_path / "ada-lovelace.yaml"

    result = runner.invoke(
        cli.app,
        ["compile", "Ada Lovelace", "--sources", str(sources_path), "--propose"],
    )

    assert result.exit_code == 0, result.output
    data = yaml.safe_load(sources_path.read_text())
    assert data["slug"] == "ada-lovelace"
    assert data["display_name"] == "Ada Lovelace"
    assert data["voice_policy"] == "generated"
    assert data["sources"][0]["approved"] is False
    assert data["sources"][0]["fetch_type"] == "wikipedia"
    assert data["sources"][0]["wikipedia_title"] == "Ada Lovelace"
    assert "mentor compile ada-lovelace --build" in result.output


def test_compile_build_uses_only_approved_sources(monkeypatch, tmp_path):
    received: dict[str, object] = {}

    async def fake_compile_once(slug: str, sources_config: dict, from_stage_idx: int = 0):
        received["slug"] = slug
        received["sources_config"] = sources_config
        received["from_stage_idx"] = from_stage_idx

    monkeypatch.setattr(cli, "_compile_once", fake_compile_once)
    sources_path = _write_sources_yaml(tmp_path)

    result = runner.invoke(
        cli.app,
        ["compile", "ada-lovelace", "--sources", str(sources_path), "--build"],
    )

    assert result.exit_code == 0, result.output
    assert received["slug"] == "ada-lovelace"
    assert received["from_stage_idx"] == 0
    config = received["sources_config"]
    assert isinstance(config, dict)
    assert [s["id"] for s in config["sources"]] == ["approved-book"]


def test_compile_build_requires_approved_sources(tmp_path):
    sources_path = _write_sources_yaml(tmp_path, approved=False)

    result = runner.invoke(
        cli.app,
        ["compile", "ada-lovelace", "--sources", str(sources_path), "--build"],
    )

    assert result.exit_code == 1
    assert "No approved sources found" in result.output


@pytest.mark.asyncio
async def test_compile_loop_stops_when_thresholds_pass(monkeypatch):
    calls = []

    async def fake_compile_once(slug: str, sources_config: dict):
        calls.append(slug)
        return {
            "groundedness": 0.72,
            "style_match": 0.64,
            "misattribution": 0,
            "passed": True,
        }

    monkeypatch.setattr(cli, "_compile_once", fake_compile_once)
    monkeypatch.setattr(cli, "_best_existing_eval", lambda slug: None)

    await cli._compile_loop("ada-lovelace", {"sources": []}, budget=3)

    assert calls == ["ada-lovelace"]


@pytest.mark.asyncio
async def test_compile_loop_stops_on_plateau(monkeypatch):
    calls = []

    async def fake_compile_once(slug: str, sources_config: dict):
        calls.append(slug)
        return {
            "groundedness": 0.51,
            "style_match": 0.41,
            "misattribution": 0,
            "passed": False,
        }

    monkeypatch.setattr(cli, "_compile_once", fake_compile_once)
    monkeypatch.setattr(
        cli,
        "_best_existing_eval",
        lambda slug: {"groundedness": 0.5, "style_match": 0.4, "misattribution": 0},
    )

    await cli._compile_loop("ada-lovelace", {"sources": []}, budget=5)

    assert len(calls) == 3


@pytest.mark.asyncio
async def test_compile_loop_uses_improved_config_between_iterations(monkeypatch):
    seen_configs = []

    async def fake_compile_once(slug: str, sources_config: dict):
        seen_configs.append(sources_config)
        return {
            "groundedness": 0.6,
            "style_match": 0.5,
            "misattribution": 0,
            "passed": False,
        }

    async def fake_improve_sources_config(
        sources_config: dict,
        eval_result: dict,
        *,
        iteration: int,
    ):
        updated = dict(sources_config)
        updated["improved_at"] = iteration
        return updated, ["test action"]

    monkeypatch.setattr(cli, "_compile_once", fake_compile_once)
    monkeypatch.setattr(cli, "_best_existing_eval", lambda slug: None)
    monkeypatch.setattr(
        "compiler.stages.improve.improve_sources_config",
        fake_improve_sources_config,
    )

    await cli._compile_loop("ada-lovelace", {"sources": []}, budget=2)

    assert seen_configs[0] == {"sources": []}
    assert seen_configs[1]["improved_at"] == 1


@pytest.mark.asyncio
async def test_compile_loop_rolls_back_on_regression(monkeypatch):
    rolled_back = []

    async def fake_compile_once(slug: str, sources_config: dict):
        return {
            "groundedness": 0.7,
            "style_match": 0.5,
            "misattribution": 0,
            "passed": False,
        }

    monkeypatch.setattr(cli, "_compile_once", fake_compile_once)
    monkeypatch.setattr(
        cli,
        "_best_existing_eval",
        lambda slug: {"groundedness": 0.8, "style_match": 0.7, "misattribution": 0},
    )
    monkeypatch.setattr(cli, "_rollback_current", lambda slug: rolled_back.append(slug))

    with pytest.raises(RuntimeError, match="regressed"):
        await cli._compile_loop("ada-lovelace", {"sources": []}, budget=3)

    assert rolled_back == ["ada-lovelace"]


def test_best_existing_eval_reads_current_history(monkeypatch, tmp_path):
    root = tmp_path / "personas"
    history = root / "ada-lovelace" / "current" / "eval" / "history.jsonl"
    history.parent.mkdir(parents=True)
    history.write_text(
        "\n".join(
            [
                '{"groundedness": 0.4, "style_match": 0.8, "misattribution": 0}',
                '{"groundedness": 0.7, "style_match": 0.6, "misattribution": 0}',
            ]
        )
        + "\n"
    )
    monkeypatch.setenv("MENTORCORE_PERSONAS_ROOT", str(root))

    result = cli._best_existing_eval("ada-lovelace")

    assert result is not None
    assert result["groundedness"] == 0.7
    assert result["style_match"] == 0.6


def test_write_eval_to_manifest_updates_fidelity(tmp_path):
    manifest_path = tmp_path / "persona.yaml"
    manifest_path.write_text(
        yaml.dump(
            {
                "schema_version": 1,
                "slug": "ada-lovelace",
                "display_name": "Ada Lovelace",
                "fidelity": {"groundedness": 0.0, "style_match": 0.0, "last_eval": None},
            }
        )
    )

    cli._write_eval_to_manifest(
        tmp_path,
        {
            "timestamp": "2026-06-11T00:00:00+00:00",
            "groundedness": 0.82,
            "style_match": 0.71,
        },
    )

    data = yaml.safe_load(manifest_path.read_text())
    assert data["fidelity"]["groundedness"] == 0.82
    assert data["fidelity"]["style_match"] == 0.71
    assert data["fidelity"]["last_eval"] == "2026-06-11T00:00:00+00:00"


def test_fake_compiler_acquire_all_writes_text_fixtures(monkeypatch, tmp_path):
    monkeypatch.setenv("FAKE_COMPILER", "1")

    def should_not_call_real_acquire(sources, raw_dir):
        raise AssertionError("real acquisition should not run in fake compiler mode")

    results = cli._compiler_acquire_all(
        [
            {"id": "book-src", "fetch_type": "gutenberg", "title": "Book Source"},
            {
                "id": "wiki-src",
                "fetch_type": "wikipedia",
                "wikipedia_title": "Wiki Source",
            },
            {"id": "url-src", "fetch_type": "url", "title": "URL Source"},
        ],
        tmp_path / "raw",
        should_not_call_real_acquire,
    )

    assert results["book-src"].suffix == ".txt"
    assert results["wiki-src"].suffix == ".json"
    assert results["url-src"].suffix == ".html"
    assert "Book Source" in results["book-src"].read_text()
    assert "Wiki Source" in results["wiki-src"].read_text()
    assert "URL Source" in results["url-src"].read_text()


def _write_sources_yaml(tmp_path: Path, *, approved: bool = True) -> Path:
    path = tmp_path / "ada-lovelace.yaml"
    path.write_text(
        yaml.dump(
            {
                "slug": "ada-lovelace",
                "display_name": "Ada Lovelace",
                "sources": [
                    {
                        "id": "approved-book",
                        "type": "book",
                        "title": "Approved",
                        "rights": "public-domain",
                        "fetch_type": "url",
                        "url": "https://example.com/approved",
                        "approved": approved,
                    },
                    {
                        "id": "rejected-book",
                        "type": "book",
                        "title": "Rejected",
                        "rights": "fair-use-excerpt",
                        "fetch_type": "url",
                        "url": "https://example.com/rejected",
                        "approved": False,
                    },
                ],
            }
        )
    )
    return path
