"""Tests for launcher.repair — re-sync hooks and settings into ~/.claude/settings.json."""

from __future__ import annotations

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

import pytest

from launcher.repair import _run_repair


@pytest.fixture()
def tmp_home(tmp_path: Path) -> Path:
    """Redirect plugin_root and claude_settings to tmp directories."""
    plugin_root = tmp_path / ".claude" / "tribunal"
    hooks_dir = plugin_root / "hooks"
    hooks_dir.mkdir(parents=True)
    (tmp_path / ".claude").mkdir(parents=True, exist_ok=True)
    return tmp_path


def _write_hooks(tmp_home: Path, hooks: dict) -> Path:
    hooks_path = tmp_home / ".claude" / "tribunal" / "hooks" / "hooks.json"
    hooks_path.write_text(json.dumps(hooks))
    return hooks_path


def _write_claude_settings(tmp_home: Path, settings: dict) -> Path:
    path = tmp_home / ".claude" / "settings.json"
    path.write_text(json.dumps(settings))
    return path


def _write_sf_settings(tmp_home: Path, settings: dict) -> Path:
    path = tmp_home / ".claude" / "tribunal" / "settings.json"
    path.write_text(json.dumps(settings))
    return path


class TestRunRepairNoHooks:
    def test_missing_hooks_file_prints_error(self, tmp_path: Path, capsys) -> None:
        plugin_root = str(tmp_path / ".claude" / "tribunal")
        claude_settings = str(tmp_path / ".claude" / "settings.json")
        with patch("launcher.repair._PLUGIN_ROOT", plugin_root):
            with patch("launcher.repair._CLAUDE_SETTINGS_PATH", claude_settings):
                _run_repair()
        captured = capsys.readouterr()
        assert "[!!]" in captured.out


class TestRunRepairHooksMerge:
    def test_hooks_merged_into_claude_settings(self, tmp_home: Path, capsys) -> None:
        hooks = {"hooks": {"PostToolUse": ["echo hi"]}}
        _write_hooks(tmp_home, hooks)
        _write_claude_settings(tmp_home, {"other": "value"})
        plugin_root = str(tmp_home / ".claude" / "tribunal")
        claude_settings = str(tmp_home / ".claude" / "settings.json")
        with patch("launcher.repair._PLUGIN_ROOT", plugin_root):
            with patch("launcher.repair._CLAUDE_SETTINGS_PATH", claude_settings):
                _run_repair()
        result = json.loads(Path(claude_settings).read_text())
        assert result["hooks"] == {"PostToolUse": ["echo hi"]}
        assert result["other"] == "value"

    def test_backup_created_when_settings_exist(self, tmp_home: Path, capsys) -> None:
        _write_hooks(tmp_home, {"hooks": {}})
        _write_claude_settings(tmp_home, {"x": 1})
        plugin_root = str(tmp_home / ".claude" / "tribunal")
        claude_settings = str(tmp_home / ".claude" / "settings.json")
        with patch("launcher.repair._PLUGIN_ROOT", plugin_root):
            with patch("launcher.repair._CLAUDE_SETTINGS_PATH", claude_settings):
                _run_repair()
        assert Path(claude_settings + ".bak").exists()

    def test_no_backup_when_settings_missing(self, tmp_home: Path, capsys) -> None:
        _write_hooks(tmp_home, {"hooks": {}})
        plugin_root = str(tmp_home / ".claude" / "tribunal")
        claude_settings = str(tmp_home / ".claude" / "settings.json")
        with patch("launcher.repair._PLUGIN_ROOT", plugin_root):
            with patch("launcher.repair._CLAUDE_SETTINGS_PATH", claude_settings):
                _run_repair()
        assert not Path(claude_settings + ".bak").exists()

    def test_plugin_root_substituted_in_hooks(self, tmp_home: Path, capsys) -> None:
        plugin_root = str(tmp_home / ".claude" / "tribunal")
        hooks = {"hooks": {"PostToolUse": ["$CLAUDE_PLUGIN_ROOT/script.py"]}}
        _write_hooks(tmp_home, hooks)
        claude_settings = str(tmp_home / ".claude" / "settings.json")
        with patch("launcher.repair._PLUGIN_ROOT", plugin_root):
            with patch("launcher.repair._CLAUDE_SETTINGS_PATH", claude_settings):
                _run_repair()
        result = json.loads(Path(claude_settings).read_text())
        assert plugin_root in result["hooks"]["PostToolUse"][0]
        assert "$CLAUDE_PLUGIN_ROOT" not in result["hooks"]["PostToolUse"][0]


