"""Tests for MCP and hooks auto-injection."""

from __future__ import annotations

import json
import sys

from syke.distribution.inject import (
    inject_hooks_config,
    inject_mcp_config,
    inject_mcp_config_desktop,
    inject_mcp_config_project,
)


def test_inject_mcp_config_creates_new(tmp_path, monkeypatch):
    """Creates settings.json if it doesn't exist and adds syke MCP server."""
    settings_path = tmp_path / ".claude" / "settings.json"
    monkeypatch.setattr("syke.distribution.inject.CLAUDE_SETTINGS_PATH", settings_path)

    result = inject_mcp_config("testuser", cwd=tmp_path / "project")
    assert result == settings_path
    assert settings_path.exists()

    settings = json.loads(settings_path.read_text())
    assert "syke" in settings["mcpServers"]
    assert settings["mcpServers"]["syke"]["args"][3] == "testuser"
    assert "cwd" in settings["mcpServers"]["syke"]


def test_inject_mcp_config_merges(tmp_path, monkeypatch):
    """Preserves existing MCP servers when adding syke."""
    settings_path = tmp_path / ".claude" / "settings.json"
    settings_path.parent.mkdir(parents=True)
    settings_path.write_text(json.dumps({
        "mcpServers": {
            "other-server": {"command": "other", "args": []}
        },
        "someOtherSetting": True,
    }))
    monkeypatch.setattr("syke.distribution.inject.CLAUDE_SETTINGS_PATH", settings_path)

    inject_mcp_config("testuser", cwd=tmp_path / "project")

    settings = json.loads(settings_path.read_text())
    assert "other-server" in settings["mcpServers"]
    assert "syke" in settings["mcpServers"]
    assert settings["someOtherSetting"] is True


def test_inject_mcp_config_no_cwd(tmp_path, monkeypatch):
    """Omits cwd key from config when cwd is None (pip install path)."""
    settings_path = tmp_path / ".claude" / "settings.json"
    monkeypatch.setattr("syke.distribution.inject.CLAUDE_SETTINGS_PATH", settings_path)

    inject_mcp_config("testuser")

    settings = json.loads(settings_path.read_text())
    assert "syke" in settings["mcpServers"]
    assert "cwd" not in settings["mcpServers"]["syke"]
    assert settings["mcpServers"]["syke"]["args"][3] == "testuser"


def test_inject_mcp_config_with_cwd(tmp_path, monkeypatch):
    """Includes cwd key in config when cwd is provided (source install path)."""
    settings_path = tmp_path / ".claude" / "settings.json"
    monkeypatch.setattr("syke.distribution.inject.CLAUDE_SETTINGS_PATH", settings_path)

    project = tmp_path / "myproject"
    inject_mcp_config("testuser", cwd=project)

    settings = json.loads(settings_path.read_text())
    assert settings["mcpServers"]["syke"]["cwd"] == str(project)


def test_inject_hooks_creates_new(tmp_path, monkeypatch):
    """Creates settings.json with hooks if it doesn't exist."""
    settings_path = tmp_path / ".claude" / "settings.json"
    monkeypatch.setattr("syke.distribution.inject.CLAUDE_SETTINGS_PATH", settings_path)

    result = inject_hooks_config(tmp_path / "project")
    assert result == settings_path

    settings = json.loads(settings_path.read_text())
    assert "SessionStart" in settings["hooks"]
    assert "Stop" in settings["hooks"]
    assert len(settings["hooks"]["SessionStart"]) == 1
    assert len(settings["hooks"]["Stop"]) == 1
    assert "syke-session-start" in settings["hooks"]["SessionStart"][0]["command"]
    assert "syke-session-stop" in settings["hooks"]["Stop"][0]["command"]


def test_inject_hooks_merges(tmp_path, monkeypatch):
    """Preserves existing hooks when adding syke hooks."""
    settings_path = tmp_path / ".claude" / "settings.json"
    settings_path.parent.mkdir(parents=True)
    settings_path.write_text(json.dumps({
        "hooks": {
            "SessionStart": [
                {"type": "command", "command": "echo existing-hook"}
            ],
            "PreToolUse": [
                {"type": "command", "command": "echo pre-tool"}
            ],
        }
    }))
    monkeypatch.setattr("syke.distribution.inject.CLAUDE_SETTINGS_PATH", settings_path)

    inject_hooks_config(tmp_path / "project")

    settings = json.loads(settings_path.read_text())
    # Existing SessionStart hook preserved
    commands = [h["command"] for h in settings["hooks"]["SessionStart"]]
    assert "echo existing-hook" in commands
    assert any("syke-session-start" in c for c in commands)
    # PreToolUse untouched
    assert len(settings["hooks"]["PreToolUse"]) == 1
    # Stop hook added
    assert len(settings["hooks"]["Stop"]) == 1


def test_inject_hooks_idempotent(tmp_path, monkeypatch):
    """Running inject_hooks_config twice doesn't duplicate syke hooks."""
    settings_path = tmp_path / ".claude" / "settings.json"
    monkeypatch.setattr("syke.distribution.inject.CLAUDE_SETTINGS_PATH", settings_path)

    inject_hooks_config(tmp_path / "project")
    inject_hooks_config(tmp_path / "project")

    settings = json.loads(settings_path.read_text())
    # Should have exactly 1 syke hook per event, not 2
    assert len(settings["hooks"]["SessionStart"]) == 1
    assert len(settings["hooks"]["Stop"]) == 1


