"""
Hook auto-generator for PULSE brain-first enforcement.

Detects installed AI CLI tools and generates the correct hook configs
so every agent gets brain knowledge before writing code.
Merges into existing configs without overwriting user settings.
"""

import json
import shutil
from pathlib import Path

PULSE_MARKER = "pulse.brain.brain_query"
MCP_SERVER_KEY = "pulse-brain"
_BRAIN_CMD = "python -m pulse.brain.brain_query"
_MCP_CMD = ["-m", "pulse.mcp.server"]


def _tool_available(name: str) -> bool:
    return shutil.which(name) is not None


def _detect_tools() -> dict[str, bool]:
    return {t: _tool_available(t) for t in ("claude", "gemini", "codex", "cursor")}


def _claude_settings() -> dict:
    hook = {"type": "command", "command": _BRAIN_CMD, "timeout": 5}
    return {"hooks": {
        "PreToolUse": [{"matcher": "Edit|Write", "hooks": [hook]}],
        "UserPromptSubmit": [{"hooks": [hook]}],
    }}


def _gemini_settings() -> dict:
    return {"hooks": {"BeforeTool": [{
        "matcher": "write_file|replace|create_file|edit_file|patch",
        "hooks": [{"type": "command", "command": "python .gemini/hooks/brain_hook.py",
                    "name": "brain-first", "timeout": 10000,
                    "description": "Query PULSE brain before file modifications"}],
    }]}}


GEMINI_HOOK_SCRIPT = '''\
#!/usr/bin/env python3
"""Gemini CLI BeforeTool hook — wraps brain_query output for Gemini JSON protocol."""
import json, subprocess, sys

result = subprocess.run(
    [sys.executable, "-m", "pulse.brain.brain_query", "--query", "pre-write check"],
    capture_output=True, text=True, timeout=10, encoding="utf-8", errors="replace",
)
brain = result.stdout.strip() if result.stdout else ""
output = {"decision": "approve"}
if brain and brain != "No brain results.":
    output["hookSpecificOutput"] = {"additionalContext": f"[BRAIN] {brain[:500]}"}
json.dump(output, sys.stdout)
'''

CODEX_INSTRUCTIONS = (
    "Before ANY code edit, run: python -m pulse.brain.brain_query --query '<your task>' "
    "and apply the knowledge. After completing work, run: "
    "python -m pulse.brain.pulse_save --agent codex --learnings '<what worked>' "
    "--failures '<what failed>'. This is non-negotiable."
)

# MCP server entries for each tool format
_MCP_CONFIGS = [
    (".mcp.json", "mcpServers",
     {"command": "python", "args": _MCP_CMD, "cwd": "."}),
    (".cursor/mcp.json", "mcpServers",
     {"command": "python", "args": _MCP_CMD}),
    (".vscode/mcp.json", "servers",
     {"type": "stdio", "command": "python", "args": _MCP_CMD}),
]


# --- Merge helpers ---

def _read_json(path: Path) -> dict:
    if not path.exists():
        return {}
    try:
        return json.loads(path.read_text(encoding="utf-8"))
    except (json.JSONDecodeError, OSError):
        return {}


def _has_pulse_hook(data: dict) -> bool:
    return PULSE_MARKER in json.dumps(data)


def _has_pulse_mcp(data: dict) -> bool:
    return any(MCP_SERVER_KEY in data.get(k, {}) for k in ("mcpServers", "servers"))


def _merge_claude(existing: dict) -> dict:
    hooks = existing.setdefault("hooks", {})
    pulse_hook = {"type": "command", "command": _BRAIN_CMD, "timeout": 5}
    hooks.setdefault("PreToolUse", []).append(
        {"matcher": "Edit|Write", "hooks": [pulse_hook]})
    hooks.setdefault("UserPromptSubmit", []).append({"hooks": [pulse_hook]})
    return existing


def _merge_gemini(existing: dict) -> dict:
    hooks = existing.setdefault("hooks", {})
    hooks.setdefault("BeforeTool", []).append({
        "matcher": "write_file|replace|create_file|edit_file|patch",
        "hooks": [{"type": "command", "command": "python .gemini/hooks/brain_hook.py",
                    "name": "brain-first", "timeout": 10000,
                    "description": "Query PULSE brain before file modifications"}],
    })
    return existing


def _write_json(path: Path, data: dict) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(data, indent=2) + "\n", encoding="utf-8")


def _write_text(path: Path, content: str) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(content, encoding="utf-8")


def _handle_hook_config(path, existing, has_pulse, merge_fn, build_fn, label, gen, mrg, skip):
    """Route: skip if PULSE present, merge if user config exists, create if fresh."""
    if has_pulse:
        skip.append(f"{label} (PULSE already present)")
    elif existing:
        _write_json(path, merge_fn())
        mrg.append(f"{label} (merged)")
    else:
        _write_json(path, build_fn())
        gen.append(label)


# --- Main generator ---

def generate_hooks(project_root: Path) -> dict:
    """Generate or merge hook configs for all supported AI tools.

    Returns: {"generated": [...], "merged": [...], "skipped": [...], "tools": {...}}
    """
    tools = _detect_tools()
    gen, mrg, skip = [], [], []

    # Claude Code
    p = project_root / ".claude" / "settings.json"
    ex = _read_json(p)
    _handle_hook_config(p, ex, _has_pulse_hook(ex),
                        lambda: _merge_claude(ex), _claude_settings,
                        "Claude Code hooks (.claude/settings.json)", gen, mrg, skip)

    # Gemini CLI
    p = project_root / ".gemini" / "settings.json"
    ex = _read_json(p)
    _handle_hook_config(p, ex, _has_pulse_hook(ex),
                        lambda: _merge_gemini(ex), _gemini_settings,
                        "Gemini CLI hooks (.gemini/settings.json)", gen, mrg, skip)

    # Gemini hook script
    hp = project_root / ".gemini" / "hooks" / "brain_hook.py"
    if hp.exists() and PULSE_MARKER in hp.read_text(encoding="utf-8"):
        skip.append("Gemini hook script (already exists)")
    else:
        _write_text(hp, GEMINI_HOOK_SCRIPT)
        gen.append("Gemini hook script (.gemini/hooks/brain_hook.py)")

    # MCP configs (Claude Code, Cursor, VS Code)
    for rel_path, container_key, server in _MCP_CONFIGS:
        p = project_root / rel_path
        ex = _read_json(p)
        label = f"MCP ({rel_path})"
        if _has_pulse_mcp(ex):
            skip.append(f"{label} (PULSE already present)")
        elif ex:
            ex.setdefault(container_key, {})[MCP_SERVER_KEY] = server
            _write_json(p, ex)
            mrg.append(f"{label} (merged)")
        else:
            _write_json(p, {container_key: {MCP_SERVER_KEY: server}})
            gen.append(label)

    # Codex instructions
    cf = project_root / "codex.md"
    if cf.exists() and PULSE_MARKER in cf.read_text(encoding="utf-8"):
        skip.append("Codex instructions (already exists)")
    elif cf.exists():
        with open(cf, "a", encoding="utf-8") as f:
            f.write(f"\n\n# PULSE Brain Integration\n\n{CODEX_INSTRUCTIONS}\n")
        mrg.append("Codex instructions (appended to codex.md)")
    else:
        _write_text(cf, f"# PULSE Brain Integration\n\n{CODEX_INSTRUCTIONS}\n")
        gen.append("Codex instructions (codex.md)")

    return {"generated": gen, "merged": mrg, "skipped": skip, "tools": tools}