class TestRunRepairSfSettingsMerge:
    def test_env_vars_added_from_sf_settings(self, tmp_home: Path, capsys) -> None:
        _write_hooks(tmp_home, {"hooks": {}})
        _write_sf_settings(tmp_home, {"env": {"MY_VAR": "hello"}})
        plugin_root = str(tmp_home / ".claude" / "tribunal")
        claude_settings = str(tmp_home / ".claude" / "settings.json")
        with patch("launcher.repair._PLUGIN_ROOT", plugin_root):
            with patch("launcher.repair._CLAUDE_SETTINGS_PATH", claude_settings):
                _run_repair()
        result = json.loads(Path(claude_settings).read_text())
        assert result["env"]["MY_VAR"] == "hello"

    def test_existing_env_vars_not_overwritten(self, tmp_home: Path, capsys) -> None:
        _write_hooks(tmp_home, {"hooks": {}})
        _write_sf_settings(tmp_home, {"env": {"MY_VAR": "new"}})
        _write_claude_settings(tmp_home, {"env": {"MY_VAR": "original"}})
        plugin_root = str(tmp_home / ".claude" / "tribunal")
        claude_settings = str(tmp_home / ".claude" / "settings.json")
        with patch("launcher.repair._PLUGIN_ROOT", plugin_root):
            with patch("launcher.repair._CLAUDE_SETTINGS_PATH", claude_settings):
                _run_repair()
        result = json.loads(Path(claude_settings).read_text())
        assert result["env"]["MY_VAR"] == "original"

    def test_permissions_allow_merged(self, tmp_home: Path, capsys) -> None:
        _write_hooks(tmp_home, {"hooks": {}})
        _write_sf_settings(tmp_home, {"permissions": {"allow": ["Bash(*)"]}, "env": {}})
        plugin_root = str(tmp_home / ".claude" / "tribunal")
        claude_settings = str(tmp_home / ".claude" / "settings.json")
        with patch("launcher.repair._PLUGIN_ROOT", plugin_root):
            with patch("launcher.repair._CLAUDE_SETTINGS_PATH", claude_settings):
                _run_repair()
        result = json.loads(Path(claude_settings).read_text())
        assert "Bash(*)" in result["permissions"]["allow"]

    def test_status_command_set_when_missing(self, tmp_home: Path, capsys) -> None:
        _write_hooks(tmp_home, {"hooks": {}})
        _write_sf_settings(tmp_home, {"statusCommand": "sfc statusline", "env": {}})
        plugin_root = str(tmp_home / ".claude" / "tribunal")
        claude_settings = str(tmp_home / ".claude" / "settings.json")
        with patch("launcher.repair._PLUGIN_ROOT", plugin_root):
            with patch("launcher.repair._CLAUDE_SETTINGS_PATH", claude_settings):
                _run_repair()
        result = json.loads(Path(claude_settings).read_text())
        assert result["statusCommand"] == "sfc statusline"

    def test_status_command_not_overwritten(self, tmp_home: Path, capsys) -> None:
        _write_hooks(tmp_home, {"hooks": {}})
        _write_sf_settings(tmp_home, {"statusCommand": "new-cmd", "env": {}})
        _write_claude_settings(tmp_home, {"statusCommand": "existing-cmd"})
        plugin_root = str(tmp_home / ".claude" / "tribunal")
        claude_settings = str(tmp_home / ".claude" / "settings.json")
        with patch("launcher.repair._PLUGIN_ROOT", plugin_root):
            with patch("launcher.repair._CLAUDE_SETTINGS_PATH", claude_settings):
                _run_repair()
        result = json.loads(Path(claude_settings).read_text())
        assert result["statusCommand"] == "existing-cmd"
