"""
Git-based change detection: git status, git diff, function/class extraction from diffs.
"""

import re
import subprocess
from typing import TypedDict


class GitChange(TypedDict):
    file: str
    changeType: str
    functionsModified: list[str]


FUNCTION_REGEXES = [
    re.compile(r"^\s*(?:function|async\s+function)\s+(\w+)\s*\("),
    re.compile(r"^\s*class\s+(\w+)"),
    re.compile(r"^\s*def\s+(\w+)\s*\("),
    re.compile(r"^\s*(?:async\s+)?def\s+(\w+)\s*\("),
]


def _extract_functions_from_diff_hunk(hunk_lines: list[str]) -> list[str]:
    names: set[str] = set()
    for line in hunk_lines:
        if line.startswith("+") and not line.startswith("+++"):
            content = line[1:]
            for pattern in FUNCTION_REGEXES:
                m = pattern.search(content)
                if m and m.lastindex:
                    names.add(m.group(1))
    return list(names)


def _get_change_type(x: str, y: str) -> str:
    if x == "D" or y == "D":
        return "deleted"
    if x == "A" or x == "?" or y == "?":
        return "new_file"
    return "modification"


def detect_changes(cwd: str) -> list[GitChange]:
    try:
        r = subprocess.run(
            ["git", "status", "--porcelain"],
            capture_output=True,
            text=True,
            cwd=cwd,
        )
        if r.returncode != 0:
            return []
        out = r.stdout or ""
    except (subprocess.SubprocessError, FileNotFoundError):
        return []
    lines = [s for s in out.strip().split("\n") if s]
    results: list[GitChange] = []
    for line in lines:
        xy = line[:2]
        file_path = line[3:].strip()
        if not file_path:
            continue
        x, y = xy[0], xy[1]
        change_type = _get_change_type(x, y)
        functions_modified: list[str] = []
        try:
            r = subprocess.run(
                ["git", "diff", "HEAD", "--", file_path],
                capture_output=True,
                text=True,
                cwd=cwd,
            )
            diff_out = r.stdout or ""
            added_lines = [l for l in diff_out.split("\n") if l.startswith("+")]
            functions_modified = _extract_functions_from_diff_hunk(added_lines)
            if not functions_modified and change_type in ("modification", "new_file"):
                functions_modified = ["(whole file)"]
        except (subprocess.SubprocessError, FileNotFoundError):
            if change_type == "new_file":
                functions_modified = ["(whole file)"]
        results.append({
            "file": file_path,
            "changeType": change_type,
            "functionsModified": functions_modified,
        })
    return results
