/**
 * @wrnexus/csr — the browser reactive runtime.
 *
 * Components are `.wrn` files rendered on the SERVER (see @wrnexus/dev-server)
 * and hydrated in the browser by this single, generic runtime — served once at
 * `/__wrnexus/reactive.js` for any page that contains a `data-scope`. There are
 * no per-component browser bundles: SSR stays cleanly separated from CSR.
 */

import { REACTIVE_RUNTIME } from "./reactive-runtime.ts";
import { NAV_RUNTIME } from "./nav-runtime.ts";
import { REALTIME_RUNTIME } from "./realtime-runtime.ts";
import { ACTION_RUNTIME } from "./action-runtime.ts";

export { REACTIVE_RUNTIME } from "./reactive-runtime.ts";
export { NAV_RUNTIME } from "./nav-runtime.ts";
export { REALTIME_RUNTIME } from "./realtime-runtime.ts";
export { ACTION_RUNTIME } from "./action-runtime.ts";

const CONTROLLER_SECTIONS = ["PRIMARY", "UI", "PIN"] as const;

function runtimeForMode(development: boolean): string {
  if (development) {
    return REACTIVE_RUNTIME.replace(/\/\*__WRNEXUS_DEV_START__\*\//g, "").replace(
      /\/\*__WRNEXUS_DEV_END__\*\//g,
      "",
    );
  }
  return REACTIVE_RUNTIME.replace(
    /\/\*__WRNEXUS_DEV_START__\*\/[\s\S]*?\/\*__WRNEXUS_DEV_END__\*\//g,
    "",
  );
}

function controllerSection(source: string, name: (typeof CONTROLLER_SECTIONS)[number]): string {
  const pattern = new RegExp(
    `/\\*__WRNEXUS_CONTROLLERS_${name}_START__\\*/([\\s\\S]*?)/\\*__WRNEXUS_CONTROLLERS_${name}_END__\\*/`,
  );
  const match = pattern.exec(source);
  if (!match?.[1]) throw new Error(`Missing reactive runtime controller section: ${name}`);
  return match[1];
}

function stripControllerSections(source: string): string {
  for (const name of CONTROLLER_SECTIONS) {
    source = source.replace(
      new RegExp(
        `/\\*__WRNEXUS_CONTROLLERS_${name}_START__\\*/[\\s\\S]*?/\\*__WRNEXUS_CONTROLLERS_${name}_END__\\*/`,
      ),
      "",
    );
  }
  return source;
}

const CONTROLLER_LOADER = String.raw`
  var componentControllerSelector = "[data-wrn-anchored],[data-wrn-dialog],[data-wrn-roving],[data-wrn-scrollspy],[data-wrn-splitter],[data-wrn-navbar],[data-wrn-preferences],[data-wrn-select],[data-wrn-pin-input]";
  var componentControllerPromise = null;
  var componentControllerUrl = (document.currentScript && document.currentScript.src)
    ? new URL("./controllers.js", document.currentScript.src).href
    : "/__wrnexus/controllers.js";
  function needsComponentControllers(root) {
    var host = root || document;
    return !!((host.matches && host.matches(componentControllerSelector)) ||
      (host.querySelector && host.querySelector(componentControllerSelector)));
  }
  function loadComponentControllers(root) {
    if (!needsComponentControllers(root)) return;
    if (window.__wrnexusComponentControllers) {
      window.__wrnexusComponentControllers.hydrate(root || document);
      return;
    }
    if (!componentControllerPromise) {
      componentControllerPromise = new Promise(function (resolve, reject) {
        var script = document.createElement("script");
        script.src = componentControllerUrl;
        script.defer = true;
        script.onload = resolve;
        script.onerror = reject;
        document.head.appendChild(script);
      });
    }
    componentControllerPromise.then(function () {
      if (window.__wrnexusComponentControllers) window.__wrnexusComponentControllers.hydrate(root || document);
    }).catch(function (error) { console.error("[wrnexus] failed to load component controllers", error); });
  }
`;

/** The reactive runtime served at `/__wrnexus/reactive.js` (plain browser JS). */
export function getReactiveRuntime(development = false): string {
  let source = stripControllerSections(runtimeForMode(development));
  source = source.replace(
    "  function hydrateScopes(root) {",
    `${CONTROLLER_LOADER}\n  function hydrateScopes(root) {`,
  );
  source = source.replace(
    "    hydrateNavbarControllers(host);\n    hydratePreferenceControllers(host);\n    hydrateSelectControllers(host);\n    hydratePinInputControllers(host);",
    "    loadComponentControllers(host);",
  );
  source = source.replace("  startDocumentWatch();\n", "");
  source = source.replace(
    "  setupAnchoredOverlays();\n  setupModalDialogs();\n  setupRovingFocus();\n  setupScrollspy();\n  setupSplitters();",
    "  loadComponentControllers(document);",
  );
  source = source.replace(
    "  window.__wrnexusRepositionAnchored = repositionAnchored;",
    "  window.__wrnexusRepositionAnchored = function (root) { if (window.__wrnexusComponentControllers) window.__wrnexusComponentControllers.reposition(root); };",
  );
  source = source.replace(
    "  window.__wrnexusMountClientRoots = mountClientRoots;",
    `  window.__wrnexusControllerBridge = { invokeComponentOutput: invokeComponentOutput, callServerFunction: callServerFunction, dispatchComponentEvent: dispatchComponentEvent, emitPinInputEvent: emitPinInputEvent, parseScopeDecl: parseScopeDecl, warn: ${development ? "warnOnce" : "function () {}"} };\n  window.__wrnexusMountClientRoots = mountClientRoots;`,
  );
  return source;
}

/** Component-specific controllers, loaded only when their marker is present. */
export function getComponentControllerRuntime(development = false): string {
  const source = runtimeForMode(development);
  const sections = CONTROLLER_SECTIONS.map((name) => controllerSection(source, name)).join("\n");
  return `
(function () {
  "use strict";
  var bridge = window.__wrnexusControllerBridge || {};
  var invokeComponentOutput = bridge.invokeComponentOutput;
  var callServerFunction = bridge.callServerFunction;
  var dispatchComponentEvent = bridge.dispatchComponentEvent;
  var emitPinInputEvent = bridge.emitPinInputEvent;
  var parseScopeDecl = bridge.parseScopeDecl;
  var warnOnce = bridge.warn || function () {};
${sections}
  function hydrate(root) {
    var host = root || document;
    hydrateNavbarControllers(host);
    hydratePreferenceControllers(host);
    hydrateSelectControllers(host);
    hydratePinInputControllers(host);
    syncRovingGroups(host);
  }
  setupAnchoredOverlays();
  setupModalDialogs();
  setupRovingFocus();
  setupScrollspy();
  setupSplitters();
  window.__wrnexusComponentControllers = { hydrate: hydrate, reposition: repositionAnchored };
  hydrate(document);
})();
`.trim();
}

/** The client-side navigation runtime served at `/__wrnexus/nav.js`. */
export function getNavRuntime(): string {
  return NAV_RUNTIME;
}

/** The realtime client runtime served at `/__wrnexus/realtime.js`. */
export function getRealtimeRuntime(): string {
  return REALTIME_RUNTIME;
}

export function getActionRuntime(): string {
  return ACTION_RUNTIME;
}

export * from "./outputs.ts";
export * from "./server-client.ts";
export * from "./refs.ts";
export * from "./client-functions.ts";
export * from "./actions.ts";
export type * from "./types.ts";
