#!/usr/bin/env python3
"""Auto Incident Capture — Stop Hook

Scans the session transcript for error patterns that indicate bugs encountered
during the session. For each unique error not already logged, appends a
structured incident entry to the memory directory.

Usage (as Stop hook):
  Invoked automatically when a Claude Code session ends.
  Reads $TRANSCRIPT from the environment.
"""

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


def _memory_dir() -> Path:
    cwd = os.getcwd()
    slug = cwd.replace("/", "-").lstrip("-")
    return Path.home() / ".claude" / "projects" / slug / "memory"


# Error patterns that indicate real bugs (not test assertions or expected errors)
ERROR_PATTERNS: list[tuple[str, str, str]] = [
    (r'NameError:\s*name\s+[\'"](\w+)[\'"].*not defined', "HIGH", "Backend"),
    (r'TypeError:\s*(.{10,80})', "MEDIUM", "Backend"),
    (r'AttributeError:\s*(.{10,80})', "MEDIUM", "Backend"),
    (r'ImportError:\s*(.{10,80})', "HIGH", "Backend"),
    (r'HTTP\s+500\s+Internal\s+Server\s+Error', "HIGH", "Backend"),
    (r'CRITICAL.*?(?:crash|down|unavailable)', "CRITICAL", "Infrastructure"),
    (r'Cannot read propert(?:y|ies) of (?:null|undefined)', "HIGH", "Frontend"),
    (r'TypeError:.*\.map is not a function', "HIGH", "Frontend"),
    (r'Unhandled Runtime Error', "HIGH", "Frontend"),
    (r'EACCES:\s*permission denied', "MEDIUM", "Docker"),
    (r'connection refused.*(?:5432|6379|3000|8000)', "HIGH", "Infrastructure"),
]

FIX_INDICATORS = [
    r'(?:Fixed|Resolved|Patched)',
    r'tests?\s+passed',
    r'\d+ passed,?\s*0 failed',
]


def auto_analyze(error_text: str) -> str:
    """Run reactive analysis on captured error. Returns formatted suggestions or empty string."""
    script_dir = Path(__file__).parent
    root = Path(__file__).resolve().parents[1]  # scripts/ -> project root
    try:
        # Try direct import (same directory)
        sys.path.insert(0, str(script_dir))
        from detect_error_patterns import analyze_error, Finding
        findings = analyze_error(error_text, root)
        if findings:
            lines = ["**Suggested patterns**:"]
            for f in findings[:5]:  # Cap at 5 suggestions per incident
                lines.append(f"  - {f.rule_id}: {f.rule_name} ({f.file}:{f.line})")
            return "\n".join(lines)
    except (ImportError, Exception):
        # Fallback: subprocess call
        try:
            result = subprocess.run(
                [sys.executable, str(script_dir / "detect_error_patterns.py"), "--analyze", error_text],
                capture_output=True, text=True, timeout=30, cwd=str(root)
            )
            if result.stdout.strip():
                return f"**Suggested patterns**:\n{result.stdout.strip()[:500]}"
        except (subprocess.TimeoutExpired, FileNotFoundError, Exception):
            pass
    return ""


def _extract_changed_files(transcript: str, error_pos: int) -> str:
    """Extract file names changed after the error from the transcript."""
    after_error = transcript[error_pos:]
    # Look for common file-change indicators
    files = set()
    for m in re.finditer(r'(?:edited|modified|wrote|updated|changed)\s+[`"]?([^\s`"]+\.\w+)', after_error, re.IGNORECASE):
        files.add(m.group(1))
    for m in re.finditer(r'diff --git a/(\S+)', after_error):
        files.add(m.group(1))
    if files:
        return ", ".join(sorted(files)[:10])
    return "_unknown — review session transcript_"


def get_existing_signatures(content: str) -> set[str]:
    sigs = set()
    for m in re.finditer(r'\*\*Error\*\*:\s*(.+)', content):
        sigs.add(m.group(1).strip().lower()[:50])
    return sigs


def get_next_id(content: str) -> str:
    today = datetime.now().strftime("%Y-%m-%d")
    existing = re.findall(rf'INC-{today}-(\d{{3}})', content)
    n = max((int(x) for x in existing), default=0) + 1
    return f"INC-{today}-{n:03d}"


def scan_transcript(transcript: str) -> list[dict]:
    incidents = []
    seen = set()

    for pattern, severity, area in ERROR_PATTERNS:
        for m in re.finditer(pattern, transcript, re.IGNORECASE):
            error_text = m.group(0).strip()[:120]
            sig = error_text.lower()[:50]
            if sig in seen:
                continue
            seen.add(sig)

            status = "OPEN"
            for fix_pat in FIX_INDICATORS:
                if re.search(fix_pat, transcript[m.end():], re.IGNORECASE):
                    status = "CLOSED"
                    break

            incidents.append({
                "error": error_text,
                "severity": severity,
                "area": area,
                "status": status,
                "match_pos": m.end(),
            })

    return incidents


def format_incident(inc_id: str, inc: dict, transcript: str = "") -> str:
    fix_text = "_Fixed during session_" if inc["status"] == "CLOSED" else "_Not yet resolved_"

    # Auto-analyze: try to find matching patterns for this error
    analysis = auto_analyze(inc["error"])
    analysis_block = f"\n{analysis}" if analysis else ""

    # Fix-pattern suggestion for CLOSED incidents (C2)
    fix_pattern_block = ""
    if inc["status"] == "CLOSED" and transcript:
        changed_files = _extract_changed_files(transcript, inc.get("match_pos", 0))
        fix_pattern_block = (
            f"\n**Fix applied**: Files changed after error: {changed_files}"
            f"\n**Prevent recurrence**: Consider adding to detect_error_patterns.py:"
            f'\n  PatternRule(id="EP-2XX", name="<describe the bug>", severity="high", '
            f'file_glob="**/*.py", pattern=r"<regex for the bug pattern>", '
            f'fix_hint="<what to do instead>")'
        )

    return f"""
---

## {inc_id} | {inc['severity']} | {inc['area']} | {inc['status']}
**Error**: {inc['error']}
**Root Cause**: _Auto-captured — review and update_{analysis_block}
**Fix**: {fix_text}{fix_pattern_block}
**Prevention**: _Review and add prevention measures_
"""


def main() -> int:
    transcript = os.environ.get("TRANSCRIPT", "")
    if not transcript:
        return 0

    mem = _memory_dir()
    incidents_file = mem / "incidents.md"
    existing = ""
    if incidents_file.exists():
        existing = incidents_file.read_text(encoding="utf-8")

    existing_sigs = get_existing_signatures(existing)
    incidents = scan_transcript(transcript)

    new_incidents = [i for i in incidents if i["error"].lower()[:50] not in existing_sigs]
    if not new_incidents:
        return 0

    mem.mkdir(parents=True, exist_ok=True)
    entries = []
    for inc in new_incidents:
        inc_id = get_next_id(existing + "\n".join(entries))
        entries.append(format_incident(inc_id, inc, transcript))

    with open(incidents_file, "a", encoding="utf-8") as f:
        for entry in entries:
            f.write(entry)

    print(f"Auto-captured {len(new_incidents)} new incident(s) in {incidents_file}", file=sys.stderr)
    return 0


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