"""Tests for plugin.py — Claude Code plugin install/uninstall"""

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

import pytest

from index1.plugin import (
    _HOOKS_JSON,
    _cleanup_manual_hooks,
    _get_mcp_json,
    _get_plugin_json,
    check_status,
    install_plugin,
    uninstall_plugin,
)


@pytest.fixture
def plugin_env(tmp_path):
    """Mock plugin environment with temp directories"""
    marketplace_dir = tmp_path / "plugins" / "marketplaces" / "index1-marketplace" / "plugins" / "index1"
    settings_path = tmp_path / "settings.json"
    settings_path.write_text(json.dumps({
        "hooks": {},
        "enabledPlugins": {},
    }))

    with (
        patch("index1.plugin._MARKETPLACE_DIR", marketplace_dir),
        patch("index1.plugin._SETTINGS_PATH", settings_path),
    ):
        yield {
            "marketplace_dir": marketplace_dir,
            "settings_path": settings_path,
        }


class TestInstallPlugin:
    def test_creates_directory_structure(self, plugin_env):
        plugin_dir = install_plugin()
        assert (plugin_dir / ".claude-plugin" / "plugin.json").exists()
        assert (plugin_dir / "hooks" / "hooks.json").exists()
        assert (plugin_dir / ".mcp.json").exists()
        assert (plugin_dir / "CLAUDE.md").exists()

    def test_hooks_json_content(self, plugin_env):
        install_plugin()
        hooks_path = plugin_env["marketplace_dir"] / "hooks" / "hooks.json"
        data = json.loads(hooks_path.read_text())
        hooks = data["hooks"]
        assert "SessionStart" in hooks
        assert "UserPromptSubmit" in hooks
        assert "PostToolUse" in hooks
        assert "SessionEnd" in hooks
        # All use same command
        for event, configs in hooks.items():
            for config in configs:
                for h in config["hooks"]:
                    assert h["command"] == "index1 hook"

    def test_mcp_json_content(self, plugin_env):
        install_plugin()
        mcp_path = plugin_env["marketplace_dir"] / ".mcp.json"
        data = json.loads(mcp_path.read_text())
        assert "index1" in data["mcpServers"]
        assert data["mcpServers"]["index1"]["command"] == "index1"

    def test_enables_plugin(self, plugin_env):
        install_plugin()
        settings = json.loads(plugin_env["settings_path"].read_text())
        assert settings["enabledPlugins"]["index1@index1-marketplace"] is True

    def test_cleans_manual_hooks(self, plugin_env):
        # Pre-add manual hooks
        settings = json.loads(plugin_env["settings_path"].read_text())
        settings["hooks"]["PostToolUse"] = [{
            "hooks": [{"type": "command", "command": "index1 hook PostToolUse"}]
        }]
        plugin_env["settings_path"].write_text(json.dumps(settings))

        install_plugin()

        settings = json.loads(plugin_env["settings_path"].read_text())
        # Manual hooks should be cleaned
        assert "PostToolUse" not in settings["hooks"]

    def test_idempotent(self, plugin_env):
        install_plugin()
        install_plugin()  # Second call should not fail
        settings = json.loads(plugin_env["settings_path"].read_text())
        assert settings["enabledPlugins"]["index1@index1-marketplace"] is True


class TestUninstallPlugin:
    def test_removes_hooks_dir(self, plugin_env):
        install_plugin()
        assert (plugin_env["marketplace_dir"] / "hooks").exists()
        uninstall_plugin()
        assert not (plugin_env["marketplace_dir"] / "hooks").exists()

    def test_removes_enabled(self, plugin_env):
        install_plugin()
        uninstall_plugin()
        settings = json.loads(plugin_env["settings_path"].read_text())
        assert "index1@index1-marketplace" not in settings.get("enabledPlugins", {})

    def test_uninstall_without_install(self, plugin_env):
        # Should not raise
        result = uninstall_plugin()
        assert result is True


class TestCleanupManualHooks:
    def test_removes_index1_hooks_only(self, plugin_env):
        settings = json.loads(plugin_env["settings_path"].read_text())
        settings["hooks"] = {
            "PostToolUse": [
                {"hooks": [{"type": "command", "command": "bun some-other-hook"}]},
                {"hooks": [{"type": "command", "command": "index1 hook PostToolUse"}]},
            ]
        }
        plugin_env["settings_path"].write_text(json.dumps(settings))

        _cleanup_manual_hooks()

        settings = json.loads(plugin_env["settings_path"].read_text())
        configs = settings["hooks"]["PostToolUse"]
        assert len(configs) == 1
        assert "bun" in configs[0]["hooks"][0]["command"]

    def test_handles_missing_settings(self, plugin_env):
        plugin_env["settings_path"].unlink()
        _cleanup_manual_hooks()  # Should not raise


class TestCheckStatus:
    def test_not_installed(self, plugin_env):
        status = check_status()
        assert not status["hooks_json"]
        assert not status["plugin_json"]

    def test_installed(self, plugin_env):
        install_plugin()
        status = check_status()
        assert status["hooks_json"]
        assert status["plugin_json"]
        assert status["enabled"]
        assert not status["manual_hooks"]


class TestHookAutoDetect:
    """Test hook_main auto-detects event from stdin"""

    def test_auto_detect_event(self, tmp_path):
        """hook_main reads hook_event_name from stdin when event=None"""
        from io import StringIO
        from unittest.mock import MagicMock, patch as _patch

        data = {
            "hook_event_name": "PostToolUse",
            "tool_name": "Bash",
            "tool_input": {"command": "echo test"},
            "tool_response": "",
            "cwd": str(tmp_path),
        }

        with (
            _patch("sys.stdin", StringIO(json.dumps(data))),
            _patch("index1.cognitive.hooks.load_config") as mock_config,
        ):
            mock_config.return_value = MagicMock(cognitive_enabled=False)
            from index1.cognitive.hooks import hook_main
            hook_main(None)  # No event argument — auto-detect

    def test_explicit_event_still_works(self, tmp_path):
        """hook_main still accepts explicit event argument"""
        from io import StringIO
        from unittest.mock import MagicMock, patch as _patch

        data = {"cwd": str(tmp_path)}

        with (
            _patch("sys.stdin", StringIO(json.dumps(data))),
            _patch("index1.cognitive.hooks.load_config") as mock_config,
        ):
            mock_config.return_value = MagicMock(cognitive_enabled=False)
            from index1.cognitive.hooks import hook_main
            hook_main("PostToolUse")  # Explicit — should work


class TestPluginJson:
    def test_plugin_json_fields(self):
        pj = _get_plugin_json()
        assert pj["name"] == "index1"
        assert "version" in pj
        assert "description" in pj

    def test_mcp_json_fields(self):
        mj = _get_mcp_json()
        assert "index1" in mj["mcpServers"]
        assert mj["mcpServers"]["index1"]["args"] == ["serve"]

    def test_hooks_json_has_all_events(self):
        hooks = _HOOKS_JSON["hooks"]
        assert set(hooks.keys()) == {
            "SessionStart", "UserPromptSubmit", "PostToolUse", "SessionEnd",
        }
