"""Tests for ievo.commands.cortex — ievo cortex build command.

TDD: tests written before commands/cortex.py is implemented.
"""

from __future__ import annotations

from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest
from typer.testing import CliRunner

from ievo.cli import app

# ── Helpers ──────────────────────────────────────────────────


def make_fake_subprocess_run(tag: str = "v1.0.0", dist_dir: Path | None = None):
    """Return a subprocess.run mock that simulates git + build.py calls.

    The mock creates the expected output directory structure in dist_dir
    when build.py is called.
    """

    def _fake_run(cmd, **kwargs):
        result = MagicMock()
        result.returncode = 0
        result.stdout = ""
        result.stderr = ""

        cmd_str = " ".join(str(c) for c in cmd)

        if "git describe" in cmd_str or "git tag" in cmd_str:
            result.stdout = tag
        elif "build.py" in cmd_str and dist_dir is not None:
            # Simulate build.py creating the dist structure
            (dist_dir / "claude").mkdir(parents=True, exist_ok=True)
            (dist_dir / "claude" / "iEVO.md").write_text("# iEVO\n")
            (dist_dir / "codex").mkdir(parents=True, exist_ok=True)
            (dist_dir / "codex" / "BUILD_TARGET.md").write_text("# Codex\n")
        return result

    return _fake_run


runner = CliRunner()


# ── ievo cortex build ────────────────────────────────────────


