"""Shared module loader for heavy imports.

Per startup_speedup_20260606 spec §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 modules are removed
from main-thread-reachable files entirely and accessed via `_require_warmed`
at use sites. The warmup mechanism in `src/warmup.py` (driven by
`AppController.__init__`) pre-loads them on the `_io_pool` so the first
user-triggered call is instant — the import cost is paid during startup on
a background thread, not on the user's first click.

This module is the single home of the `_require_warmed` helper so that
multiple files (src/ai_client.py, src/app_controller.py, future Phase 5
feature-gated modules) share one canonical implementation instead of
duplicating the lookup logic. The earlier practice of defining the helper
locally in src/ai_client.py created a cross-module import smell
(app_controller -> ai_client) when other modules needed the same primitive.

Public API:
  _require_warmed(name) -> module
    O(1) sys.modules lookup if warmup ran; importlib fallback otherwise.

Why a function instead of a `from X import Y` inside each call site?
Per the spec, lazy-loading on first use causes user-perceptible lag when
a UI action propagates to a controller method that triggers the first
import. Proactive warmup on bg threads means the cost is paid during the
visible startup window, not on the user's click.
"""

import importlib
import sys
from typing import Any


def _require_warmed(name: str) -> Any:
 """Return a heavy module that the AppController's warmup should have loaded.

 Heavy SDKs (anthropic, google.genai, openai, google.genai.types, requests,
 fastapi, fastapi.security.api_key, src.command_palette, src.theme_nerv,
 src.theme_nerv_fx, src.markdown_table, numpy) are warmed on
 AppController's _io_pool at startup. This function expects them to
 already be in sys.modules and just returns the cached module object. If
 the module is NOT in sys.modules (e.g. in tests where warmup didn't
 run), falls back to importlib so the call still works.

 In production: this is an O(1) sys.modules lookup. The 1+ second
 import cost is paid during startup on a bg thread, NOT on the first
 user-triggered AI call.

 Callers should pass the TOP-LEVEL package name (e.g. 'google.genai'),
 not a leaf sub-module (e.g. 'google.genai.types'). The library's
 __init__.py chain has a circular-import bug when a sub-module is
 loaded before its parent is fully initialized. The ai_client code
 follows this convention: `_require_warmed("google.genai").types`.
 """
 mod = sys.modules.get(name)
 if mod is not None:
  return mod
 return importlib.import_module(name)
