"""Tests that the Main Thread Purity Invariant holds for refactored modules.

Per spec.md:2.2 Layer 1, the main thread (the one that enters immapp.run())
must NEVER import a module heavier than imgui_bundle and the lean gui_2
skeleton. Heavy imports are loaded by the AppController's _io_pool
warmup and accessed via _require_warmed() at use sites.

This is the static enforcement test. The runtime audit hook test
(sys.addaudithook) is the empirical check; see tests/test_main_thread_purity_runtime.py
for the runtime version (Phase 8 T8.2).

This test uses scripts/audit_main_thread_imports.py (built in T1.4) to
verify that the modules we refactored in Phases 3-7 contribute ZERO
new violations to the main-thread import graph. The test is RED before
each phase's refactor, GREEN after.

For each refactored file, the test checks the top-level imports against
the heavy denylist. If any of these files still have top-level heavy
imports, the test FAILS - that means the refactor is incomplete or a
future commit re-introduced a heavy import.
"""

import subprocess
import sys
import textwrap
from pathlib import Path

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


def _run_in_subprocess(snippet: str) -> subprocess.CompletedProcess:
 script = textwrap.dedent(snippet)
 return subprocess.run(
  [sys.executable, "-c", script],
  capture_output=True,
  text=True,
  cwd=str(ROOT),
  timeout=30,
 )


# Heavy modules that must NOT be top-level imports in any main-thread-
# reachable file. Mirrors the spec's heavy denylist.
HEAVY_DENYLIST = {
 "google.genai", "anthropic", "openai", "requests",
 "google.genai.types", "fastapi", "fastapi.security.api_key",
 "src.command_palette", "src.theme_nerv", "src.theme_nerv_fx",
 "src.markdown_table", "numpy", "tkinter", "tomli_w",
}

# Files that the refactor targets. Each must have ZERO top-level imports
# of any module in HEAVY_DENYLIST.
REFACTOR_TARGETS = [
 "src/ai_client.py",       # Phase 3
 "src/app_controller.py",  # Phase 4
 "src/commands.py",        # Phase 5A
 "src/theme_2.py",         # Phase 5B
 "src/markdown_helper.py", # Phase 5C
 "src/gui_2.py",           # Phase 5D
]


def _check_file_violations(file_path: str) -> str:
 """Run a subprocess that AST-checks the file for heavy top-level imports.
 Returns the stdout of the subprocess (which lists any violations found)."""
 res = _run_in_subprocess(f"""
  import ast
  from pathlib import Path
  root = Path('.').resolve()
  path = root / {file_path!r}
  if not path.exists():
   print('FILE_NOT_FOUND')
  else:
   tree = ast.parse(path.read_text(encoding='utf-8'))
   heavy = {sorted(HEAVY_DENYLIST)!r}
   violations = []
   for node in tree.body:
    if isinstance(node, ast.Import):
     for alias in node.names:
      for h in heavy:
       if alias.name == h or alias.name.startswith(h + '.'):
        violations.append(f'{{alias.name}} @ line {{node.lineno}}')
    elif isinstance(node, ast.ImportFrom):
     if node.module:
      for h in heavy:
       if node.module == h or node.module.startswith(h + '.'):
        violations.append(f'{{node.module}} @ line {{node.lineno}}')
   for v in violations:
    print('VIOLATION:', v)
   if not violations:
    print('CLEAN')
 """)
 return res.stdout


def test_ai_client_has_no_heavy_top_level_imports() -> None:
 out = _check_file_violations("src/ai_client.py")
 assert "VIOLATION" not in out, f"ai_client.py has heavy top-level imports: {out}"


def test_app_controller_has_no_heavy_top_level_imports() -> None:
 out = _check_file_violations("src/app_controller.py")
 assert "VIOLATION" not in out, f"app_controller.py has heavy top-level imports: {out}"


def test_commands_has_no_heavy_top_level_imports() -> None:
 out = _check_file_violations("src/commands.py")
 assert "VIOLATION" not in out, f"commands.py has heavy top-level imports: {out}"


def test_theme_2_has_no_heavy_top_level_imports() -> None:
 out = _check_file_violations("src/theme_2.py")
 assert "VIOLATION" not in out, f"theme_2.py has heavy top-level imports: {out}"


def test_markdown_helper_has_no_heavy_top_level_imports() -> None:
 out = _check_file_violations("src/markdown_helper.py")
 assert "VIOLATION" not in out, f"markdown_helper.py has heavy top-level imports: {out}"


def test_gui_2_has_no_heavy_top_level_imports() -> None:
 out = _check_file_violations("src/gui_2.py")
 assert "VIOLATION" not in out, f"gui_2.py has heavy top-level imports: {out}"


def test_refactor_targets_summary() -> None:
 """Aggregate check: all 6 refactored files are clean."""
 summary = []
 for f in REFACTOR_TARGETS:
  out = _check_file_violations(f)
  clean = "CLEAN" in out
  summary.append((f, clean))
 all_clean = all(c for _, c in summary)
 assert all_clean, f"Not all refactored files are clean: {summary}"
