# -*- coding: utf-8 -*-
# -----------------------------------------------------------------------------
# NukeChineseAnnotation - bootstrap
#
# Author / 作者:YanKaiHu
# Copyright (c) 2026 Nuke学习社 / YanKaiHu
# All Rights Reserved.
#
# LICENSE / 使用许可:
# This source code is source-available for learning and internal use ONLY.
# It is NOT open-source software.
#
# 禁止行为：
# - 禁止任何形式的修改、改写、二次开发、衍生作品
# - 禁止倒卖、转售、收费分发、打包售卖（含付费群/课程/资源包）
# - 禁止转载、二次分发、公开发布全部或部分源码
# - 禁止移除或修改本版权与作者声明
#
# 未经作者书面授权，任何超出以上范围的使用一律禁止。
# 本软件按“现状”提供，作者不对任何损失负责。
# -----------------------------------------------------------------------------
from __future__ import absolute_import, division, print_function, unicode_literals

import importlib
import logging
import os
import sys

_PKG_DIR = os.path.dirname(__file__)
_PKG_NAME = os.path.basename(_PKG_DIR)
_PARENT_DIR = os.path.dirname(_PKG_DIR)

if _PARENT_DIR and _PARENT_DIR not in sys.path:
    sys.path.insert(0, _PARENT_DIR)

_THIS_MODULE = sys.modules.get(__name__)
if _THIS_MODULE is not None:
    sys.modules.setdefault('{0}.bootstrap'.format(_PKG_NAME), _THIS_MODULE)

_BOOTSTRAP_READY = False
_GUI_READY = False
_MANAGEMENT_COMMANDS_READY = False
_INIT_ENTRY_RAN = False
_MENU_ENTRY_RAN = False
_MENU_START_SCHEDULED = False
_BOOTSTRAP_LIFECYCLE_TOKEN = object()
_STARTUP_FAILURE = None

_NCA_MENU_ROOT = 'NukeChineseAnnotation'
_MGMT_MENU_PATHS = (
    _NCA_MENU_ROOT + '/偏好设置...',
    _NCA_MENU_ROOT + '/自检面板...',
    _NCA_MENU_ROOT + '/刷新当前界面翻译',
)
_NODE_HELP_MENU_PATH = _NCA_MENU_ROOT + '/显示中文节点说明 (F1)'

_VIEWER_KNOB_TOOLTIPS_ENGLISH = {
    'Viewer.gain': 'Multiply the image by this amount before display.',
    'Viewer.gamma': 'Gamma correction is applied after the gain, viewer process, and any custom input process.',
    'Viewer.saturation': 'Saturation correction is applied before the gain, viewer process, any custom input process and gamma.',
    'Viewer.out': 'The out point of the clip/sequence.',
}


def _qualify_module_name(module_name):
    if not module_name:
        raise ImportError('Empty module name')

    if module_name.startswith(_PKG_NAME + '.'):
        return module_name

    if '.' in module_name:
        return module_name

    return '{0}.{1}'.format(_PKG_NAME, module_name)


def _import_local(module_name):
    qualified_name = _qualify_module_name(module_name)
    return importlib.import_module(qualified_name)


_BOOTSTRAP_LOGGER = None


_PROFILE_MODULE = None

try:
    from .runtime_logging import log_event as _log_runtime_event, log_exception as _log_runtime_exception
except ImportError:
    try:
        from runtime_logging import log_event as _log_runtime_event, log_exception as _log_runtime_exception  # type: ignore
    except ImportError:
        def _log_runtime_event(logger, level, event, **fields):
            if logger is None:
                return
            logger.log(level, 'event=%s %s' % (event, fields))

        def _log_runtime_exception(logger, level, event, exc, **fields):
            if logger is None:
                return
            fields = dict(fields or {})
            fields.setdefault('exc_type', getattr(getattr(exc, '__class__', None), '__name__', 'Exception'))
            fields.setdefault('exc', str(exc))
            logger.log(level, 'event=%s %s' % (event, fields))



class _NullProfileSpan(object):
    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc, tb):
        return False


def _profile_module():
    global _PROFILE_MODULE
    if _PROFILE_MODULE is None:
        _PROFILE_MODULE = _safe_import('performance_profile')
    return _PROFILE_MODULE


