"""Tests that src/ai_client.py has NO top-level heavy SDK imports.

Per spec.md:2.2 Layer 1, the main thread's import chain must not include
heavy modules. Heavy SDKs (google.genai, anthropic, openai, requests) are
warmed on AppController's _io_pool and accessed via _require_warmed().

These tests run in a fresh subprocess to ensure no warmup state leaks
from the test runner. We assert:
  - The four heavy SDKs are NOT imported as a side effect of
    `import src.ai_client`
  - The _require_warmed helper exists
  - Calling _require_warmed with an un-warmed module works (fallback
    import via importlib)
"""

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,
 )


def test_ai_client_does_not_import_google_genai_at_module_level() -> None:
 res = _run_in_subprocess("""
  import sys
  import src.ai_client
  print('google.genai' in sys.modules)
  print('google' in sys.modules)
 """)
 assert res.returncode == 0, f"stderr: {res.stderr}"
 lines = res.stdout.strip().splitlines()
 assert lines == ["False", "False"], f"ai_client triggered google.genai import: {res.stdout}"


def test_ai_client_does_not_import_anthropic_at_module_level() -> None:
 res = _run_in_subprocess("""
  import sys
  import src.ai_client
  print('anthropic' in sys.modules)
 """)
 assert res.returncode == 0, f"stderr: {res.stderr}"
 assert res.stdout.strip() == "False"


def test_ai_client_does_not_import_openai_at_module_level() -> None:
 res = _run_in_subprocess("""
  import sys
  import src.ai_client
  print('openai' in sys.modules)
 """)
 assert res.returncode == 0, f"stderr: {res.stderr}"
 assert res.stdout.strip() == "False"


def test_ai_client_does_not_import_requests_at_module_level() -> None:
 res = _run_in_subprocess("""
  import sys
  import src.ai_client
  print('requests' in sys.modules)
 """)
 assert res.returncode == 0, f"stderr: {res.stderr}"
 assert res.stdout.strip() == "False"


def test_ai_client_does_not_import_google_genai_types_at_module_level() -> None:
 res = _run_in_subprocess("""
  import sys
  import src.ai_client
  print('google.genai.types' in sys.modules)
 """)
 assert res.returncode == 0, f"stderr: {res.stderr}"
 assert res.stdout.strip() == "False"


def test_ai_client_exposes_require_warmed_helper() -> None:
 res = _run_in_subprocess("""
  import src.ai_client
  print(hasattr(src.ai_client, '_require_warmed'))
  print(callable(src.ai_client._require_warmed))
 """)
 assert res.returncode == 0, f"stderr: {res.stderr}"
 assert res.stdout.strip() == "True\nTrue"


def test_require_warmed_returns_module_when_already_loaded() -> None:
 res = _run_in_subprocess("""
  import sys
  import importlib
  import src.ai_client
  # Pre-load the module (simulating warmup)
  importlib.import_module('json')
  mod = src.ai_client._require_warmed('json')
  print(mod is sys.modules['json'])
 """)
 assert res.returncode == 0, f"stderr: {res.stderr}"
 assert res.stdout.strip() == "True"


def test_require_warmed_falls_back_to_import_if_not_warmed() -> None:
 res = _run_in_subprocess("""
  import sys
  import src.ai_client
  # Pick a stdlib module that ai_client doesn't trigger
  assert 'collections' in sys.modules  # stdlib
  # Pick a module that's definitely NOT imported yet
  target = 'collections.abc'
  importlib_mod = __import__('importlib')
  if target in sys.modules:
   del sys.modules[target]
  mod = src.ai_client._require_warmed(target)
  print(mod is not None)
  print(target in sys.modules)
 """)
 assert res.returncode == 0, f"stderr: {res.stderr}"
 lines = res.stdout.strip().splitlines()
 assert lines[0] == "True"
 assert lines[1] == "True"


def test_audit_main_thread_imports_sees_no_new_violation_from_ai_client() -> None:
 """Run the static audit and check that src/ai_client.py contributes no
 new heavy imports (any remaining violations are pre-existing in OTHER
 files; Phase 4-5 will address them).
 """
 res = _run_in_subprocess("""
  import sys
  from pathlib import Path
  sys.path.insert(0, str(Path.cwd()))
  from scripts.audit_main_thread_imports import audit, _walk_import_graph
  from pathlib import Path
  root = Path('.').resolve()
  entry = (root / 'sloppy.py').resolve()
  graph = _walk_import_graph(entry, root)
  ai_client_path = root / 'src' / 'ai_client.py'
  if ai_client_path in graph:
   import ast
   tree = ast.parse(ai_client_path.read_text(encoding='utf-8'))
   heavy = ['anthropic', 'google.genai', 'openai', 'requests', 'google.genai.types']
   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 + '.'):
        print('VIOLATION:', alias.name)
    elif isinstance(node, ast.ImportFrom):
     if node.module:
      for h in heavy:
       if node.module == h or node.module.startswith(h + '.'):
        print('VIOLATION:', node.module)
  print('OK')
 """)
 assert res.returncode == 0, f"stderr: {res.stderr}"
 assert "OK" in res.stdout
 assert "VIOLATION" not in res.stdout
