"""Tests for ievo add command."""

from __future__ import annotations

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

import yaml

from ievo.commands.add import _create_local_agent, _print_dep_summary, add
from ievo.core.registry import Registry, RegistryEntry


def test_add_creates_agent_dir(tmp_project: Path) -> None:
    """Adding an agent creates its directory structure."""
    registry = Registry(
        {
            "test-agent": RegistryEntry(
                name="test-agent",
                version="0.1.0",
                description="Test",
                model="sonnet",
                dependencies=[],
            )
        }
    )

    with (
        patch("ievo.commands.add.require_project", return_value=tmp_project),
        patch("ievo.commands.add.Registry.load_cached", return_value=registry),
        patch.object(registry, "fetch", new_callable=AsyncMock),
        patch.object(registry, "download_agent", new_callable=AsyncMock, return_value=False),
    ):
        add(agents=["test-agent"])

    assert (tmp_project / "agents" / "test-agent").exists()


def test_add_records_in_manifest(tmp_project: Path) -> None:
    """Added agent is recorded in ievo.yaml."""
    registry = Registry(
        {
            "test-agent": RegistryEntry(
                name="test-agent",
                version="0.2.0",
                description="Test",
                model="sonnet",
                dependencies=[],
            )
        }
    )

    with (
        patch("ievo.commands.add.require_project", return_value=tmp_project),
        patch("ievo.commands.add.Registry.load_cached", return_value=registry),
        patch.object(registry, "fetch", new_callable=AsyncMock),
        patch.object(registry, "download_agent", new_callable=AsyncMock, return_value=False),
    ):
        add(agents=["test-agent"])

    manifest = yaml.safe_load((tmp_project / "ievo.yaml").read_text())
    assert manifest["agents"]["test-agent"] == "0.2.0"


def test_add_skips_already_installed(tmp_project_with_agent: Path) -> None:
    """Re-adding an installed agent is skipped."""
    registry = Registry()

    with (
        patch("ievo.commands.add.require_project", return_value=tmp_project_with_agent),
        patch("ievo.commands.add.Registry.load_cached", return_value=registry),
        patch.object(registry, "fetch", new_callable=AsyncMock),
        patch.object(
            registry, "download_agent", new_callable=AsyncMock, return_value=False
        ) as mock_dl,
    ):
        add(agents=["spec-writer"])

    mock_dl.assert_not_called()


def test_add_creates_local_scaffold(tmp_project: Path) -> None:
    """When marketplace unavailable, creates local scaffold."""
    registry = Registry()  # empty registry

    with (
        patch("ievo.commands.add.require_project", return_value=tmp_project),
        patch("ievo.commands.add.Registry.load_cached", return_value=registry),
        patch.object(registry, "fetch", new_callable=AsyncMock),
        patch.object(registry, "download_agent", new_callable=AsyncMock, return_value=False),
    ):
        add(agents=["custom-agent"])

    agent_dir = tmp_project / "agents" / "custom-agent"
    assert (agent_dir / "agent.yaml").exists()
    assert (agent_dir / "ROLE.md").exists()
    assert (agent_dir / "memory").is_dir()
    assert (agent_dir / "skills" / "evo" / "SKILL.md").exists()
    assert (agent_dir / "EVOLUTION_LOG.md").exists()


def test_add_resolves_dependencies(tmp_project: Path) -> None:
    """Agent with dependencies installs deps first."""
    registry = Registry(
        {
            "spec-writer": RegistryEntry(
                name="spec-writer",
                version="0.1.0",
                description="Spec",
                model="sonnet",
                dependencies=[],
            ),
            "architect": RegistryEntry(
                name="architect",
                version="0.1.0",
                description="Arch",
                model="opus",
                dependencies=["spec-writer"],
            ),
        }
    )

    with (
        patch("ievo.commands.add.require_project", return_value=tmp_project),
        patch("ievo.commands.add.Registry.load_cached", return_value=registry),
        patch.object(registry, "fetch", new_callable=AsyncMock),
        patch.object(registry, "download_agent", new_callable=AsyncMock, return_value=False),
    ):
        add(agents=["architect"])

    manifest = yaml.safe_load((tmp_project / "ievo.yaml").read_text())
    assert "spec-writer" in manifest["agents"]
    assert "architect" in manifest["agents"]


def test_create_local_agent_structure(tmp_path: Path) -> None:
    """_create_local_agent produces complete scaffold."""
    dest = tmp_path / "my-agent"
    _create_local_agent(dest, "my-agent", "1.0.0")

    assert (dest / "agent.yaml").exists()
    assert (dest / "ROLE.md").exists()
    assert (dest / "EVOLUTION_LOG.md").exists()
    assert (dest / "memory" / "CONTEXT.md").exists()
    assert (dest / "memory" / "DECISIONS.md").exists()
    assert (dest / "memory" / "VOCABULARY.md").exists()
    assert (dest / "memory" / "HISTORY.md").exists()
    assert (dest / "skills" / "evo" / "SKILL.md").exists()

    agent_data = yaml.safe_load((dest / "agent.yaml").read_text())
    assert agent_data["name"] == "my-agent"
    assert agent_data["version"] == "1.0.0"


# ── _print_dep_summary ──────────────────────────────────────


def test_print_dep_summary_no_deps(tmp_project_with_agent: Path) -> None:
    """No external deps — no deps install message."""
    root = tmp_project_with_agent
    _print_dep_summary(root, ["spec-writer"])


def test_print_dep_summary_with_external_deps(tmp_path: Path) -> None:
    """External MCP and plugins are printed."""
    root = tmp_path
    manifest_data = {"project": "test", "version": "0.1.0", "agents": {"researcher": "0.1.0"}}
    (root / "ievo.yaml").write_text(yaml.dump(manifest_data, sort_keys=False))

    agent_dir = root / "agents" / "researcher"
    agent_dir.mkdir(parents=True)
    agent_yaml = {
        "name": "researcher",
        "model": {"primary": "sonnet"},
        "mcp": [
            {"name": "filesystem", "type": "builtin"},
            {"name": "pubmed", "type": "npm", "package": "@test/pubmed"},
        ],
        "plugins": [{"name": "claude-mem", "marketplace": "thedotmack", "required": True}],
    }
    (agent_dir / "agent.yaml").write_text(yaml.dump(agent_yaml, sort_keys=False))

    _print_dep_summary(root, ["researcher"])


def test_print_dep_summary_missing_agent(tmp_path: Path) -> None:
    """Missing agent.yaml is skipped."""
    (tmp_path / "agents" / "broken").mkdir(parents=True)
    _print_dep_summary(tmp_path, ["broken"])