def _profile_has_session():
    prof = _profile_module()
    if prof is None:
        return False
    try:
        return bool(prof.has_active_session())
    except Exception:
        return False


def _profile_begin(name='startup', reset=False):
    prof = _profile_module()
    if prof is None:
        return None
    try:
        return prof.begin_session(name=name, reset=reset)
    except Exception:
        return None


def _profile_mark(key, value):
    prof = _profile_module()
    if prof is None:
        return value
    try:
        prof.mark(key, value)
    except Exception:
        pass
    return value


def _profile_span(name, extra=None):
    prof = _profile_module()
    if prof is None:
        return _NullProfileSpan()
    try:
        return prof.profile_span(name, extra=extra)
    except Exception:
        return _NullProfileSpan()


def _profile_end(status='ok', save=True):
    prof = _profile_module()
    if prof is None:
        return None
    try:
        return prof.end_session(status=status, save=save)
    except Exception:
        return None


def _get_logger():
    global _BOOTSTRAP_LOGGER
    if _BOOTSTRAP_LOGGER is not None:
        return _BOOTSTRAP_LOGGER

    import logging

    logger = logging.getLogger('NCA.bootstrap')
    logger.propagate = False

    if not logger.handlers:
        handler = logging.StreamHandler()
        handler.setFormatter(logging.Formatter('[%(asctime)s] %(levelname)s %(name)s: %(message)s'))
        logger.addHandler(handler)

    level_name = os.getenv('NCA_LOG_LEVEL', 'WARNING').upper()
    logger.setLevel(getattr(logging, level_name, logging.WARNING))
    _BOOTSTRAP_LOGGER = logger
    return logger


def _record_startup_failure(stage, exc):
    """Record a startup failure without allowing it to abort Nuke startup."""
    global _STARTUP_FAILURE

    exc_type = getattr(getattr(exc, '__class__', None), '__name__', 'Exception')
    _STARTUP_FAILURE = {
        'stage': str(stage or 'unknown'),
        'exc_type': str(exc_type),
        'message': str(exc),
    }

    logger = _get_logger()
    _log_runtime_exception(
        logger,
        logging.ERROR,
        'bootstrap.startup_failed',
        exc,
        stage=_STARTUP_FAILURE['stage'],
    )

    nuke = _nuke_module()
    if nuke is not None:
        try:
            nuke.tprint(
                '[NukeChineseAnnotation] Startup was disabled at {0}: {1}: {2}'.format(
                    _STARTUP_FAILURE['stage'],
                    _STARTUP_FAILURE['exc_type'],
                    _STARTUP_FAILURE['message'],
                )
            )
        except Exception:
            pass
    return False


def _clear_startup_failure():
    global _STARTUP_FAILURE
    _STARTUP_FAILURE = None


def get_startup_failure():
    """Return the last isolated startup failure for diagnostics."""
    if _STARTUP_FAILURE is None:
        return None
    return dict(_STARTUP_FAILURE)


def _license_module():
    return _import_local('license_check')


def _nuke_module():
    try:
        import nuke
        return nuke
    except ImportError:
        return None


def _safe_import(module_name):
    try:
        return _import_local(module_name)
    except ImportError as exc:
        _log_runtime_exception(_get_logger(), logging.DEBUG, 'bootstrap.safe_import.import_error', exc, module=module_name)
        return None


def _safe_call(module_name, fn_name, logger=None, *args, **kwargs):
    try:
        mod = _import_local(module_name)
    except ImportError as exc:
        _log_runtime_exception(logger, logging.DEBUG, 'bootstrap.safe_call.import_error', exc, module=module_name, function=fn_name)
        return None
    except Exception as exc:
        _log_runtime_exception(logger, logging.DEBUG, 'bootstrap.safe_call.import_failed', exc, module=module_name, function=fn_name)
        return None

    if not hasattr(mod, fn_name):
        _log_runtime_event(logger, logging.DEBUG, 'bootstrap.safe_call.missing_attr', module=module_name, function=fn_name)
        return None

    try:
        return getattr(mod, fn_name)(*args, **kwargs)
    except Exception as exc:
        _log_runtime_exception(logger, logging.DEBUG, 'bootstrap.safe_call.invoke_failed', exc, module=module_name, function=fn_name)
        return None


