#!/usr/bin/env python3
"""Fix TS6133 unused variable/import errors reported by tsc."""
import re
import subprocess
import sys
from pathlib import Path

ROOT = Path(__file__).resolve().parent.parent


def run_tsc() -> list[str]:
    result = subprocess.run(
        [str(ROOT / "node_modules/.bin/tsc"), "--noEmit"],
        cwd=ROOT,
        capture_output=True,
        text=True,
    )
    lines = (result.stdout + result.stderr).splitlines()
    return [l for l in lines if "error TS6133" in l or "error TS6198" in l]


def parse_error(line: str) -> tuple[str, int, str, str] | None:
    m = re.match(
        r"^(src/[^:]+)\((\d+),(\d+)\): error TS(6133|6198): '([^']+)'",
        line,
    )
    if not m:
        return None
    path, row, col, code, name = m.groups()
    return path, int(row), int(col), name, code


def fix_file(path: Path, row: int, name: str, code: str) -> bool:
    lines = path.read_text().splitlines(keepends=True)
    if row < 1 or row > len(lines):
        return False
    idx = row - 1
    line = lines[idx]

    if code == "6198":
        # All destructured elements unused — prefix each binding with _
        new_line = re.sub(r"\{([^}]+)\}", lambda m: "{" + ", ".join(
            (p.strip() if p.strip().startswith("_") else "_" + p.strip().split(":")[0].strip() + (":" + ":".join(p.strip().split(":")[1:]) if ":" in p.strip() else ""))
            for p in m.group(1).split(",")
        ) + "}", line, count=1)
        if new_line != line:
            lines[idx] = new_line
            path.write_text("".join(lines))
            return True
        return False

    # Unused import — remove named import or whole import line
    if re.search(rf"\bimport\b.*\b{re.escape(name)}\b", line):
        if re.match(r"import\s+\{" , line):
            names = re.search(r"\{([^}]+)\}", line)
            if names:
                parts = [p.strip() for p in names.group(1).split(",")]
                kept = []
                for p in parts:
                    base = p.split(" as ")[0].strip()
                    if base != name:
                        kept.append(p)
                if not kept:
                    lines[idx] = ""
                    path.write_text("".join(lines))
                    return True
                new_import = re.sub(r"\{[^}]+\}", "{" + ", ".join(kept) + "}", line)
                lines[idx] = new_import
                path.write_text("".join(lines))
                return True
        if re.match(rf"import\s+{re.escape(name)}\s", line) or re.match(rf"import\s+\*\s+as\s+{re.escape(name)}\s", line):
            lines[idx] = ""
            path.write_text("".join(lines))
            return True

    # Parameter or local — prefix with underscore
    patterns = [
        (rf"\(({re.escape(name)})\:", rf"(_{name}:"),
        (rf",\s*{re.escape(name)}\:", rf", _{name}:"),
        (rf"\(\s*{re.escape(name)}\s*\)", rf"(_{name})"),
        (rf"\(\s*{re.escape(name)}\s*,", rf"(_{name},"),
        (rf",\s*{re.escape(name)}\s*\)", rf", _{name})"),
        (rf"\b(const|let)\s+{re.escape(name)}\b", rf"\1 _{name}"),
        (rf"\basync\s+{re.escape(name)}\b", rf"async _{name}"),
        (rf"function\s+{re.escape(name)}\b", rf"function _{name}"),
    ]
    new_line = line
    for pat, repl in patterns:
        new_line2 = re.sub(pat, repl, new_line)
        if new_line2 != new_line:
            new_line = new_line2
            break
    else:
        # fallback: word boundary prefix in destructuring { name }
        new_line = re.sub(rf"\{{([^}}]*)\b{re.escape(name)}\b", rf"{{\1_{name}", line, count=1)
        if new_line == line:
            new_line = re.sub(rf"\b{re.escape(name)}\b", f"_{name}", line, count=1)

    if new_line != line:
        lines[idx] = new_line
        path.write_text("".join(lines))
        return True
    return False


def main() -> int:
    fixed = 0
    for _ in range(5):
        errors = run_tsc()
        if not errors:
            break
        changed = False
        seen: set[tuple] = set()
        for err in errors:
            parsed = parse_error(err)
            if not parsed:
                continue
            rel, row, col, name, code = parsed
            key = (rel, row, name)
            if key in seen:
                continue
            seen.add(key)
            path = ROOT / rel
            if fix_file(path, row, name, code):
                fixed += 1
                changed = True
        if not changed:
            break
    print(f"Fixed {fixed} TS6133/6198 issues")
    remaining = run_tsc()
    print(f"Remaining TS6133/6198: {len(remaining)}")
    return 0


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