"""Tests for the 'tribunal uninstall' command."""

from __future__ import annotations

import json
import shutil
from pathlib import Path
from unittest.mock import patch

import pytest

from launcher.cli import _run_uninstall
from launcher.uninstall import (
    _build_removal_plan,
    _clean_claude_settings,
    _collect_shell_configs_with_alias,
    _remove_alias_basic,
)


def _setup_fake_install(home: Path) -> dict[str, Path]:
    """Create a minimal fake Tribunal installation under *home*."""
    plugin_dir = home / ".claude" / "tribunal"
    plugin_dir.mkdir(parents=True)
    (plugin_dir / "hooks.json").write_text("{}")

    claude_settings = home / ".claude" / "settings.json"
    settings = {
        "hooks": {"PostToolUse": []},
        "env": {
            "SF_WORKER_URL": "http://localhost:1234",
            "UNRELATED_VAR": "keep-me",
        },
        "statusCommand": "tribunal statusline",
        "permissions": {"allow": ["tribunal:*", "other:permission"]},
    }
    claude_settings.write_text(json.dumps(settings))

    bashrc = home / ".bashrc"
    bashrc.write_text(
        "# existing content\n# Tribunal\nalias tribunal=\"$HOME/.tribunal/bin/tribunal\"\n"
    )

    tribunal_home = home / ".tribunal"
    tribunal_home.mkdir()
    (tribunal_home / "sessions").mkdir()

    return {
        "plugin_dir": plugin_dir,
        "claude_settings": claude_settings,
        "bashrc": bashrc,
        "tribunal_home": tribunal_home,
    }


class TestUninstall:
    def test_removes_plugin_dir(self, tmp_path: Path) -> None:
        paths = _setup_fake_install(tmp_path)
        with (
            patch("pathlib.Path.home", return_value=tmp_path),
            patch("builtins.input", return_value="y"),
        ):
            _run_uninstall(remove_all=False)
        assert not paths["plugin_dir"].exists()

    def test_cleans_settings_json(self, tmp_path: Path) -> None:
        paths = _setup_fake_install(tmp_path)
        with (
            patch("pathlib.Path.home", return_value=tmp_path),
            patch("builtins.input", return_value="y"),
        ):
            _run_uninstall(remove_all=False)

        settings = json.loads(paths["claude_settings"].read_text())
        assert "hooks" not in settings
        assert "SF_WORKER_URL" not in settings.get("env", {})
        assert settings["env"]["UNRELATED_VAR"] == "keep-me"
        assert "tribunal" not in str(settings.get("statusCommand", ""))
        perms_allow = settings.get("permissions", {}).get("allow", [])
        assert "tribunal:*" not in perms_allow
        assert "other:permission" in perms_allow

    def test_removes_shell_aliases(self, tmp_path: Path) -> None:
        paths = _setup_fake_install(tmp_path)
        with (
            patch("pathlib.Path.home", return_value=tmp_path),
            patch("builtins.input", return_value="y"),
        ):
            _run_uninstall(remove_all=False)

        bashrc_content = paths["bashrc"].read_text()
        assert "alias tribunal" not in bashrc_content
        assert "# Tribunal" not in bashrc_content

    def test_preserves_tribunal_home_without_all(self, tmp_path: Path) -> None:
        paths = _setup_fake_install(tmp_path)
        with (
            patch("pathlib.Path.home", return_value=tmp_path),
            patch("builtins.input", return_value="y"),
        ):
            _run_uninstall(remove_all=False)
        assert paths["tribunal_home"].exists()

    def test_removes_tribunal_home_with_all(self, tmp_path: Path) -> None:
        paths = _setup_fake_install(tmp_path)
        with (
            patch("pathlib.Path.home", return_value=tmp_path),
            patch("builtins.input", return_value="y"),
        ):
            _run_uninstall(remove_all=True)
        assert not paths["tribunal_home"].exists()

    def test_aborts_on_n(self, tmp_path: Path) -> None:
        paths = _setup_fake_install(tmp_path)
        with (
            patch("pathlib.Path.home", return_value=tmp_path),
            patch("builtins.input", return_value="n"),
        ):
            _run_uninstall(remove_all=False)
        assert paths["plugin_dir"].exists()

    def test_yes_flag_skips_prompt(self, tmp_path: Path) -> None:
        paths = _setup_fake_install(tmp_path)
        with patch("pathlib.Path.home", return_value=tmp_path):
            _run_uninstall(remove_all=False, yes=True)
        assert not paths["plugin_dir"].exists()

    def test_nothing_to_remove(self, tmp_path: Path, capsys: pytest.CaptureFixture) -> None:
        (tmp_path / ".claude").mkdir(parents=True)
        with (
            patch("pathlib.Path.home", return_value=tmp_path),
            patch("builtins.input", return_value="y"),
        ):
            _run_uninstall(remove_all=False)
        out = capsys.readouterr().out
        assert "Nothing to remove" in out