def _remove_menu_item(menu_obj, menu_path):
    if not menu_obj:
        return False

    try:
        menu_obj.removeItem(menu_path)
        return True
    except (AttributeError, RuntimeError, TypeError):
        pass

    try:
        item = menu_obj.findItem(menu_path)
    except (AttributeError, RuntimeError, TypeError):
        item = None

    if item is None:
        return False

    for method_name in ('setEnabled', 'setVisible'):
        try:
            method = getattr(item, method_name, None)
            if method:
                method(False)
        except (AttributeError, RuntimeError, TypeError):
            pass
    return True


def _refresh_top_menu(logger=None):
    try:
        top_menu = _import_local('top_menu')
        if hasattr(top_menu, 'translate_menu_toolbar'):
            top_menu.translate_menu_toolbar()
            return True
    except Exception as exc:
        _log_runtime_exception(logger, logging.DEBUG, 'bootstrap.top_menu.refresh_failed', exc)
    return False


def _apply_language_switcher_english(logger=None):
    ls = _safe_import('language_switcher')
    if ls is None:
        return False

    for fn_name in ('uninstall_language_switcher',):
        if hasattr(ls, fn_name):
            try:
                getattr(ls, fn_name)()
                return True
            except Exception as exc:
                _log_runtime_exception(logger, logging.DEBUG, 'bootstrap.language_switcher.uninstall_failed', exc, function=fn_name)

    if hasattr(ls, '_apply_english_mode'):
        try:
            ls._apply_english_mode()
            return True
        except Exception as exc:
            _log_runtime_exception(logger, logging.DEBUG, 'bootstrap.language_switcher.apply_english_failed', exc)

    try:
        bilingual = bool(getattr(ls, 'bilingual_mode', False))
    except Exception:
        bilingual = False

    if bilingual and hasattr(ls, 'toggle_language'):
        try:
            ls.toggle_language()
            return True
        except Exception as exc:
            _log_runtime_exception(logger, logging.DEBUG, 'bootstrap.language_switcher.toggle_failed', exc)

    return False

def is_gui_session():
    nuke = _nuke_module()
    if nuke is None:
        return False
    val = None
    try:
        val = nuke.env.get('gui')
    except Exception:
        pass
    if val is None:
        try:
            val = nuke.env['gui']
        except Exception:
            val = None
    if isinstance(val, str):
        return val.strip().lower() in ('1', 'true', 'yes', 'on')
    return bool(val)


def _env_flag_enabled(name):
    """Return a stable Boolean value for NCA opt-in environment flags."""
    return os.getenv(name, '').strip().lower() in ('1', 'true', 'yes', 'on')


def ensure_bootstrap_ready():
    global _BOOTSTRAP_READY
    if _BOOTSTRAP_READY:
        return True

    with _profile_span('bootstrap.ready'):
        lc = _license_module()
        if not bool(getattr(lc, '_NCA_BOOTSTRAPPED', False)):
            with _profile_span('bootstrap.verify_package_manifest'):
                ok, msg = lc.verify_package_manifest(_PKG_DIR)
            if not ok:
                raise RuntimeError(msg)
            with _profile_span('bootstrap.install_import_guard'):
                manifest_dict = lc.get_last_verified_manifest_dict() or lc.load_manifest_dict(_PKG_DIR)
                lc.install_import_guard(_PKG_DIR, manifest_dict)
            try:
                lc._NCA_BOOTSTRAPPED = True
            except Exception:
                pass

        os.environ.setdefault('NCA_PLUGIN_ROOT', _PKG_DIR)

        _BOOTSTRAP_READY = True
    return True


