"""Tests for ievo CLI entry point (cli.py)."""

from __future__ import annotations

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

import pytest
import typer

from ievo.cli import app, version_callback


def test_version_callback_true() -> None:
    with pytest.raises(typer.Exit):
        version_callback(value=True)


def test_version_callback_false() -> None:
    # Should do nothing when value is False
    version_callback(value=False)


def test_main_with_subcommand() -> None:
    """When a subcommand is invoked, main should not launch TUI."""
    # Import the actual underlying function
    from ievo.cli import main

    ctx = MagicMock(spec=typer.Context)
    ctx.invoked_subcommand = "install"
    # Should not raise or call run_tui
    main(ctx=ctx, version=False)


def test_main_no_subcommand_launches_tui(tmp_path: Path) -> None:
    """When no subcommand and project exists, main should launch the TUI."""
    from ievo.cli import main

    ctx = MagicMock(spec=typer.Context)
    ctx.invoked_subcommand = None

    mock_tui_module = MagicMock()
    with (
        patch("ievo.core.config.ensure_home", return_value=tmp_path),
        patch("ievo.core.startup.ensure_startup"),
        patch("ievo.core.config.find_project_root", return_value=tmp_path),
        patch("ievo.core.agent_sync.sync_marketplace", return_value=[]),
        patch.dict(sys.modules, {"ievo.tui.app": mock_tui_module}),
    ):
        main(ctx=ctx, version=False)
        mock_tui_module.run_tui.assert_called_once()


def test_main_no_project_confirms_then_inits(tmp_path: Path) -> None:
    """When no project found, main asks confirmation then runs install."""
    from ievo.cli import main

    ctx = MagicMock(spec=typer.Context)
    ctx.invoked_subcommand = None

    mock_tui_module = MagicMock()
    with (
        patch("ievo.core.config.ensure_home", return_value=tmp_path),
        patch("ievo.core.startup.ensure_startup"),
        patch("ievo.core.config.find_project_root", return_value=None),
        patch("ievo.cli.typer.confirm", return_value=True),
        patch("ievo.commands.install.install") as mock_install,
        patch.dict(sys.modules, {"ievo.tui.app": mock_tui_module}),
    ):
        main(ctx=ctx, version=False)
        mock_install.assert_called_once_with(name=".")
        mock_tui_module.run_tui.assert_called_once()


def test_main_no_project_user_declines_exits(tmp_path: Path) -> None:
    """When user declines install, exit gracefully without TUI."""
    from ievo.cli import main

    ctx = MagicMock(spec=typer.Context)
    ctx.invoked_subcommand = None

    mock_tui_module = MagicMock()
    with (
        patch("ievo.core.config.ensure_home", return_value=tmp_path),
        patch("ievo.core.startup.ensure_startup"),
        patch("ievo.core.config.find_project_root", return_value=None),
        patch("ievo.cli.typer.confirm", return_value=False),
        patch("ievo.commands.install.install") as mock_install,
        patch.dict(sys.modules, {"ievo.tui.app": mock_tui_module}),
    ):
        with pytest.raises(typer.Exit):
            main(ctx=ctx, version=False)
        mock_install.assert_not_called()
        mock_tui_module.run_tui.assert_not_called()


def test_auto_init_creates_project(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    """Integration: no project → auto-init creates .ievo/manifest.yaml and project dirs."""
    from ievo.cli import main

    monkeypatch.chdir(tmp_path)

    ctx = MagicMock(spec=typer.Context)
    ctx.invoked_subcommand = None

    mock_tui_module = MagicMock()
    home = tmp_path / ".home"
    home.mkdir()
    # Pre-set github_auth so prompt_github_auth() is not triggered (no stdin in CI)
    import json

    (home / "config.json").write_text(json.dumps({"github_auth": "gh_cli"}))
    with (
        patch("ievo.core.config.ensure_home", return_value=home),
        patch("ievo.commands.install.ensure_home", return_value=home),
        patch("ievo.core.startup.ensure_startup"),
        patch("ievo.cli.typer.confirm", return_value=True),
        patch.dict(sys.modules, {"ievo.tui.app": mock_tui_module}),
    ):
        main(ctx=ctx, version=False)

    # Verify real files were created
    assert (tmp_path / ".ievo" / "manifest.yaml").is_file()
    assert (tmp_path / "agents").is_dir()
    assert (tmp_path / "spec").is_dir()
    assert (tmp_path / "plans").is_dir()
    mock_tui_module.run_tui.assert_called_once()


def test_main_sync_shows_updated_when_changed(tmp_path: Path) -> None:
    """sync_marketplace result is printed when files are updated."""
    from ievo.cli import main

    ctx = MagicMock(spec=typer.Context)
    ctx.invoked_subcommand = None

    mock_tui_module = MagicMock()
    printed: list[str] = []
    with (
        patch("ievo.core.config.ensure_home", return_value=tmp_path),
        patch("ievo.core.startup.ensure_startup"),
        patch("ievo.core.config.find_project_root", return_value=tmp_path),
        patch("ievo.core.agent_sync.sync_marketplace", return_value=["spec-writer", "coder"]),
        patch("ievo.utils.console.info", side_effect=lambda msg: printed.append(msg)),
        patch.dict(sys.modules, {"ievo.tui.app": mock_tui_module}),
    ):
        main(ctx=ctx, version=False)

    assert any("spec-writer" in msg and "coder" in msg for msg in printed)


def test_main_sync_silent_when_no_changes(tmp_path: Path) -> None:
    """No info message when sync_marketplace returns empty list."""
    from ievo.cli import main

    ctx = MagicMock(spec=typer.Context)
    ctx.invoked_subcommand = None

    mock_tui_module = MagicMock()
    printed: list[str] = []
    with (
        patch("ievo.core.config.ensure_home", return_value=tmp_path),
        patch("ievo.core.startup.ensure_startup"),
        patch("ievo.core.config.find_project_root", return_value=tmp_path),
        patch("ievo.core.agent_sync.sync_marketplace", return_value=[]),
        patch("ievo.utils.console.info", side_effect=lambda msg: printed.append(msg)),
        patch.dict(sys.modules, {"ievo.tui.app": mock_tui_module}),
    ):
        main(ctx=ctx, version=False)

    assert not any("Updated" in msg for msg in printed)


def test_app_has_commands() -> None:
    # Verify all expected commands are registered
    command_names = {cmd.name for cmd in app.registered_commands}
    expected = {"install", "add", "remove", "update", "list", "run", "team", "orchestrate"}
    assert expected.issubset(command_names)