class TestUninstallHelpers:
    """Tests for extracted helper functions in launcher.uninstall."""

    def test_collect_shell_configs_detects_alias(self, tmp_path: Path) -> None:
        bashrc = tmp_path / ".bashrc"
        bashrc.write_text("alias tribunal=/path/to/bin\n")
        result = _collect_shell_configs_with_alias(tmp_path)
        assert bashrc in result

    def test_collect_shell_configs_ignores_clean_files(self, tmp_path: Path) -> None:
        bashrc = tmp_path / ".bashrc"
        bashrc.write_text("export PATH=$PATH:/usr/local/bin\n")
        result = _collect_shell_configs_with_alias(tmp_path)
        assert bashrc not in result

    def test_collect_shell_configs_skips_missing_files(self, tmp_path: Path) -> None:
        result = _collect_shell_configs_with_alias(tmp_path)
        assert result == []

    def test_build_removal_plan_includes_plugin_dir(self, tmp_path: Path) -> None:
        plugin_dir = tmp_path / ".claude" / "tribunal"
        plugin_dir.mkdir(parents=True)
        settings = tmp_path / ".claude" / "settings.json"
        plan = _build_removal_plan(plugin_dir, settings, [], tmp_path / ".tribunal", False)
        assert any("plugin directory" in line for line in plan)

    def test_build_removal_plan_empty_when_nothing_installed(self, tmp_path: Path) -> None:
        plan = _build_removal_plan(
            tmp_path / "noplugin",
            tmp_path / "nosettings.json",
            [],
            tmp_path / "nohome",
            False,
        )
        assert plan == []

    def test_clean_claude_settings_removes_hooks(self, tmp_path: Path) -> None:
        settings_file = tmp_path / "settings.json"
        settings_file.write_text(json.dumps({"hooks": {"PostToolUse": []}, "env": {}}))
        err = _clean_claude_settings(settings_file)
        assert err is None
        updated = json.loads(settings_file.read_text())
        assert "hooks" not in updated

    def test_clean_claude_settings_preserves_unrelated(self, tmp_path: Path) -> None:
        settings_file = tmp_path / "settings.json"
        settings_file.write_text(json.dumps({"env": {"MY_VAR": "keep"}}))
        _clean_claude_settings(settings_file)
        updated = json.loads(settings_file.read_text())
        assert updated["env"]["MY_VAR"] == "keep"

    def test_clean_claude_settings_returns_error_on_bad_file(self, tmp_path: Path) -> None:
        bad_file = tmp_path / "bad.json"
        bad_file.write_text("not json{{{")
        err = _clean_claude_settings(bad_file)
        assert err is not None
        assert "Could not update" in err

    def test_remove_alias_basic_strips_lines(self, tmp_path: Path) -> None:
        cfg = tmp_path / ".bashrc"
        cfg.write_text("export X=1\n# Tribunal\nalias tribunal=foo\nexport Y=2\n")
        _remove_alias_basic(cfg)
        content = cfg.read_text()
        assert "# Tribunal" not in content
        assert "alias tribunal" not in content
        assert "export X=1" in content
        assert "export Y=2" in content
