"""Tests for ievo install command."""

from __future__ import annotations

from pathlib import Path
from unittest.mock import patch

import pytest

from ievo.commands.install import IEVO_LINE, _write_if_missing, install, patch_claude_md


@pytest.fixture(autouse=True)
def _mock_global_install(tmp_path: Path):
    """Mock ensure_home and github auth for all install tests."""
    fake_home = tmp_path / ".fake-ievo"
    fake_home.mkdir()
    with (
        patch("ievo.commands.install.ensure_home", return_value=fake_home),
        patch("ievo.core.global_config.get_config", return_value="gh_cli"),
    ):
        yield


def test_install_creates_project(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.chdir(tmp_path)
    project_dir = tmp_path / "my-project"

    install(name="my-project")

    assert (project_dir / ".ievo/manifest.yaml").exists()
    assert (project_dir / "CLAUDE.md").exists()
    assert (project_dir / ".gitignore").exists()
    assert (project_dir / "spec" / "SPEC_INDEX.md").exists()
    assert (project_dir / "spec" / "requirements").is_dir()
    assert (project_dir / "spec" / "changes").is_dir()
    assert (project_dir / "spec" / "questions").is_dir()
    assert (project_dir / "spec" / "templates").is_dir()
    assert (project_dir / "plans").is_dir()
    assert (project_dir / "agents").is_dir()
    assert (project_dir / ".claude" / "agents").is_dir()


def test_install_current_dir(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.chdir(tmp_path)
    install(name=".")

    assert (tmp_path / ".ievo/manifest.yaml").exists()


def test_install_rejects_duplicate(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """Re-install on an existing project succeeds and deploys any missing files."""
    monkeypatch.chdir(tmp_path)
    ievo_dir = tmp_path / ".ievo"
    ievo_dir.mkdir()
    (ievo_dir / "manifest.yaml").write_text(
        "project: test\nversion: 0.1.0\ncreated: '2025-01-01'\n"
    )

    # Should NOT raise — re-install is idempotent
    install(name=".")

    # Integration files should be deployed (agents, skills)
    assert (tmp_path / ".claude" / "agents").is_dir()
    assert (tmp_path / ".claude" / "skills").is_dir()


def test_install_recovers_missing_manifest(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """Re-install works when .ievo/ dir exists but manifest.yaml is missing."""
    monkeypatch.chdir(tmp_path)
    # Simulate partial state: directory exists, manifest missing
    ievo_dir = tmp_path / ".ievo"
    ievo_dir.mkdir()
    (ievo_dir / "config.yaml").write_text("some: data\n")

    install(name=".")

    assert (tmp_path / ".ievo" / "manifest.yaml").is_file()
    # Existing files should be preserved
    assert (ievo_dir / "config.yaml").read_text() == "some: data\n"


def test_install_spec_index_contains_legend(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    monkeypatch.chdir(tmp_path)
    install(name="legend-test")

    content = (tmp_path / "legend-test" / "spec" / "SPEC_INDEX.md").read_text()
    assert "Status Legend" in content
    assert "ready" in content
    assert "implemented" in content


# ── New tests ──────────────────────────────────────────────────


def test_install_creates_gitkeep_in_empty_dirs(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    monkeypatch.chdir(tmp_path)
    install(name="gk-test")

    project_dir = tmp_path / "gk-test"
    for d in ["spec/requirements", "spec/changes", "spec/questions", "plans", "agents"]:
        assert (project_dir / d / ".gitkeep").exists(), f"Missing .gitkeep in {d}"


def test_install_creates_gitignore(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.chdir(tmp_path)
    install(name="gi-test")

    content = (tmp_path / "gi-test" / ".gitignore").read_text()
    assert ".env" in content
    assert "__pycache__" in content


def test_install_claude_md_has_scaffold(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.chdir(tmp_path)
    install(name="claude-test")

    content = (tmp_path / "claude-test" / "CLAUDE.md").read_text()
    assert "# claude-test" in content
    assert "Tech stack" in content
    assert "Conventions" in content
    assert "What this project is" in content


def test_install_creates_spec_templates(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.chdir(tmp_path)
    install(name="tpl2-test")

    project_dir = tmp_path / "tpl2-test"
    assert (project_dir / "spec" / "templates" / "REQ_TEMPLATE.md").exists()
    assert (project_dir / "spec" / "templates" / "CR_TEMPLATE.md").exists()

    req_content = (project_dir / "spec" / "templates" / "REQ_TEMPLATE.md").read_text()
    assert "Acceptance Criteria" in req_content

    cr_content = (project_dir / "spec" / "templates" / "CR_TEMPLATE.md").read_text()
    assert "What Changes" in cr_content


# ── Claude Code hooks tests ───────────────────────────────────


def test_install_creates_claude_hooks_dir(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.chdir(tmp_path)
    install(name="hooks-test")

    assert (tmp_path / "hooks-test" / ".claude" / "hooks").is_dir()


def test_install_creates_precompact_hook(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.chdir(tmp_path)
    install(name="hook-script-test")

    hook_file = tmp_path / "hook-script-test" / ".claude" / "hooks" / "precompact_save.py"
    assert hook_file.exists()
    content = hook_file.read_text()
    assert "PreCompact" in content
    assert "claude-mem" in content
    assert "def main()" in content


def test_install_creates_settings_local(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    import json

    monkeypatch.chdir(tmp_path)
    install(name="settings-test")

    settings_file = tmp_path / "settings-test" / ".claude" / "settings.local.json"
    assert settings_file.exists()
    data = json.loads(settings_file.read_text())
    assert "hooks" in data
    assert "PreCompact" in data["hooks"]
    assert "contextPaths" in data
    assert data["contextPaths"] == [".ievo/iEVO.md"]


def test_install_gitignore_includes_settings_local(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    monkeypatch.chdir(tmp_path)
    install(name="gi-local-test")

    content = (tmp_path / "gi-local-test" / ".gitignore").read_text()
    assert "settings.local.json" in content


# ── _write_if_missing ────────────────────────────────────────


def test_write_if_missing_creates_file(tmp_path: Path) -> None:
    """Creates file when it doesn't exist."""
    target = tmp_path / "new_file.txt"
    assert _write_if_missing(target, "content") is True
    assert target.read_text() == "content"


def test_write_if_missing_skips_existing(tmp_path: Path) -> None:
    """Returns False when file already exists."""
    target = tmp_path / "existing.txt"
    target.write_text("original")
    assert _write_if_missing(target, "new content") is False
    assert target.read_text() == "original"


# ── Install with pre-existing files ─────────────────────────


def test_install_skips_existing_files(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """Install skips files that already exist (branch coverage)."""
    monkeypatch.chdir(tmp_path)
    project_dir = tmp_path / "preexist-test"
    project_dir.mkdir()

    # Pre-create some files that install would normally create.
    # Include the IEVO_LINE so patch_claude_md() is a no-op (idempotent test).
    (project_dir / "CLAUDE.md").write_text(f"# Existing CLAUDE.md\n\n{IEVO_LINE}\n")
    (project_dir / ".gitignore").write_text("# Existing gitignore")
    (project_dir / "spec").mkdir(parents=True)
    (project_dir / "spec" / "SPEC_INDEX.md").write_text("# Existing spec index")
    (project_dir / "spec" / "templates").mkdir(parents=True)
    (project_dir / "spec" / "templates" / "REQ_TEMPLATE.md").write_text("# Existing")
    (project_dir / "spec" / "templates" / "CR_TEMPLATE.md").write_text("# Existing")
    (project_dir / ".claude" / "hooks").mkdir(parents=True)
    (project_dir / ".claude" / "hooks" / "precompact_save.py").write_text("# Existing")
    (project_dir / ".claude" / "settings.local.json").write_text("{}")
    (project_dir / ".claude" / "agents").mkdir(parents=True, exist_ok=True)

    # Pre-create .gitkeep files in empty dirs so install skips writing them
    for d in ["spec/requirements", "spec/changes", "spec/questions", "plans", "agents"]:
        (project_dir / d).mkdir(parents=True, exist_ok=True)
        (project_dir / d / ".gitkeep").write_text("")

    install(name="preexist-test")

    # Files should NOT be overwritten; CLAUDE.md already had IEVO_LINE so it's unchanged
    assert "# Existing CLAUDE.md" in (project_dir / "CLAUDE.md").read_text()
    assert (project_dir / "CLAUDE.md").read_text().count(IEVO_LINE) == 1


# ── Install with github auth prompt ─────────────────────────


def test_install_prompts_github_auth(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """First install prompts for github auth when config is None."""
    monkeypatch.chdir(tmp_path)
    fake_home = tmp_path / ".fake-ievo"
    fake_home.mkdir(exist_ok=True)

    with (
        patch("ievo.commands.install.ensure_home", return_value=fake_home),
        patch("ievo.core.global_config.get_config", return_value=None),
        patch("ievo.core.github_auth.prompt_github_auth") as mock_auth,
    ):
        install(name="auth-test")

    mock_auth.assert_called_once_with(fake_home)


# ── No deprecation warning ──────────────────────────────────


def test_install_shows_no_deprecation_warning(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    """Running install does NOT show a deprecation warning."""
    monkeypatch.chdir(tmp_path)

    with patch("ievo.utils.deprecation.warn") as mock_warn:
        install(name="no-deprecation-test")

    mock_warn.assert_not_called()


# ── Bundled marketplace templates ──────────────────────────────


def test_install_creates_claude_agents_dir(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """Install creates .claude/agents/ directory."""
    monkeypatch.chdir(tmp_path)
    install(name="agents-dir-test")
    assert (tmp_path / "agents-dir-test" / ".claude" / "agents").is_dir()


def test_install_deploys_all_bundled_agents(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    """Install deploys all bundled agents as .md files to .claude/agents/."""
    from ievo.marketplace import list_bundled_agents

    monkeypatch.chdir(tmp_path)
    install(name="agents-test")

    agents_dir = tmp_path / "agents-test" / ".claude" / "agents"
    for agent_name in list_bundled_agents():
        assert (agents_dir / f"{agent_name}.md").is_file(), f"{agent_name}.md not deployed"


def test_install_agent_content_verbatim(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """Deployed agent file content matches bundled source exactly."""
    from ievo.marketplace import bundled_agent_path

    monkeypatch.chdir(tmp_path)
    install(name="agent-verbatim-test")

    deployed = (
        tmp_path / "agent-verbatim-test" / ".claude" / "agents" / "spec-writer.md"
    ).read_text()
    bundled = bundled_agent_path("spec-writer")
    assert bundled is not None
    assert deployed == bundled.read_text()


def test_install_agents_idempotent(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """Re-install does not overwrite existing agent files."""
    monkeypatch.chdir(tmp_path)
    install(name="idempotent-agents-test")

    # Overwrite one agent with custom content
    agents_dir = tmp_path / "idempotent-agents-test" / ".claude" / "agents"
    custom = agents_dir / "spec-writer.md"
    custom.write_text("# custom content\n")

    # Re-install: remove manifest first to allow re-install without error
    (tmp_path / "idempotent-agents-test" / ".ievo" / "manifest.yaml").unlink()
    install(name="idempotent-agents-test")

    # Custom content preserved
    assert custom.read_text() == "# custom content\n"


def test_install_marketplace_section_header_in_output(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture
) -> None:
    """Install output includes 'Marketplace files' section header."""
    monkeypatch.chdir(tmp_path)
    install(name="agents-header-test")
    captured = capsys.readouterr()
    assert "Marketplace files" in captured.out


def test_install_deploys_ievo_md_from_bundled(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    """Install deploys iEVO.md from bundled marketplace."""
    monkeypatch.chdir(tmp_path)
    install(name="ievo-md-test")

    ievo_md = tmp_path / "ievo-md-test" / ".ievo" / "iEVO.md"
    assert ievo_md.exists()
    content = ievo_md.read_text()
    assert len(content) > 100  # Non-trivial content from marketplace


def test_install_deploys_all_bundled_templates(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    """Install deploys all 6 spec templates from bundled marketplace."""
    monkeypatch.chdir(tmp_path)
    install(name="all-tpl-test")

    tpl_dir = tmp_path / "all-tpl-test" / "spec" / "templates"
    for fname in [
        "REQ_TEMPLATE.md",
        "CR_TEMPLATE.md",
        "QUESTION_TEMPLATE.md",
        "PLAN_TEMPLATE.md",
        "ACCEPTANCE_REPORT_TEMPLATE.md",
        "PROPOSAL_TEMPLATE.md",
    ]:
        assert (tpl_dir / fname).exists(), f"Missing template: {fname}"


# ── REQ-009: cli_version written by install ─────────────────


def test_install_writes_cli_version(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """After install, manifest.cli_version equals running __version__."""
    from ievo.core.project import ProjectManifest
    from ievo.version import __version__

    monkeypatch.chdir(tmp_path)
    install(name="cv-test")

    manifest = ProjectManifest.load(tmp_path / "cv-test")
    assert manifest.cli_version == __version__


def test_install_does_not_overwrite_existing_manifest(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    """Re-install preserves existing agent list in manifest but updates cli_version."""
    import yaml as _yaml

    monkeypatch.chdir(tmp_path)
    ievo_dir = tmp_path / ".ievo"
    ievo_dir.mkdir()
    (ievo_dir / "manifest.yaml").write_text(
        "project: test\nversion: 0.1.0\ncreated: '2025-01-01'\n"
        "agents:\n  spec-writer: 0.2.0\ncli_version: '0.0.1'\n"
    )

    # Re-install should succeed and load existing manifest
    install(name=".")

    data = _yaml.safe_load((ievo_dir / "manifest.yaml").read_text())
    # Existing agents preserved
    assert data.get("agents", {}).get("spec-writer") == "0.2.0"
    # cli_version updated to current version
    from ievo.version import __version__

    assert data.get("cli_version") == __version__


# ── patch_claude_md() tests ───────────────────────────────────


def test_patch_claude_md_new_file_from_template(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    """install() creates CLAUDE.md with marker block from template."""
    monkeypatch.chdir(tmp_path)
    install(name="patch-test")

    content = (tmp_path / "patch-test" / "CLAUDE.md").read_text()
    assert IEVO_LINE in content


def test_patch_claude_md_existing_file_without_marker(tmp_path: Path) -> None:
    """patch_claude_md appends marker to existing file without marker."""
    claude_md = tmp_path / "CLAUDE.md"
    claude_md.write_text("# My Project\n\n## Tech stack\nPython\n")

    result = patch_claude_md(claude_md)

    assert result is True
    content = claude_md.read_text()
    assert "# My Project" in content
    assert "## Tech stack" in content
    assert IEVO_LINE in content


def test_patch_claude_md_existing_file_with_marker(tmp_path: Path) -> None:
    """patch_claude_md returns False and leaves file unchanged when IEVO_LINE present."""
    original = f"# My Project\n\n{IEVO_LINE}\n"
    claude_md = tmp_path / "CLAUDE.md"
    claude_md.write_text(original)

    result = patch_claude_md(claude_md)

    assert result is False
    assert claude_md.read_text() == original


def test_patch_claude_md_nonexistent_file(tmp_path: Path) -> None:
    """patch_claude_md returns False for a path that does not exist."""
    result = patch_claude_md(tmp_path / "nonexistent.md")
    assert result is False


def test_patch_claude_md_file_without_trailing_newline(tmp_path: Path) -> None:
    """patch_claude_md separates injected block from existing content by one blank line."""
    claude_md = tmp_path / "CLAUDE.md"
    claude_md.write_text("# My Project")  # no trailing newline

    patch_claude_md(claude_md)

    content = claude_md.read_text()
    # The IEVO_LINE must be separated from existing content by exactly one blank line
    assert f"\n\n{IEVO_LINE}" in content


def test_patch_claude_md_idempotent_during_install(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    """Running install creates CLAUDE.md with exactly one marker block."""
    monkeypatch.chdir(tmp_path)
    install(name="idem-test")

    content = (tmp_path / "idem-test" / "CLAUDE.md").read_text()
    assert content.count(IEVO_LINE) == 1


def test_claude_md_template_contains_ievo_line() -> None:
    """CLAUDE_MD template constant already contains the IEVO_LINE."""
    from ievo.commands.init_templates import CLAUDE_MD

    assert IEVO_LINE in CLAUDE_MD


def test_patch_claude_md_file_with_double_trailing_newline(tmp_path: Path) -> None:
    """patch_claude_md handles file that already ends with double newline (no extra blank line)."""
    claude_md = tmp_path / "CLAUDE.md"
    claude_md.write_text("# My Project\n\n")  # ends with double newline already

    patch_claude_md(claude_md)

    content = claude_md.read_text()
    # Should have exactly one blank line before IEVO_LINE, no triple newline
    assert f"\n\n{IEVO_LINE}" in content
    assert f"\n\n\n{IEVO_LINE}" not in content


def test_install_with_preexisting_claude_md_patches_it(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
    """install() patches a pre-existing CLAUDE.md that has no marker block."""
    monkeypatch.chdir(tmp_path)
    project_dir = tmp_path / "patch-existing-test"
    project_dir.mkdir()

    # Pre-create CLAUDE.md without marker (install will skip writing, but should patch)
    (project_dir / "CLAUDE.md").write_text("# Existing content\n")

    # Pre-create other required dirs/files to avoid failures
    for d in ["spec/requirements", "spec/changes", "spec/questions", "plans", "agents"]:
        (project_dir / d).mkdir(parents=True, exist_ok=True)
        (project_dir / d / ".gitkeep").write_text("")

    install(name="patch-existing-test")

    content = (project_dir / "CLAUDE.md").read_text()
    assert "# Existing content" in content
    assert IEVO_LINE in content


# ── REQ-011: Skills installation ───────────────────────────────


def test_install_creates_claude_skills_dir(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """install creates .claude/skills/ directory."""
    monkeypatch.chdir(tmp_path)
    install(name="skills-dir-test")
    assert (tmp_path / "skills-dir-test" / ".claude" / "skills").is_dir()


def test_install_deploys_backlog_skill(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """install deploys backlog skill to .claude/skills/backlog/SKILL.md."""
    from ievo.marketplace import bundled_skill_path

    monkeypatch.chdir(tmp_path)
    install(name="backlog-test")

    skill_path = tmp_path / "backlog-test" / ".claude" / "skills" / "backlog" / "SKILL.md"
    assert skill_path.exists()

    # Verify content matches bundled
    bundled = bundled_skill_path("backlog")
    assert bundled is not None
    assert skill_path.read_text() == bundled.read_text()


def test_install_deploys_ievo_skill(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """install deploys ievo skill to .claude/skills/ievo/SKILL.md."""
    from ievo.marketplace import bundled_skill_path

    monkeypatch.chdir(tmp_path)
    install(name="ievo-test")

    skill_path = tmp_path / "ievo-test" / ".claude" / "skills" / "ievo" / "SKILL.md"
    assert skill_path.exists()

    # Verify content matches bundled
    bundled = bundled_skill_path("ievo")
    assert bundled is not None
    assert skill_path.read_text() == bundled.read_text()


def test_install_skill_content_verbatim(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """Deployed skill content is byte-identical to bundled."""
    from ievo.marketplace import bundled_skill_path

    monkeypatch.chdir(tmp_path)
    install(name="verbatim-test")

    for skill_name in ["backlog", "ievo"]:
        deployed = (
            tmp_path / "verbatim-test" / ".claude" / "skills" / skill_name / "SKILL.md"
        ).read_text()
        bundled = bundled_skill_path(skill_name)
        assert bundled is not None
        assert deployed == bundled.read_text()


def test_install_skills_idempotent_backlog(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """Re-install with existing backlog skill does not overwrite."""
    monkeypatch.chdir(tmp_path)
    install(name=".")

    # Modify skill file
    skill_path = tmp_path / ".claude" / "skills" / "backlog" / "SKILL.md"
    original = skill_path.read_text()
    modified = original + "\n# MODIFIED\n"
    skill_path.write_text(modified)

    # Remove manifest to allow re-install
    (tmp_path / ".ievo" / "manifest.yaml").unlink()

    # Re-install
    install(name=".")

    # Verify not overwritten
    assert skill_path.read_text() == modified


def test_install_skills_idempotent_ievo(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """Re-install with existing ievo skill does not overwrite."""
    monkeypatch.chdir(tmp_path)
    install(name=".")

    # Modify skill file
    skill_path = tmp_path / ".claude" / "skills" / "ievo" / "SKILL.md"
    original = skill_path.read_text()
    modified = original + "\n# MODIFIED\n"
    skill_path.write_text(modified)

    # Remove manifest to allow re-install
    (tmp_path / ".ievo" / "manifest.yaml").unlink()

    # Re-install
    install(name=".")

    # Verify not overwritten
    assert skill_path.read_text() == modified


def test_install_creates_registry_yaml(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """install creates .ievo/registry.yaml with mtime tracking for agents, skills, templates."""
    import yaml

    monkeypatch.chdir(tmp_path)
    install(name="reg-test")

    registry_path = tmp_path / "reg-test" / ".ievo" / "registry.yaml"
    assert registry_path.exists()

    data = yaml.safe_load(registry_path.read_text())
    assert "agents" in data
    assert "skills" in data
    assert "templates" in data


# ── REQ-001: Cortex download integration ────────────────────────


def test_install_downloads_cortex(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """install calls fetch_latest_cortex_tag and download_cortex_release."""
    monkeypatch.chdir(tmp_path)

    with (
        patch(
            "ievo.commands.install.fetch_latest_cortex_tag",
            return_value=("v1.0.0", "https://example.com/cortex-v1.0.0.tar.gz"),
        ) as mock_fetch,
        patch("ievo.commands.install.download_cortex_release") as mock_download,
    ):
        install(name="cortex-dl-test")

    mock_fetch.assert_called_once()
    mock_download.assert_called_once()
    call_kwargs = mock_download.call_args
    assert call_kwargs.kwargs.get("tag") == "v1.0.0" or call_kwargs.args[0] == "v1.0.0"


def test_install_cortex_failure_is_non_fatal(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture
) -> None:
    """install completes successfully (exit 0) when Cortex download fails."""
    from ievo.core.cortex import CortexDownloadError

    monkeypatch.chdir(tmp_path)

    with (
        patch(
            "ievo.commands.install.fetch_latest_cortex_tag",
            side_effect=CortexDownloadError(
                "Failed to reach https://github.com/ievo-ai/cortex/releases"
            ),
        ),
    ):
        # Must NOT raise — non-fatal
        install(name="cortex-fail-test")

    # Install should succeed despite Cortex failure
    assert (tmp_path / "cortex-fail-test" / ".ievo" / "manifest.yaml").exists()


def test_install_cortex_failure_warns_with_url(
    tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture
) -> None:
    """install warning on Cortex failure contains the releases URL and ievo cortex build hint."""
    from ievo.core.cortex import CortexDownloadError

    monkeypatch.chdir(tmp_path)

    releases_url = "https://github.com/ievo-ai/cortex/releases"
    with (
        patch(
            "ievo.commands.install.fetch_latest_cortex_tag",
            side_effect=CortexDownloadError(f"Failed to reach {releases_url}"),
        ),
    ):
        install(name="warn-url-test")

    captured = capsys.readouterr()
    assert "releases" in captured.out.lower() or "cortex" in captured.out.lower()
    assert "cortex build" in captured.out


def test_install_writes_cortex_version(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """install writes .ievo/cortex-version after successful Cortex download."""
    monkeypatch.chdir(tmp_path)

    with (
        patch(
            "ievo.commands.install.fetch_latest_cortex_tag",
            return_value=("v1.0.0", "https://example.com/cortex-v1.0.0.tar.gz"),
        ),
        patch("ievo.commands.install.download_cortex_release") as mock_download,
    ):
        # Simulate download_cortex_release writing the version file
        def fake_download(tag, asset_url, root):
            version_file = root / ".ievo" / "cortex-version"
            version_file.parent.mkdir(parents=True, exist_ok=True)
            version_file.write_text(tag + "\n")

        mock_download.side_effect = fake_download
        install(name="version-file-test")

    version_file = tmp_path / "version-file-test" / ".ievo" / "cortex-version"
    assert version_file.exists()
    assert version_file.read_text().strip() == "v1.0.0"


def test_install_idempotent_with_cortex(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """Running install twice with Cortex download produces the same result."""
    monkeypatch.chdir(tmp_path)

    download_count = [0]

    def fake_download(tag, asset_url, root):
        download_count[0] += 1
        version_file = root / ".ievo" / "cortex-version"
        version_file.parent.mkdir(parents=True, exist_ok=True)
        version_file.write_text(tag + "\n")

    with (
        patch(
            "ievo.commands.install.fetch_latest_cortex_tag",
            return_value=("v1.0.0", "https://example.com/cortex-v1.0.0.tar.gz"),
        ),
        patch("ievo.commands.install.download_cortex_release", side_effect=fake_download),
    ):
        install(name="idem-cortex-test")

    # Remove manifest to allow re-install
    (tmp_path / "idem-cortex-test" / ".ievo" / "manifest.yaml").unlink()

    with (
        patch(
            "ievo.commands.install.fetch_latest_cortex_tag",
            return_value=("v1.0.0", "https://example.com/cortex-v1.0.0.tar.gz"),
        ),
        patch("ievo.commands.install.download_cortex_release", side_effect=fake_download),
    ):
        install(name="idem-cortex-test")

    # Both runs should have downloaded Cortex
    assert download_count[0] == 2

    # Version file should exist and be correct
    version_file = tmp_path / "idem-cortex-test" / ".ievo" / "cortex-version"
    assert version_file.exists()
    assert version_file.read_text().strip() == "v1.0.0"
