#!/usr/bin/env python3
"""
Pre-Commit Advisory Report — Consolidated advisory checks.

Replaces multiple separate advisory hooks with one unified process.
Runs built-in checks in order (fast first, slow last), collects output,
deduplicates silent results, and prints a single advisory report.

Always exits 0 — advisory only, never blocks commits.

Projects can extend the built-in check list by placing an overlay at:
    <PROJECT>/scripts/docs/advisory_config.py
The overlay must define EXTRA_CHECKS as a list of dicts with the same
schema as the built-in registry below.

Usage:
    pre_commit_advisory.py          # Run all advisory checks
    pre_commit_advisory.py --quiet  # Suppress the report header/footer

Exit codes:
    0 — Always (advisory only)
"""
import re
import subprocess
import sys
import time
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]

# ---------------------------------------------------------------------------
# Built-in check registry (order matters: fast checks first)
# ---------------------------------------------------------------------------
CHECKS = [
    {
        "name": "Bulk Change",
        "cmd": ["bulk_change_guard.py", "--check-commit"],
        "timeout": 5,
    },
    {
        "name": "Spec Gate",
        "cmd": ["check_spec.py"],
        "timeout": 5,
    },
    {
        "name": "Provenance Log",
        "cmd": ["provenance_log.py"],
        "timeout": 5,
    },
    {
        "name": "Doc Impact",
        "cmd": ["enforce_doc_impact.py", "--staged"],
        "timeout": 10,
    },
    {
        "name": "Perf Guardrails",
        "cmd": ["perf_guardrails.py"],
        "timeout": 10,
    },
    {
        "name": "Vertical Slice",
        "cmd": ["vertical_slice_check.py"],
        "timeout": 5,
    },
]

# Patterns that indicate "nothing to report" — case-insensitive
SILENT_PATTERNS = re.compile(
    r"^(?:no issues found|no doc updates|no staged files|pass|ok|)\s*$",
    re.IGNORECASE,
)

# ---------------------------------------------------------------------------
# Optional config overlay
# ---------------------------------------------------------------------------

def load_overlay() -> list[dict]:
    """Load extra checks from project overlay if it exists."""
    overlay_path = ROOT / "scripts" / "docs" / "advisory_config.py"
    if not overlay_path.is_file():
        return []
    ns: dict = {}
    try:
        exec(compile(overlay_path.read_text(encoding="utf-8"), str(overlay_path), "exec"), ns)
    except Exception as exc:
        print(f"[advisory] Warning: failed to load overlay: {exc}", file=sys.stderr)
        return []
    return ns.get("EXTRA_CHECKS", [])


# ---------------------------------------------------------------------------
# Runner
# ---------------------------------------------------------------------------

def is_silent(output: str) -> bool:
    """Return True if every non-empty line matches a silent pattern."""
    lines = output.strip().splitlines()
    if not lines:
        return True
    return all(SILENT_PATTERNS.match(line.strip()) for line in lines)


def run_check(check: dict) -> tuple[str, str, float]:
    """Run a single advisory check and return (name, output, elapsed)."""
    name = check["name"]
    cmd = check["cmd"]
    timeout = check.get("timeout", 10)

    # Resolve script path relative to this script's directory
    script = ROOT / "scripts" / cmd[0]
    if not script.is_file():
        return name, "", 0.0

    full_cmd = [sys.executable, str(script)] + cmd[1:]

    t0 = time.monotonic()
    try:
        proc = subprocess.run(
            full_cmd,
            capture_output=True,
            text=True,
            cwd=ROOT,
            timeout=timeout,
        )
        output = (proc.stdout + proc.stderr).strip()
    except subprocess.TimeoutExpired:
        output = f"[timed out after {timeout}s]"
    except Exception as exc:
        output = f"[error: {exc}]"
    elapsed = time.monotonic() - t0

    return name, output, elapsed


def main() -> int:
    quiet = "--quiet" in sys.argv

    all_checks = CHECKS + load_overlay()

    results: list[tuple[str, str, float]] = []
    total_time = 0.0

    for check in all_checks:
        name, output, elapsed = run_check(check)
        total_time += elapsed
        if not is_silent(output):
            results.append((name, output, elapsed))

    if not results:
        return 0

    # Print report
    sep = "=" * 60
    if not quiet:
        print(sep, file=sys.stderr)
        print("PRE-COMMIT ADVISORY REPORT", file=sys.stderr)
        print(sep, file=sys.stderr)
        print(file=sys.stderr)

    for name, output, _ in results:
        print(f"--- {name} ---", file=sys.stderr)
        print(output, file=sys.stderr)
        print(file=sys.stderr)

    if not quiet:
        print(
            f"({len(results)} advisory check(s) with output, {total_time:.1f}s total)",
            file=sys.stderr,
        )
        print(sep, file=sys.stderr)

    return 0


if __name__ == "__main__":
    sys.exit(main())
