(function exposePageModuleRuntime(global) {
  "use strict";

  const definitions = new Map();
  const featureBindings = new Map();
  let sealed = false;

  function register(feature, viewIds, lifecycle = {}) {
    if (sealed) throw new Error("Page module registry is already sealed");
    if (!feature || !Array.isArray(viewIds) || !viewIds.length) {
      throw new Error("Page modules require a feature and at least one view ID");
    }
    if (typeof lifecycle.bind === "function") {
      const existing = featureBindings.get(feature);
      if (existing && existing !== lifecycle.bind) {
        throw new Error(`Duplicate feature binding owner: ${feature}`);
      }
      featureBindings.set(feature, lifecycle.bind);
    }
    viewIds.forEach((viewId) => {
      if (definitions.has(viewId)) throw new Error(`Duplicate page module: ${viewId}`);
      definitions.set(viewId, Object.freeze({
        feature,
        viewId,
        enter: Object.freeze([...(lifecycle.enter || [])]),
        leave: Object.freeze([...(lifecycle.leave || [])]),
      }));
    });
  }

  function create(options) {
    sealed = true;
    const pages = options.pages;
    const actions = Object.freeze({ ...(options.actions || {}) });
    const missing = pages.all.filter((page) => !definitions.has(page.id)).map((page) => page.id);
    if (missing.length) throw new Error(`Missing page modules: ${missing.join(", ")}`);
    let eventsBound = false;

    function bind() {
      if (eventsBound) return;
      eventsBound = true;
      featureBindings.forEach((handler) => handler());
    }

    function run(actionNames, context) {
      actionNames.forEach((actionName) => {
        const action = actions[actionName];
        if (typeof action !== "function") throw new Error(`Unknown page action: ${actionName}`);
        action(context);
      });
    }

    function beforeMount(viewId, previousView) {
      actions.closeTransientUi?.({ viewId, previousView });
      if (previousView && previousView !== viewId) {
        run(definitions.get(previousView)?.leave || [], { viewId, previousView });
      }
    }

    function afterMount(viewId, previousView) {
      const context = { viewId, previousView };
      actions.applyAccess?.(context);
      run(definitions.get(viewId)?.enter || [], context);
    }

    return Object.freeze({
      afterMount,
      beforeMount,
      bind,
      get: (viewId) => definitions.get(viewId) || null,
      has: (viewId) => definitions.has(viewId),
    });
  }

  global.XiaobaiPageModules = Object.freeze({ create, register });
})(window);
