#!/usr/bin/env python
"""
Audit top-level imports in src/gui_2.py and classify them.

For each top-level `import X` or `from X import Y` statement in gui_2.py,
report:
  - file:line
  - the imported module
  - whether it's at module level (always loaded on main thread) or inside
    a function (potentially feature-gated)

This is a static analysis tool for the startup_speedup_20260606 track.
The output is meant to be read by a human who knows which functions
are first-frame vs feature-gated.

Output format (text):
  MODULE-LEVEL imports (these run on the main thread's import chain):
    src/gui_2.py:1:   import imgui_bundle
    src/gui_2.py:15:  from src.app_controller import AppController
    ...

  FUNCTION-LEVEL imports (potentially feature-gated; candidates for _require_warmed):
    src/gui_2.py:42 (inside _render_command_palette): from src.command_palette import ...
    ...
"""

import ast
import sys
from pathlib import Path
from typing import Iterable


def classify_imports(source: str) -> tuple[list[tuple[int, str, str]], list[tuple[int, str, str, str]]]:
 """Parse a Python source and return (module_level, function_level) imports.

 Each entry is (line, imported_name, full_statement).
 """
 tree = ast.parse(source)
 module_level: list[tuple[int, str, str]] = []
 function_level: list[tuple[int, str, str, str]] = []

 def imported_names(node: ast.stmt) -> list[str]:
  if isinstance(node, ast.Import):
   return [alias.name for alias in node.names]
  if isinstance(node, ast.ImportFrom):
   if not node.module or node.level != 0:
    return []
   return [node.module]
  return []

 for node in tree.body:
  names = imported_names(node)
  if not names:
   continue
  for name in names:
   stmt = ast.unparse(node).strip().replace("\n", " ")
   module_level.append((node.lineno, name, stmt))

 for node in ast.walk(tree):
  if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
   for child in node.body:
    names = imported_names(child)
    if not names:
     continue
    for name in names:
     stmt = ast.unparse(child).strip().replace("\n", " ")
     function_level.append((child.lineno, node.name, name, stmt))

 return module_level, function_level


def render_report(source_path: Path) -> str:
 source = source_path.read_text(encoding="utf-8", errors="replace")
 module_level, function_level = classify_imports(source)
 lines: list[str] = []
 lines.append(f"Audit of {source_path}")
 lines.append("=" * 80)
 lines.append("")
 lines.append(f"MODULE-LEVEL imports: {len(module_level)} (these run on the main thread's import chain)")
 lines.append("-" * 80)
 for lineno, name, stmt in module_level:
  lines.append(f"  L{lineno:>5}  {name:<40}  {stmt[:60]}")
 lines.append("")
 lines.append(f"FUNCTION-LEVEL imports: {len(function_level)} (potentially feature-gated)")
 lines.append("-" * 80)
 if function_level:
  by_function: dict[str, list[tuple[int, str, str]]] = {}
  for lineno, fname, name, stmt in function_level:
   by_function.setdefault(fname, []).append((lineno, name, stmt))
  for fname in sorted(by_function):
   entries = by_function[fname]
   lines.append(f"  {fname}  ({len(entries)} imports)")
   for lineno, name, stmt in entries:
    lines.append(f"    L{lineno:>5}  {name:<40}  {stmt[:60]}")
 else:
  lines.append("  (none)")
 lines.append("")
 return "\n".join(lines)


def main(argv: list[str]) -> int:
 if len(argv) < 2:
  print("usage: audit_gui2_imports.py <path-to-gui_2.py>", file=sys.stderr)
  return 2
 path = Path(argv[1])
 if not path.exists():
  print(f"file not found: {path}", file=sys.stderr)
  return 2
 print(render_report(path))
 return 0


if __name__ == "__main__":
 raise SystemExit(main(sys.argv))