def ensure_license_for_session(show_ui_if_needed=True):
    lc = _license_module()

    license_ok = bool(getattr(lc, '_NCA_LICENSE_OK', False))
    skipped_in_headless = bool(getattr(lc, '_NCA_LICENSE_SKIPPED_IN_HEADLESS', False))
    
    # 只要验证通过了，就直接放行，不再做任何打断
    if license_ok and not skipped_in_headless:
        return True

    enforce_in_headless = _env_flag_enabled('NCA_ENFORCE_IN_HEADLESS')
    gui = is_gui_session()
    _profile_mark('license.gui_session', bool(gui))
    _profile_mark('license.enforce_in_headless', bool(enforce_in_headless))

    with _profile_span('bootstrap.ensure_license_for_session', {'show_ui_if_needed': bool(show_ui_if_needed)}):
        if gui:
            try:
                with _profile_span('license.enforce.gui_quiet'):
                    lc.enforce_or_prompt(show_ui=False)
            except Exception:
                if show_ui_if_needed:
                    with _profile_span('license.enforce.gui_prompt'):
                        lc.enforce_or_prompt(show_ui=True)
                else:
                    raise
            try:
                lc._NCA_LICENSE_OK = True
                lc._NCA_LICENSE_SKIPPED_IN_HEADLESS = False
            except Exception:
                pass
            return True

        if enforce_in_headless:
            with _profile_span('license.enforce.headless'):
                lc.enforce_or_prompt(show_ui=False)
            try:
                lc._NCA_LICENSE_OK = True
                lc._NCA_LICENSE_SKIPPED_IN_HEADLESS = False
            except Exception:
                pass
            return True

        try:
            lc._NCA_LICENSE_SKIPPED_IN_HEADLESS = True
        except Exception:
            pass
    return True


def _set_viewer_knob_tooltips(mapping):
    nuke = _nuke_module()
    if nuke is None:
        return
    try:
        for knob_name, text in mapping.items():
            nuke.knobTooltip(knob_name, text)
    except Exception:
        pass


def _restore_viewer_knob_tooltips_english():
    _set_viewer_knob_tooltips(_VIEWER_KNOB_TOOLTIPS_ENGLISH)


def _open_nca_preferences():
    nuke = _nuke_module()
    if nuke is None:
        return
    try:
        _import_local('plugin_preferences_dialog').open_preferences_dialog()
    except Exception as exc:
        try:
            nuke.message('Failed to open NCA Preferences: {0}'.format(exc))
        except Exception:
            pass


def _open_nca_selfcheck():
    nuke = _nuke_module()
    if nuke is None:
        return
    try:
        _import_local('self_check_dialog').open_self_check_dialog()
    except Exception as exc:
        try:
            nuke.message('Failed to open NCA Self-Check: {0}'.format(exc))
        except Exception:
            pass


def _refresh_nca_translations():
    nuke = _nuke_module()
    if nuke is None:
        return
    try:
        language_switcher = _import_local('language_switcher')
        refresh_fn = getattr(language_switcher, 'refresh_current_ui', None)
        if callable(refresh_fn):
            refresh_fn()
    except Exception as exc:
        try:
            nuke.message('Failed to refresh NCA translations: {0}'.format(exc))
        except Exception:
            pass


def _install_management_commands():
    global _MANAGEMENT_COMMANDS_READY
    if _MANAGEMENT_COMMANDS_READY:
        return True
    nuke = _nuke_module()
    if nuke is None:
        return False

    try:
        root = nuke.menu('Nuke')
    except Exception:
        root = None

    if root is None:
        return False

    for path in _MGMT_MENU_PATHS:
        try:
            _remove_menu_item(root, path)
        except Exception:
            pass

    try:
        root.addCommand(_MGMT_MENU_PATHS[0], _open_nca_preferences)
        root.addCommand(_MGMT_MENU_PATHS[1], _open_nca_selfcheck)
        root.addCommand(_MGMT_MENU_PATHS[2], _refresh_nca_translations)
    except Exception as exc:
        for path in _MGMT_MENU_PATHS:
            try:
                _remove_menu_item(root, path)
            except Exception:
                pass
        _MANAGEMENT_COMMANDS_READY = False
        _log_runtime_exception(
            _get_logger(),
            logging.DEBUG,
            'bootstrap.management.install_failed',
            exc,
        )
        return False
    _MANAGEMENT_COMMANDS_READY = True
    return True


