# Copyright (C) 2026 GoodData Corporation

"""Lifecycle hook installation for supported AI tools."""

from __future__ import annotations

from pathlib import Path
from typing import Any, cast

import orjson

# Marker prefix used to identify AIDA-managed hooks for idempotent updates.
AIDA_HOOK_MARKER = "[AIDA]"

_CLAUDE_GET_RULES_INSTRUCTION = (
    f"{AIDA_HOOK_MARKER} Call get_rules when the user's request relates to this repository "
    "— new tasks, code changes, questions about the codebase, releases, documentation. "
    "Skip for commit/push/PR-only requests and for questions unrelated to this repository. "
    "Use a concrete query matching the user's request. "
    "Reuse a stable session_id for the whole conversation and pass delta_cursor from the previous response."
)


def install_hooks_for_claude_project(project_root: Path) -> None:
    """Install AIDA UserPromptSubmit hook into ``.claude/settings.local.json``."""
    dest = project_root / ".claude" / "settings.local.json"
    data = _read_json(dest)
    hooks = _ensure_dict(data, "hooks")
    ups_list = _ensure_list(hooks, "UserPromptSubmit")
    _upsert_aida_hook(ups_list)
    _write_json(dest, data)


def has_aida_claude_hooks(project_root: Path) -> bool:
    """Return True if the AIDA UserPromptSubmit hook is already configured."""
    dest = project_root / ".claude" / "settings.local.json"
    if not dest.exists():
        return False
    data = _read_json(dest)
    hooks = data.get("hooks")
    if not isinstance(hooks, dict):
        return False
    ups_list = hooks.get("UserPromptSubmit")
    if not isinstance(ups_list, list):
        return False
    return _find_aida_hook_index(ups_list) is not None


# ---------------------------------------------------------------------------
# Internal helpers
# ---------------------------------------------------------------------------


def _read_json(path: Path) -> dict[str, Any]:
    if not path.exists():
        return {}
    try:
        loaded = orjson.loads(path.read_text(encoding="utf-8"))
        if isinstance(loaded, dict):
            return cast(dict[str, Any], loaded)
    except Exception:
        pass
    return {}


def _write_json(path: Path, data: dict[str, Any]) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(orjson.dumps(data, option=orjson.OPT_INDENT_2).decode("utf-8") + "\n", encoding="utf-8")


def _ensure_dict(parent: dict[str, Any], key: str) -> dict[str, Any]:
    val = parent.get(key)
    if isinstance(val, dict):
        return cast(dict[str, Any], val)
    new: dict[str, Any] = {}
    parent[key] = new
    return new


def _ensure_list(parent: dict[str, Any], key: str) -> list[Any]:
    val = parent.get(key)
    if isinstance(val, list):
        return cast(list[Any], val)
    new: list[Any] = []
    parent[key] = new
    return new


def get_hook_prompt(event: str) -> str:
    """Return the prompt text for a given hook event.

    Supported events: ``user-prompt-submit``.
    """
    if event == "user-prompt-submit":
        return _CLAUDE_GET_RULES_INSTRUCTION
    raise ValueError(f"Unknown hook event: {event}")


def _make_aida_hook_entry() -> dict[str, Any]:
    # Use a command hook that delegates to `aida-mcp hook-prompt` so the
    # prompt text lives in the installed package, not inlined in settings.json.
    # Upgrading aida-mcp automatically upgrades the prompt.
    return {
        "matcher": "",
        "hooks": [
            {
                "type": "command",
                "command": "aida-mcp hook-prompt user-prompt-submit",
            }
        ],
    }


_AIDA_HOOK_SIGNATURES = (AIDA_HOOK_MARKER, "aida-mcp hook-prompt")


def _find_aida_hook_index(ups_list: list[Any]) -> int | None:
    for i, entry in enumerate(ups_list):
        if not isinstance(entry, dict):
            continue
        inner_hooks = entry.get("hooks")
        if not isinstance(inner_hooks, list):
            continue
        for hook in inner_hooks:
            if not isinstance(hook, dict):
                continue
            for field in ("prompt", "command"):
                value = hook.get(field, "")
                if isinstance(value, str) and any(sig in value for sig in _AIDA_HOOK_SIGNATURES):
                    return i
    return None


def _upsert_aida_hook(ups_list: list[Any]) -> None:
    idx = _find_aida_hook_index(ups_list)
    entry = _make_aida_hook_entry()
    if idx is not None:
        ups_list[idx] = entry
    else:
        ups_list.append(entry)