# --- Claude Desktop MCP injection tests ---


def test_inject_mcp_config_desktop_creates_new(tmp_path, monkeypatch):
    """Creates claude_desktop_config.json if it doesn't exist and adds syke MCP server."""
    config_path = tmp_path / "Claude" / "claude_desktop_config.json"
    config_path.parent.mkdir(parents=True)
    monkeypatch.setattr("syke.distribution.inject.CLAUDE_DESKTOP_CONFIG_PATH", config_path)
    monkeypatch.setattr("syke.distribution.inject.sys", type(sys)("fake_sys"))
    # Need to set platform to darwin
    import types
    fake_sys = types.ModuleType("fake_sys")
    fake_sys.platform = "darwin"
    fake_sys.executable = sys.executable
    monkeypatch.setattr("syke.distribution.inject.sys", fake_sys)

    result = inject_mcp_config_desktop("testuser", cwd=tmp_path / "project")
    assert result == config_path
    assert config_path.exists()

    config = json.loads(config_path.read_text())
    assert "syke" in config["mcpServers"]
    assert config["mcpServers"]["syke"]["args"][3] == "testuser"
    assert "cwd" in config["mcpServers"]["syke"]


def test_inject_mcp_config_desktop_merges(tmp_path, monkeypatch):
    """Preserves existing MCP servers when adding syke to Claude Desktop."""
    config_path = tmp_path / "Claude" / "claude_desktop_config.json"
    config_path.parent.mkdir(parents=True)
    config_path.write_text(json.dumps({
        "mcpServers": {
            "other-server": {"command": "other", "args": []}
        },
        "globalShortcut": "Ctrl+Space",
    }))

    import types
    fake_sys = types.ModuleType("fake_sys")
    fake_sys.platform = "darwin"
    fake_sys.executable = sys.executable
    monkeypatch.setattr("syke.distribution.inject.sys", fake_sys)
    monkeypatch.setattr("syke.distribution.inject.CLAUDE_DESKTOP_CONFIG_PATH", config_path)

    inject_mcp_config_desktop("testuser", cwd=tmp_path / "project")

    config = json.loads(config_path.read_text())
    assert "other-server" in config["mcpServers"]
    assert "syke" in config["mcpServers"]
    assert config["globalShortcut"] == "Ctrl+Space"


def test_inject_mcp_config_desktop_no_cwd(tmp_path, monkeypatch):
    """Omits cwd key from desktop config when cwd is None."""
    config_path = tmp_path / "Claude" / "claude_desktop_config.json"
    config_path.parent.mkdir(parents=True)

    import types
    fake_sys = types.ModuleType("fake_sys")
    fake_sys.platform = "darwin"
    fake_sys.executable = sys.executable
    monkeypatch.setattr("syke.distribution.inject.sys", fake_sys)
    monkeypatch.setattr("syke.distribution.inject.CLAUDE_DESKTOP_CONFIG_PATH", config_path)

    inject_mcp_config_desktop("testuser")

    config = json.loads(config_path.read_text())
    assert "syke" in config["mcpServers"]
    assert "cwd" not in config["mcpServers"]["syke"]


def test_inject_mcp_config_desktop_skips_non_darwin(tmp_path, monkeypatch):
    """Returns None on non-macOS platforms."""
    import types
    fake_sys = types.ModuleType("fake_sys")
    fake_sys.platform = "linux"
    fake_sys.executable = sys.executable
    monkeypatch.setattr("syke.distribution.inject.sys", fake_sys)

    result = inject_mcp_config_desktop("testuser", cwd=tmp_path / "project")
    assert result is None


# --- Project-level MCP injection tests ---


def test_inject_mcp_config_project_merges(tmp_path):
    """Creates syke entry in existing project-level .claude/settings.json."""
    project_root = tmp_path / "project"
    settings_path = project_root / ".claude" / "settings.json"
    settings_path.parent.mkdir(parents=True)
    settings_path.write_text(json.dumps({
        "permissions": {
            "allow": ["Bash(python -m pytest*)"]
        },
        "mcpServers": {
            "other-server": {"command": "other", "args": []}
        },
    }))

    result = inject_mcp_config_project("testuser", project_root)
    assert result == settings_path

    settings = json.loads(settings_path.read_text())
    # Syke entry added with relative .venv path
    assert "syke" in settings["mcpServers"]
    assert settings["mcpServers"]["syke"]["command"] == ".venv/bin/python"
    assert settings["mcpServers"]["syke"]["args"][3] == "testuser"
    # Existing entries preserved
    assert "other-server" in settings["mcpServers"]
    assert settings["permissions"]["allow"] == ["Bash(python -m pytest*)"]


def test_inject_mcp_config_project_skips_missing(tmp_path):
    """Returns None when no project-level .claude/settings.json exists."""
    project_root = tmp_path / "project"
    project_root.mkdir()

    result = inject_mcp_config_project("testuser", project_root)
    assert result is None