def _uninstall_management_commands(logger=None):
    global _MANAGEMENT_COMMANDS_READY
    nuke = _nuke_module()
    if nuke is None:
        _MANAGEMENT_COMMANDS_READY = False
        return False

    try:
        root = nuke.menu('Nuke')
    except Exception:
        root = None

    if root is None:
        _MANAGEMENT_COMMANDS_READY = False
        return False

    changed = False
    for path in _MGMT_MENU_PATHS:
        try:
            if _remove_menu_item(root, path):
                changed = True
        except Exception as exc:
            if logger is not None:
                logger.debug('NCA bootstrap: failed to remove management menu item %s: %s', path, exc)

    try:
        _remove_menu_item(root, _NCA_MENU_ROOT)
    except Exception:
        pass

    _MANAGEMENT_COMMANDS_READY = False
    return changed


def _install_node_help(logger=None):
    return bool(_safe_call('node_help_popup', 'install_node_help', logger))


def _uninstall_node_help(logger=None):
    removed = False

    if _safe_call('node_help_popup', 'uninstall_node_help', logger) is not None:
        removed = True

    nuke = _nuke_module()
    if nuke is None:
        return removed

    try:
        root = nuke.menu('Nuke')
    except Exception:
        root = None

    if root is None:
        return removed

    try:
        if _remove_menu_item(root, _NODE_HELP_MENU_PATH):
            removed = True
    except Exception as exc:
        if logger is not None:
            logger.debug('NCA bootstrap: failed to remove node help menu item %s: %s', _NODE_HELP_MENU_PATH, exc)

    return removed


def install_gui_modules():
    global _GUI_READY
    if _GUI_READY:
        return True

    logger = _get_logger()
    nuke = _nuke_module()
    if nuke is None or not is_gui_session():
        return False

    component_status = {
        'language_switcher': False,
        'windows_menu': False,
        'node_help': False,
        'management_commands': False,
    }

    with _profile_span('bootstrap.install_gui_modules'):
        try:
            with _profile_span('bootstrap.nuke_plugin_path'):
                nuke.pluginAddPath(_PKG_DIR)
        except Exception:
            pass

        try:
            with _profile_span('bootstrap.language_switcher_install'):
                ls = _import_local('language_switcher')
                if hasattr(ls, 'install_language_switcher'):
                    ls.install_language_switcher()
                if hasattr(ls, 'apply_startup_mode'):
                    ls.apply_startup_mode()
                elif hasattr(ls, 'toggle_language'):
                    ls.toggle_language()
                component_status['language_switcher'] = True
        except Exception as exc:
            _log_runtime_exception(logger, logging.DEBUG, 'bootstrap.language_switcher.install_failed', exc)

        try:
            with _profile_span('bootstrap.windows_menu_install'):
                windows_menu = _import_local('windows_menu_annotation')
                language_switcher = _safe_import('language_switcher')
                if bool(getattr(language_switcher, 'bilingual_mode', True)):
                    component_status['windows_menu'] = bool(
                        windows_menu.install_windows_menu_translator()
                    )
                elif hasattr(windows_menu, 'uninstall_windows_menu_translator'):
                    component_status['windows_menu'] = bool(
                        windows_menu.uninstall_windows_menu_translator()
                    )
        except Exception as exc:
            _log_runtime_exception(logger, logging.DEBUG, 'bootstrap.windows_menu.install_failed', exc)

        try:
            with _profile_span('bootstrap.node_help_install'):
                component_status['node_help'] = bool(_install_node_help(logger))
        except Exception as exc:
            _log_runtime_exception(logger, logging.DEBUG, 'bootstrap.node_help.install_failed', exc)

        with _profile_span('bootstrap.management_commands_install'):
            component_status['management_commands'] = bool(_install_management_commands())
        _GUI_READY = all(component_status.values())

    if not _GUI_READY:
        _log_runtime_event(
            logger,
            logging.DEBUG,
            'bootstrap.gui_modules.degraded',
            components=component_status,
        )
    return bool(_GUI_READY)


def install_all():
    _profile_begin('startup', reset=not _profile_has_session())
    _profile_mark('entry', 'install_all')
    try:
        with _profile_span('bootstrap.install_all'):
            ensure_bootstrap_ready()
            ensure_license_for_session(show_ui_if_needed=True)
            install_gui_modules()
        _profile_end(status='ok', save=True)
        return True
    except Exception:
        _profile_end(status='error', save=True)
        raise


