#!/usr/bin/env python3
"""
PostToolUse hook for bluera-knowledge plugin.

Fires after Claude fetches from source code hosting or package registry URLs.
Emits a mandatory reminder to index the library with BK if it will be used
in the project. Deduplicates per-project so the same repo only triggers once
per hour.
"""

import hashlib
import json
import os
import re
import sys
import time
from typing import TypedDict


class RepoInfo(TypedDict):
    """Extracted repository/package information from a URL."""

    canonical_url: str  # e.g., "https://github.com/ml-explore/mlx-lm"
    name: str  # e.g., "mlx-lm"
    command: str  # e.g., "/bluera-knowledge:add-repo https://github.com/ml-explore/mlx-lm"


# How long (seconds) before a seen repo can trigger again
DEDUP_TTL = 3600  # 1 hour


def extract_repo_info(url: str) -> RepoInfo | None:
    """Extract repository info from a URL if it matches a known source/registry site."""

    # GitHub: github.com/<org>/<repo>/...
    match = re.match(r"https?://github\.com/([^/]+)/([^/]+)", url)
    if match:
        org, repo = match.group(1), match.group(2)
        # Strip .git suffix if present
        repo = re.sub(r"\.git$", "", repo)
        canonical = f"https://github.com/{org}/{repo}"
        return RepoInfo(
            canonical_url=canonical,
            name=repo,
            command=f"/bluera-knowledge:add-repo {canonical}",
        )

    # raw.githubusercontent.com/<org>/<repo>/...
    match = re.match(r"https?://raw\.githubusercontent\.com/([^/]+)/([^/]+)", url)
    if match:
        org, repo = match.group(1), match.group(2)
        canonical = f"https://github.com/{org}/{repo}"
        return RepoInfo(
            canonical_url=canonical,
            name=repo,
            command=f"/bluera-knowledge:add-repo {canonical}",
        )

    # PyPI: pypi.org/project/<package>/...
    match = re.match(r"https?://pypi\.org/project/([^/]+)", url)
    if match:
        name = match.group(1).rstrip("/")
        return RepoInfo(
            canonical_url=f"https://pypi.org/project/{name}",
            name=name,
            command=f"/bluera-knowledge:add-repo {name}",
        )

    # npm: (www.)npmjs.com/package/<package>/...
    match = re.match(r"https?://(?:www\.)?npmjs\.com/package/(@?[^/]+(?:/[^/]+)?)", url)
    if match:
        name = match.group(1)
        return RepoInfo(
            canonical_url=f"https://www.npmjs.com/package/{name}",
            name=name,
            command=f"/bluera-knowledge:add-repo {name}",
        )

    # crates.io: crates.io/crates/<crate>/...
    match = re.match(r"https?://crates\.io/crates/([^/]+)", url)
    if match:
        name = match.group(1)
        return RepoInfo(
            canonical_url=f"https://crates.io/crates/{name}",
            name=name,
            command=f"/bluera-knowledge:add-repo {name}",
        )

    # pkg.go.dev: pkg.go.dev/<module>
    match = re.match(r"https?://pkg\.go\.dev/(.+?)(?:\?|$|#)", url)
    if match:
        module = match.group(1).rstrip("/")
        return RepoInfo(
            canonical_url=f"https://pkg.go.dev/{module}",
            name=module.split("/")[-1],
            command=f"/bluera-knowledge:add-repo {module}",
        )

    # docs.rs: docs.rs/<crate>/...
    match = re.match(r"https?://docs\.rs/([^/]+)", url)
    if match:
        name = match.group(1)
        return RepoInfo(
            canonical_url=f"https://docs.rs/{name}",
            name=name,
            command=f"/bluera-knowledge:add-repo {name}",
        )

    # readthedocs: <project>.readthedocs.io/...
    match = re.match(r"https?://([^.]+)\.readthedocs\.io", url)
    if match:
        name = match.group(1)
        return RepoInfo(
            canonical_url=f"https://{name}.readthedocs.io",
            name=name,
            command=f"/bluera-knowledge:crawl https://{name}.readthedocs.io",
        )

    return None


def _dedup_path(project_root: str) -> str:
    """Return the path to the per-project dedup file."""
    h = hashlib.md5(project_root.encode()).hexdigest()[:8]
    return f"/tmp/bk-web-research-{h}.json"


def is_already_seen(repo_key: str, project_root: str) -> bool:
    """Check if this repo was already flagged within the TTL."""
    try:
        path = _dedup_path(project_root)
        with open(path) as f:
            seen: dict[str, float] = json.load(f)
        ts = seen.get(repo_key, 0)
        return (time.time() - ts) < DEDUP_TTL
    except Exception:
        return False


def mark_seen(repo_key: str, project_root: str) -> None:
    """Record this repo as seen, pruning expired entries."""
    try:
        path = _dedup_path(project_root)
        now = time.time()
        seen: dict[str, float] = {}
        try:
            with open(path) as f:
                seen = json.load(f)
        except Exception:
            pass
        # Prune expired
        seen = {k: v for k, v in seen.items() if (now - v) < DEDUP_TTL}
        seen[repo_key] = now
        with open(path, "w") as f:
            json.dump(seen, f)
    except Exception:
        pass  # Never fail


def main() -> int:
    try:
        stdin_data = sys.stdin.read()
        if not stdin_data.strip():
            return 0
        hook_input = json.loads(stdin_data)

        tool_name = hook_input.get("tool_name", "")
        tool_input = hook_input.get("tool_input", {})

        if tool_name != "WebFetch":
            return 0

        url = tool_input.get("url", "")
        if not url:
            return 0

        info = extract_repo_info(url)
        if not info:
            return 0

        project_root = os.environ.get("PROJECT_ROOT", os.environ.get("PWD", ""))

        if is_already_seen(info["canonical_url"], project_root):
            return 0

        mark_seen(info["canonical_url"], project_root)

        message = (
            f"MANDATORY: You fetched documentation from {info['name']}.\n"
            f"If this library will be used in the project, index it BEFORE continuing:\n"
            f"  {info['command']}\n"
            f"This gives you authoritative, searchable access instead of raw web fetches."
        )

        output = {
            "hookSpecificOutput": {
                "hookEventName": "PostToolUse",
                "additionalContext": message,
            }
        }
        print(json.dumps(output))
        return 0

    except Exception:
        return 0  # Never fail on errors


if __name__ == "__main__":
    raise SystemExit(main())