def test_cortex_build_clones_and_builds(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """cortex build clones the Cortex repo and runs build.py."""
    monkeypatch.chdir(tmp_path)

    # Create a fake project
    (tmp_path / ".ievo").mkdir()
    (tmp_path / ".ievo" / "manifest.yaml").write_text(
        "project: test\nversion: 0.1.0\ncreated: '2026-01-01'\n"
    )

    cache_dir = tmp_path / ".cache" / "cortex"
    dist_dir = tmp_path / ".ievo" / "cortex"

    called_cmds: list[list[str]] = []

    def fake_run(cmd, **kwargs):
        called_cmds.append([str(c) for c in cmd])
        result = MagicMock()
        result.returncode = 0
        result.stdout = "v1.0.0"
        result.stderr = ""
        # Simulate build.py creating dist structure
        if "build.py" in " ".join(str(c) for c in cmd):
            dist_dir.mkdir(parents=True, exist_ok=True)
            (dist_dir / "claude").mkdir(exist_ok=True)
            (dist_dir / "claude" / "iEVO.md").write_text("# iEVO\n")
            (dist_dir / "codex").mkdir(exist_ok=True)
            (dist_dir / "codex" / "BUILD_TARGET.md").write_text("# Codex\n")
        return result

    with (
        patch("ievo.commands.cortex.subprocess.run", side_effect=fake_run),
        patch("ievo.commands.cortex.CORTEX_CACHE_DIR", cache_dir),
    ):
        result = runner.invoke(app, ["cortex", "build"])

    assert result.exit_code == 0, f"Command failed:\n{result.output}"

    # At least one git clone or git pull call should have been made
    git_calls = [c for c in called_cmds if c[0] == "git"]
    assert git_calls, f"No git commands were called. Called: {called_cmds}"

    # build.py should have been invoked
    build_calls = [c for c in called_cmds if "build.py" in " ".join(c)]
    assert build_calls, f"build.py not called. Called: {called_cmds}"


def test_cortex_build_uses_cached_clone(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """cortex build uses git pull (not git clone) when cache dir already exists."""
    monkeypatch.chdir(tmp_path)

    (tmp_path / ".ievo").mkdir()
    (tmp_path / ".ievo" / "manifest.yaml").write_text(
        "project: test\nversion: 0.1.0\ncreated: '2026-01-01'\n"
    )

    # Pre-create cache dir to simulate existing clone
    cache_dir = tmp_path / ".cache" / "cortex"
    cache_dir.mkdir(parents=True)
    (cache_dir / ".git").mkdir()  # mark as a git repo

    dist_dir = tmp_path / ".ievo" / "cortex"
    called_cmds: list[list[str]] = []

    def fake_run(cmd, **kwargs):
        called_cmds.append([str(c) for c in cmd])
        result = MagicMock()
        result.returncode = 0
        result.stdout = "v1.0.0"
        result.stderr = ""
        if "build.py" in " ".join(str(c) for c in cmd):
            dist_dir.mkdir(parents=True, exist_ok=True)
            (dist_dir / "claude").mkdir(exist_ok=True)
            (dist_dir / "claude" / "iEVO.md").write_text("# iEVO\n")
            (dist_dir / "codex").mkdir(exist_ok=True)
            (dist_dir / "codex" / "BUILD_TARGET.md").write_text("# Codex\n")
        return result

    with (
        patch("ievo.commands.cortex.subprocess.run", side_effect=fake_run),
        patch("ievo.commands.cortex.CORTEX_CACHE_DIR", cache_dir),
    ):
        result = runner.invoke(app, ["cortex", "build"])

    assert result.exit_code == 0, f"Command failed:\n{result.output}"

    # Should use git pull, not git clone
    clone_calls = [c for c in called_cmds if "clone" in c]
    pull_calls = [c for c in called_cmds if "pull" in c]
    assert not clone_calls, f"git clone called on existing cache: {clone_calls}"
    assert pull_calls, f"git pull not called. Called: {called_cmds}"


def test_cortex_build_output_structure(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """cortex build populates .ievo/cortex/ with claude/ and codex/ directories."""
    monkeypatch.chdir(tmp_path)

    (tmp_path / ".ievo").mkdir()
    (tmp_path / ".ievo" / "manifest.yaml").write_text(
        "project: test\nversion: 0.1.0\ncreated: '2026-01-01'\n"
    )

    cache_dir = tmp_path / ".cache" / "cortex"
    dist_dir = tmp_path / ".ievo" / "cortex"

    def fake_run(cmd, **kwargs):
        result = MagicMock()
        result.returncode = 0
        result.stdout = "v1.0.0"
        result.stderr = ""
        if "build.py" in " ".join(str(c) for c in cmd):
            dist_dir.mkdir(parents=True, exist_ok=True)
            (dist_dir / "claude").mkdir(exist_ok=True)
            (dist_dir / "claude" / "iEVO.md").write_text("# iEVO\n")
            (dist_dir / "codex").mkdir(exist_ok=True)
            (dist_dir / "codex" / "BUILD_TARGET.md").write_text("# Codex\n")
        return result

    with (
        patch("ievo.commands.cortex.subprocess.run", side_effect=fake_run),
        patch("ievo.commands.cortex.CORTEX_CACHE_DIR", cache_dir),
    ):
        runner.invoke(app, ["cortex", "build"])

    assert (tmp_path / ".ievo" / "cortex" / "claude").is_dir()
    assert (tmp_path / ".ievo" / "cortex" / "codex").is_dir()


def test_cortex_build_updates_version(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """cortex build writes .ievo/cortex-version with the built tag."""
    monkeypatch.chdir(tmp_path)

    (tmp_path / ".ievo").mkdir()
    (tmp_path / ".ievo" / "manifest.yaml").write_text(
        "project: test\nversion: 0.1.0\ncreated: '2026-01-01'\n"
    )

    cache_dir = tmp_path / ".cache" / "cortex"
    dist_dir = tmp_path / ".ievo" / "cortex"
    expected_tag = "v2.0.0"

    def fake_run(cmd, **kwargs):
        result = MagicMock()
        result.returncode = 0
        result.stdout = expected_tag
        result.stderr = ""
        if "build.py" in " ".join(str(c) for c in cmd):
            dist_dir.mkdir(parents=True, exist_ok=True)
            (dist_dir / "claude").mkdir(exist_ok=True)
            (dist_dir / "claude" / "iEVO.md").write_text("# iEVO\n")
            (dist_dir / "codex").mkdir(exist_ok=True)
            (dist_dir / "codex" / "BUILD_TARGET.md").write_text("# Codex\n")
        return result

    with (
        patch("ievo.commands.cortex.subprocess.run", side_effect=fake_run),
        patch("ievo.commands.cortex.CORTEX_CACHE_DIR", cache_dir),
    ):
        runner.invoke(app, ["cortex", "build"])

    version_file = tmp_path / ".ievo" / "cortex-version"
    assert version_file.exists()
    assert version_file.read_text().strip() == expected_tag