def uninstall_all():
    global _BOOTSTRAP_READY, _GUI_READY, _MANAGEMENT_COMMANDS_READY
    global _INIT_ENTRY_RAN, _MENU_ENTRY_RAN, _MENU_START_SCHEDULED
    global _BOOTSTRAP_LIFECYCLE_TOKEN
    logger = _get_logger()

    # Cancel deferred startup/late-refresh work owned by this installation.
    # QTimer.singleShot is not cancellable on every supported Qt version, so a
    # per-install token makes already queued callbacks harmless.
    _BOOTSTRAP_LIFECYCLE_TOKEN = object()
    _MENU_START_SCHEDULED = False

    try:
        _apply_language_switcher_english(logger)
    except Exception as exc:
        _log_runtime_exception(logger, logging.DEBUG, 'bootstrap.language_switcher.rollback_failed', exc)

    try:
        _refresh_top_menu(logger)
    except Exception:
        pass

    for mod_name, fn_name in [
        ('viewer_toolbar_annotation', 'uninstall_viewer_toolbar_tooltips'),
        ('windows_menu_annotation', 'uninstall_windows_menu_translator'),
        ('_suppress_output', 'uninstall_print_filter'),
    ]:
        try:
            mod = _import_local(mod_name)
            if hasattr(mod, fn_name):
                getattr(mod, fn_name)()
        except Exception as exc:
            _log_runtime_exception(logger, logging.DEBUG, 'bootstrap.uninstall.step_failed', exc, module=mod_name, function=fn_name)

    try:
        _uninstall_node_help(logger)
    except Exception as exc:
        _log_runtime_exception(logger, logging.DEBUG, 'bootstrap.node_help.uninstall_failed', exc)

    try:
        _uninstall_management_commands(logger)
    except Exception as exc:
        _log_runtime_exception(logger, logging.DEBUG, 'bootstrap.management.uninstall_failed', exc)

    try:
        _restore_viewer_knob_tooltips_english()
    except Exception as exc:
        _log_runtime_exception(logger, logging.DEBUG, 'bootstrap.viewer_knob_tooltips.restore_failed', exc)

    try:
        runtime_state = _safe_import('runtime_state')
        if runtime_state is not None and hasattr(runtime_state, 'reset_runtime_state'):
            runtime_state.reset_runtime_state()
    except Exception as exc:
        _log_runtime_exception(logger, logging.DEBUG, 'bootstrap.runtime_state.reset_failed', exc)

    try:
        license_module = _safe_import('license_check')
        if license_module is not None:
            uninstall_guard = getattr(license_module, 'uninstall_import_guard', None)
            if callable(uninstall_guard):
                uninstall_guard(_PKG_DIR)
            license_module._NCA_BOOTSTRAPPED = False
            license_module._NCA_LICENSE_OK = False
            license_module._NCA_LICENSE_SKIPPED_IN_HEADLESS = False
    except Exception as exc:
        _log_runtime_exception(logger, logging.DEBUG, 'bootstrap.integrity_guard.uninstall_failed', exc)

    _BOOTSTRAP_READY = False
    _GUI_READY = False
    _MANAGEMENT_COMMANDS_READY = False
    _INIT_ENTRY_RAN = False
    _MENU_ENTRY_RAN = False
    return True


def _schedule_late_menu_refresh():
    if not is_gui_session():
        return
    try:
        qt_compat = _import_local('qt_compat')
        QtCore = getattr(qt_compat, 'QtCore', None)
    except Exception:
        QtCore = None
    if QtCore is None:
        return

    lifecycle_token = _BOOTSTRAP_LIFECYCLE_TOKEN

    def _refresh_once():
        if lifecycle_token is not _BOOTSTRAP_LIFECYCLE_TOKEN:
            return
        if not _GUI_READY:
            try:
                install_gui_modules()
            except Exception:
                pass
        try:
            _import_local('top_menu').translate_menu_toolbar()
        except Exception:
            pass

    # apply_startup_mode() has already performed the immediate menu pass.
    # Keep one delayed catch-up for menu items registered late by other
    # plug-ins, rather than traversing the same menu tree two more times.
    for ms in (1000,):
        try:
            QtCore.QTimer.singleShot(ms, _refresh_once)
        except Exception:
            pass


