#!/usr/bin/env python3
"""Spec Gate — Pre-Commit Advisory

Checks whether complex changes have an associated spec file in docs/specs/.
Complexity triggers: >5 files changed, cross-stack changes, or sensitive paths.

Behavior:
  - Complex changes without spec: WARN (advisory, does not block)
  - Complex changes with spec missing required sections: WARN
  - Simple changes: pass silently

Usage:
  python3 scripts/docs/check_spec.py              # Check staged files
  python3 scripts/docs/check_spec.py --generate SR-2026-02-28-001 "Feature title"
"""

import re
import subprocess
import sys
from datetime import date
from pathlib import Path

ROOT = Path(__file__).resolve().parents[2]
SPECS_DIR = ROOT / "docs" / "specs"
BACKLOG_FILE = ROOT / "BACKLOG.md"

REQUIRED_SECTIONS = [
    "Context",
    "Scope",
    "Acceptance Criteria",
    "Affected Files",
    "Test Plan",
]

# Paths that indicate complexity (auth, migrations, core infrastructure)
COMPLEX_PATHS = [
    "alembic/",
    "migrations/",
    "models/",
    "services/auth",
    "core/permissions",
    "core/security",
    "middleware",
    "prisma/",
]


def get_staged_files() -> list[str]:
    result = subprocess.run(
        ["git", "diff", "--cached", "--name-only"],
        capture_output=True, text=True, cwd=ROOT,
    )
    return [f for f in result.stdout.strip().split("\n") if f.strip()]


def get_active_sr_id() -> str | None:
    if not BACKLOG_FILE.exists():
        return None
    content = BACKLOG_FILE.read_text(encoding="utf-8")
    match = re.search(r'\[in_progress\s*\].*?(SR-[\d-]+)', content)
    return match.group(1) if match else None


def is_complex_change(files: list[str]) -> tuple[bool, list[str]]:
    reasons = []
    if len(files) > 5:
        reasons.append(f"{len(files)} files changed (>5)")

    # Detect cross-stack changes (any two of backend/frontend/infra)
    domains = set()
    for f in files:
        if any(d in f for d in ["backend/", "app/", "api/", "server/"]):
            domains.add("backend")
        elif any(d in f for d in ["frontend/", "src/components/", "src/pages/", "src/app/"]):
            domains.add("frontend")
        elif any(d in f for d in ["docker", "compose", "Dockerfile", "infra/"]):
            domains.add("infrastructure")
    if len(domains) > 1:
        reasons.append(f"cross-stack ({' + '.join(sorted(domains))})")

    for f in files:
        for cp in COMPLEX_PATHS:
            if cp in f:
                reasons.append(f"sensitive path: {cp}")
                break

    return bool(reasons), reasons


def find_spec(sr_id: str) -> Path | None:
    if not SPECS_DIR.exists():
        return None
    for spec_file in SPECS_DIR.glob("*.md"):
        if sr_id in spec_file.read_text(encoding="utf-8"):
            return spec_file
    return None


def validate_spec(spec_path: Path) -> list[str]:
    content = spec_path.read_text(encoding="utf-8")
    return [s for s in REQUIRED_SECTIONS if not re.search(rf'#+\s*{re.escape(s)}', content, re.IGNORECASE)]


def generate_spec(sr_id: str, title: str) -> Path:
    SPECS_DIR.mkdir(parents=True, exist_ok=True)
    slug = re.sub(r'[^a-z0-9]+', '-', title.lower()).strip('-')[:50]
    spec_path = SPECS_DIR / f"{sr_id}-{slug}.md"

    template = f"""---
sr_id: {sr_id}
title: "{title}"
status: draft
created: {date.today().isoformat()}
updated: {date.today().isoformat()}
---

# {title}

## Context

_Why is this needed? What problem does it solve?_

## Scope

**In scope:**
- ...

**Out of scope:**
- ...

## Acceptance Criteria

- [ ] ...
- [ ] ...

## Affected Files

### Backend
- ...

### Frontend
- ...

## Risks

- **Security**: ...
- **Data migration**: ...

## Test Plan

### Unit Tests
- ...

### Integration Tests
- ...

## Rollout Notes

- ...
"""
    spec_path.write_text(template, encoding="utf-8")
    return spec_path


def main() -> int:
    if "--generate" in sys.argv:
        idx = sys.argv.index("--generate")
        if len(sys.argv) < idx + 3:
            print("Usage: --generate SR-ID \"Title\"", file=sys.stderr)
            return 1
        path = generate_spec(sys.argv[idx + 1], sys.argv[idx + 2])
        print(f"Spec template created: {path}")
        return 0

    files = get_staged_files()
    if not files:
        return 0

    is_complex, reasons = is_complex_change(files)
    if not is_complex:
        return 0

    sr_id = get_active_sr_id()
    if not sr_id:
        print(
            f"SPEC ADVISORY: Complex change detected ({', '.join(reasons)}) "
            f"but no active backlog item found.",
            file=sys.stderr,
        )
        return 0

    spec = find_spec(sr_id)
    if not spec:
        print(
            f"SPEC ADVISORY: Complex change for {sr_id}:\n"
            f"  Reasons: {', '.join(reasons)}\n"
            f"  No spec file found in docs/specs/.\n"
            f"  Generate one: python3 scripts/docs/check_spec.py --generate {sr_id} \"Title\"",
            file=sys.stderr,
        )
        return 0

    missing = validate_spec(spec)
    if missing:
        print(f"SPEC ADVISORY: Spec {spec.name} missing sections: {', '.join(missing)}", file=sys.stderr)

    return 0


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