def _run_deferred_menu_startup(lifecycle_token=None):
    """Complete licensed GUI startup after Nuke's Qt event loop is available."""
    global _MENU_START_SCHEDULED

    if (
        lifecycle_token is not None
        and lifecycle_token is not _BOOTSTRAP_LIFECYCLE_TOKEN
    ):
        return False

    try:
        with _profile_span('bootstrap.menu_entry.deferred'):
            ensure_bootstrap_ready()
            ensure_license_for_session(show_ui_if_needed=True)
            install_gui_modules()
            _schedule_late_menu_refresh()
        _clear_startup_failure()
        _profile_end(status='ok', save=True)
        return True
    except Exception as exc:
        _profile_end(status='error', save=True)
        return _record_startup_failure('menu.deferred', exc)
    finally:
        if (
            lifecycle_token is None
            or lifecycle_token is _BOOTSTRAP_LIFECYCLE_TOKEN
        ):
            _MENU_START_SCHEDULED = False


def _schedule_deferred_menu_startup():
    """Schedule GUI initialization without opening UI while menu.py is executing."""
    global _MENU_START_SCHEDULED

    if _MENU_START_SCHEDULED:
        return True
    _MENU_START_SCHEDULED = True

    try:
        qt_compat = _import_local('qt_compat')
        QtCore = getattr(qt_compat, 'QtCore', None)
        if QtCore is None or not hasattr(QtCore, 'QTimer'):
            raise RuntimeError('QtCore.QTimer is unavailable')
        lifecycle_token = _BOOTSTRAP_LIFECYCLE_TOKEN
        QtCore.QTimer.singleShot(
            0,
            lambda: _run_deferred_menu_startup(lifecycle_token),
        )
        return True
    except Exception:
        _MENU_START_SCHEDULED = False
        # Let run_menu_entry isolate and report this error. Do not run the
        # licensing UI synchronously as a fallback from menu.py.
        raise


def run_init_entry():
    global _INIT_ENTRY_RAN
    if _INIT_ENTRY_RAN:
        return True

    gui = bool(is_gui_session())

    # NCA is a GUI-localization plug-in. Foundry executes init.py for both
    # interactive and command-line/render sessions, while menu.py is GUI-only.
    # Keep render-farm and terminal processes effectively untouched unless a
    # studio explicitly opts into headless integrity/licensing enforcement.
    if not gui and not _env_flag_enabled('NCA_ENFORCE_IN_HEADLESS'):
        os.environ.setdefault('NCA_PLUGIN_ROOT', _PKG_DIR)
        _clear_startup_failure()
        _INIT_ENTRY_RAN = True
        return True

    _profile_begin('startup', reset=True)
    _profile_mark('entry', 'init')
    _profile_mark('gui_session', gui)
    try:
        with _profile_span('bootstrap.init_entry'):
            ensure_bootstrap_ready()
            # Foundry loads init.py before the GUI is ready. Never open licensing
            # UI here; GUI licensing is deferred from menu.py instead.
            if not gui:
                ensure_license_for_session(show_ui_if_needed=False)
        _clear_startup_failure()
        _INIT_ENTRY_RAN = True
        # In GUI sessions menu.py continues this same startup profile after
        # Qt's event loop becomes available.  Keep the successful init phase
        # in memory and persist one authoritative snapshot only after the
        # deferred menu phase completes.  Headless/enforced sessions have no
        # menu phase, so they still save here.
        _profile_end(status='ok', save=not gui)
        return True
    except Exception as exc:
        _INIT_ENTRY_RAN = True
        _profile_end(status='error', save=True)
        return _record_startup_failure('init', exc)



def run_menu_entry():
    global _MENU_ENTRY_RAN
    if _MENU_ENTRY_RAN:
        return True
    _profile_begin('startup', reset=not _profile_has_session())
    _profile_mark('entry', 'menu')
    gui = bool(is_gui_session())
    _profile_mark('gui_session', gui)
    _MENU_ENTRY_RAN = True
    try:
        if gui:
            if not _schedule_deferred_menu_startup():
                return False
        else:
            _profile_end(status='ok', save=True)
        return True
    except Exception as exc:
        _profile_end(status='error', save=True)
        return _record_startup_failure('menu.schedule', exc)
