/**
 * Shared request runtime used by BOTH the dev server and the production server.
 *
 * It owns the HTTP/WebSocket dispatch and the SSR document assembly, but knows
 * nothing about *how* modules or assets are produced — those come in via
 * `RuntimeDeps`. Dev wires in dynamic module loading + on-the-fly bundling;
 * prod wires in a static manifest + pre-built chunks on disk.
 */

import {
  bridgeRealtime,
  createContext,
  createCorsPreflightResponse,
  createRealtimeRegistry,
  csrfToken,
  etag,
  isRoomDefinition,
  isWebSocketOriginAllowed,
  notModified,
  isSafeRequestPath,
  renderError,
  renderNotFound,
  withContextHeaders,
  withSecurityHeaders,
  resolveRequestUrl,
  type Context,
  type Middleware,
  type Mode,
  type PageMeta,
  type RealtimeBus,
  type RealtimeConnectMeta,
  type SecurityConfig,
  type SeoConfig,
  type TFunction,
} from "@wrnexus/core";
import type { Router } from "@wrnexus/router";
import { renderDocument } from "@wrnexus/ssr";
import {
  THEME_COOKIE,
  THEME_CSS_HREF,
  THEME_JS_HREF,
  resolveThemeName,
  type ResolvedTheme,
  type MobileConfig,
  type PwaConfig,
} from "@wrnexus/styles";
import {
  LANG_COOKIE,
  I18N_JS_HREF,
  makeT,
  resolveLang,
  translateHtml,
  type ResolvedI18n,
} from "@wrnexus/i18n";
import { runMiddleware } from "./pipeline.ts";
import type { HmrHub } from "./hmr.ts";

/** HTTP methods we recognize as API handler exports. */
const HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"] as const;

/** A realtime module's `websocket` export: a bag of optional lifecycle hooks. */
export type WsHandler = Record<string, (...args: any[]) => unknown>;

/**
 * Per-connection socket data. A socket is either an app realtime connection or
 * an internal HMR connection — discriminated by `kind`.
 */
export type WsData =
  | { kind: "realtime"; handler: WsHandler } // legacy raw `websocket` export
  | { kind: "room"; meta: RealtimeConnectMeta } // `defineRoom` default export
  | { kind: "hmr"; baseUrl: string; headers: [string, string][] };

type RouteModule = Record<string, unknown>;
type ApiRegistry = Record<string, unknown>;
interface CsrBinding {
  id: string;
  method?: string;
  path: string;
  body?: string;
  expr?: string;
  helpers?: string;
}

/** Serves framework-owned assets under `/__wrnexus/*` (islands, reactive, hmr). */
export interface AssetServer {
  serve(pathname: string): Promise<Response | null>;
}

export interface RuntimeDeps {
  mode: Mode;
  /** When true, inject the live-reload client into rendered pages. */
  hmr: boolean;
  router: Router;
  /** Load a route module by absolute path (dev: dynamic import; prod: manifest). */
  loadModule(file: string): Promise<RouteModule>;
  /** Resolve the ordered middleware chain. */
  getMiddleware(): Promise<Middleware[]>;
  /** Serve `/__wrnexus/*` assets. */
  assets: AssetServer;
  /** When true, inject the global stylesheet link into every page head. */
  hasStyles?: boolean;
  /** When true, inject the Wire UI stylesheet link (`/__wrnexus/ui.css`). */
  hasUi?: boolean;
  /** Production build combined theme + UI stylesheet. */
  hasFrameworkStyles?: boolean;
  /** App stylesheet already contains theme + UI CSS and is the only CSS request needed. */
  stylesIncludeFramework?: boolean;
  /** Resolved theme config: enables `/__wrnexus/theme.css` + `<html data-theme>`. */
  theme?: ResolvedTheme;
  /** Resolved i18n bundle: enables `ctx.t`, `<html lang>`, and `{t:key}` markers. */
  i18n?: ResolvedI18n;
  /** Small production stylesheets can be inlined to avoid a render-blocking request. */
  inlineStyles?: string;
  /** Production cache-busting version appended to framework asset URLs. */
  assetVersion?: string;
  /** Raw HTML appended to every page head (e.g. CDN framework links). */
  head?: string;
  /** Global SEO defaults. */
  seo?: SeoConfig;
  mobile?: MobileConfig;
  pwa?: PwaConfig | false;
  /** Framework security headers and CORS policy. */
  security?: SecurityConfig;
  /** Max request body size in bytes (413 above this). Default 10 MB. */
  maxBodyBytes?: number;
  /** HMR hub for browser live-update sockets (dev only). */
  hub?: HmrHub;
  /**
   * Cross-process realtime bus. When provided, room broadcasts/`toUser` sends are
   * bridged to it so they reach clients on every app process/instance sharing the
   * bus (use the Redis pub/sub driver). Enables realtime across multiple apps.
   */
  realtimeBus?: RealtimeBus;
}

export const PWA_CLIENT = `if ("serviceWorker" in navigator) {
  var swUrl = "/sw.js";
  if (window.trustedTypes) {
    try {
      swUrl = window.trustedTypes.createPolicy("wrnexus-pwa", { createScriptURL: function(value) { return value; } }).createScriptURL(swUrl);
    } catch (_) {}
  }
  addEventListener("load", () => navigator.serviceWorker.register(swUrl).catch(() => {}));
}`;

function renderPwaServiceWorker(pwa: PwaConfig): string {
  const offlineUrl = pwa.offlineUrl ?? pwa.startUrl ?? "/";
  const cacheUrls = [...new Set([offlineUrl, ...(pwa.cacheUrls ?? [])])];
  return `const CACHE = ${JSON.stringify(pwa.cacheName ?? "wrnexus-pwa-v1")};
const OFFLINE_URL = ${JSON.stringify(offlineUrl)};
const PRECACHE_URLS = ${JSON.stringify(cacheUrls)};
self.addEventListener("install", event => {
  event.waitUntil(caches.open(CACHE).then(cache => cache.addAll(PRECACHE_URLS)).catch(() => {}));
  self.skipWaiting();
});
self.addEventListener("activate", event => event.waitUntil(
  caches.keys().then(keys => Promise.all(keys.filter(key => key.startsWith("wrnexus-pwa-") && key !== CACHE).map(key => caches.delete(key))))
    .then(() => self.clients.claim())
));
self.addEventListener("fetch", event => {
  if (event.request.method !== "GET" || event.request.mode !== "navigate") return;
  event.respondWith(fetch(event.request).then(response => {
    if (response.ok) {
      const copy = response.clone();
      caches.open(CACHE).then(cache => cache.put(event.request, copy));
    }
    return response;
  }).catch(() => caches.match(event.request).then(hit => hit || caches.match(OFFLINE_URL))));
});`;
}

const DEFAULT_PWA_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
  <rect width="512" height="512" rx="112" fill="#6366f1"/>
  <path d="M112 144h64l42 168 38-128h48l38 128 42-168h64l-72 224h-60l-36-117-36 117h-60z" fill="white"/>
</svg>`;

export const MOBILE_CLIENT = `const isNativeApp = window.Capacitor?.isNativePlatform?.() === true ||
  ["android", "ios"].includes(window.Capacitor?.getPlatform?.());
const nativeTarget = isNativeApp ? "mobile" : "browser";
const nativeCapabilities = new Map();
const registerNative = (name, adapters) => nativeCapabilities.set(name, adapters);
const unregisterNative = name => nativeCapabilities.delete(name);
const nativeAdapter = name => nativeCapabilities.get(name)?.[nativeTarget];
const nativeSupports = name => {
  const adapter = nativeAdapter(name);
  return !!adapter && (adapter.supported?.() ?? true);
};
const nativeRun = async (name, options) => {
  const adapter = nativeAdapter(name);
  if (!adapter || !(adapter.supported?.() ?? true)) throw new Error('Native capability "' + name + '" is unavailable on ' + nativeTarget);
  return adapter.run(options);
};
const cap = (plugin, method) => ({
  supported: () => typeof window.Capacitor?.Plugins?.[plugin]?.[method] === "function",
  run: options => window.Capacitor.Plugins[plugin][method](options)
});
const capWith = (plugin, method, transform) => ({
  supported: () => typeof window.Capacitor?.Plugins?.[plugin]?.[method] === "function",
  run: options => window.Capacitor.Plugins[plugin][method](transform(options))
});
registerNative("clipboard.write", {
  browser: { supported: () => !!navigator.clipboard?.writeText, run: o => navigator.clipboard.writeText(String(o?.text ?? "")) },
  mobile: capWith("Clipboard", "write", o => ({ string: String(o?.text ?? o?.string ?? "") }))
});
registerNative("share", {
  browser: { supported: () => typeof navigator.share === "function", run: o => navigator.share(o) },
  mobile: cap("Share", "share")
});
registerNative("geolocation", {
  browser: { supported: () => !!navigator.geolocation, run: o => new Promise((resolve, reject) => navigator.geolocation.getCurrentPosition(resolve, reject, o)) },
  mobile: cap("Geolocation", "getCurrentPosition")
});
registerNative("network", {
  browser: { run: () => ({ connected: navigator.onLine }) }, mobile: cap("Network", "getStatus")
});
registerNative("camera", {
  browser: { supported: () => !!navigator.mediaDevices?.getUserMedia, run: o => navigator.mediaDevices.getUserMedia({ video: o ?? true, audio: false }) },
  mobile: cap("Camera", "getPhoto")
});
registerNative("haptics", { mobile: cap("Haptics", "impact") });
nativeCapabilities.get("haptics").browser = { supported: () => typeof navigator.vibrate === "function", run: o => navigator.vibrate(o?.duration ?? 20) };
registerNative("storage.get", { browser: { run: o => ({ value: localStorage.getItem(String(o?.key ?? "")) }) }, mobile: cap("Preferences", "get") });
registerNative("storage.set", { browser: { run: o => localStorage.setItem(String(o?.key ?? ""), String(o?.value ?? "")) }, mobile: cap("Preferences", "set") });
registerNative("filesystem.read", {
  browser: { supported: () => "showOpenFilePicker" in window, run: async () => { const [handle] = await window.showOpenFilePicker(); return handle?.getFile(); } },
  mobile: cap("Filesystem", "readFile")
});
registerNative("filesystem.write", {
  browser: { supported: () => "showSaveFilePicker" in window, run: async o => { const handle = await window.showSaveFilePicker(); const writable = await handle.createWritable(); await writable.write(o?.data ?? ""); await writable.close(); } },
  mobile: cap("Filesystem", "writeFile")
});
registerNative("notifications.schedule", {
  browser: { supported: () => typeof Notification !== "undefined", run: async o => { if (Notification.permission === "default") await Notification.requestPermission(); if (Notification.permission !== "granted") throw new Error("Notification permission was not granted"); return new Notification(o?.title ?? "Notification", { body: o?.body }); } },
  mobile: capWith("LocalNotifications", "schedule", o => o?.notifications ? o : ({ notifications: [{ id: o?.id ?? Date.now() % 2147483647, title: o?.title ?? "Notification", body: o?.body ?? "", schedule: o?.schedule }] }))
});
registerNative("device.info", { browser: { run: () => ({ platform: "web", userAgent: navigator.userAgent, language: navigator.language }) }, mobile: cap("Device", "getInfo") });
window.__WrNexusNativePending?.forEach((adapters, name) => registerNative(name, adapters));
delete window.__WrNexusNativePending;
window.WrNexusNative = { target: nativeTarget, isMobile: isNativeApp, register: registerNative, unregister: unregisterNative, supports: nativeSupports, run: nativeRun };
const revealMobileOnly = root => {
  const elements = [];
  if (root instanceof Element) elements.push(root);
  root.querySelectorAll?.("[data-mobile-only],[data-native-only],[data-native-requires],[data-native-unsupported]").forEach(element => elements.push(element));
  elements.forEach(element => {
    if (element.matches("[data-mobile-only]")) element.hidden = !isNativeApp;
    const only = element.getAttribute("data-native-only");
    if (only) element.hidden = !((only === "mobile" && isNativeApp) || (["browser", "web"].includes(only) && !isNativeApp) || only === window.Capacitor?.getPlatform?.());
    const required = element.getAttribute("data-native-requires");
    if (required) element.hidden = !nativeSupports(required);
    const unsupported = element.getAttribute("data-native-unsupported");
    if (unsupported) element.hidden = nativeSupports(unsupported);
  });
};
revealMobileOnly(document);
new MutationObserver(records => {
  records.forEach(record => record.addedNodes.forEach(node => {
    revealMobileOnly(node);
    installNativeEvents(node);
  }));
}).observe(document.documentElement, { childList: true, subtree: true });

const handleNativeEvent = async event => {
  const eventSelector = "[data-on-wrnexus-browser-" + event.type + "],[data-on-wrnexus-mobile-" + event.type + "]";
  const actionSelector = event.type === "click" ? ",[data-native-browser],[data-native-mobile]" : "";
  const element = event.target.closest?.(eventSelector + actionSelector);
  if (!element) return;
  const platformEvent = "wrnexus-" + nativeTarget + "-" + event.type;
  if (element.hasAttribute("data-on-" + platformEvent)) element.dispatchEvent(new CustomEvent(platformEvent));
  if (event.type !== "click") return;
  const capability = element.getAttribute("data-native-" + nativeTarget);
  if (!capability) return;
  let options = {};
  const raw = element.getAttribute("data-native-options");
  if (raw) try { options = JSON.parse(raw); } catch (error) {
    element.dispatchEvent(new CustomEvent("wrnexus:native-error", { detail: error, bubbles: true }));
    return;
  }
  try {
    const result = await nativeRun(capability, options);
    element.dispatchEvent(new CustomEvent("wrnexus:native-success", { detail: { capability, result }, bubbles: true }));
  } catch (error) {
    element.dispatchEvent(new CustomEvent("wrnexus:native-error", { detail: { capability, error }, bubbles: true }));
  }
};
const nativeEventTypes = new Set();
const listenNativeEvent = type => {
  if (nativeEventTypes.has(type)) return;
  nativeEventTypes.add(type);
  document.addEventListener(type, handleNativeEvent, true);
};
const installNativeEvents = root => {
  const elements = [];
  if (root instanceof Element) elements.push(root);
  root.querySelectorAll?.("*").forEach(element => elements.push(element));
  elements.forEach(element => Array.from(element.attributes).forEach(attribute => {
    const match = /^data-on-wrnexus-(?:browser|mobile)-(.+)$/.exec(attribute.name);
    if (match) listenNativeEvent(match[1]);
  }));
};
listenNativeEvent("click");
installNativeEvents(document);

const openWebCamera = async image => {
  if (!navigator.mediaDevices?.getUserMedia) return false;
  let stream;
  try {
    stream = await navigator.mediaDevices.getUserMedia({
      video: { facingMode: { ideal: "environment" } },
      audio: false
    });
    const overlay = document.createElement("div");
    Object.assign(overlay.style, {
      position: "fixed", inset: "0", zIndex: "2147483647", display: "grid",
      placeItems: "center", padding: "24px", background: "rgba(2,6,23,.92)"
    });
    const panel = document.createElement("div");
    Object.assign(panel.style, { width: "min(720px,100%)", color: "white" });
    const video = document.createElement("video");
    video.autoplay = true;
    video.muted = true;
    video.playsInline = true;
    video.srcObject = stream;
    Object.assign(video.style, {
      display: "block", width: "100%", maxHeight: "70vh", objectFit: "contain",
      borderRadius: "16px", background: "black"
    });
    const controls = document.createElement("div");
    Object.assign(controls.style, { display: "flex", gap: "12px", marginTop: "16px" });
    const capture = document.createElement("button");
    capture.type = "button";
    capture.textContent = "Capture photo";
    const cancel = document.createElement("button");
    cancel.type = "button";
    cancel.textContent = "Cancel";
    [capture, cancel].forEach(control => Object.assign(control.style, {
      flex: "1", padding: "14px", border: "0", borderRadius: "12px",
      fontWeight: "700", cursor: "pointer"
    }));
    capture.style.background = "#6366f1";
    capture.style.color = "white";
    const close = () => {
      stream.getTracks().forEach(track => track.stop());
      overlay.remove();
    };
    capture.onclick = () => {
      const canvas = document.createElement("canvas");
      canvas.width = video.videoWidth || 1280;
      canvas.height = video.videoHeight || 720;
      canvas.getContext("2d")?.drawImage(video, 0, 0, canvas.width, canvas.height);
      if (image) image.src = canvas.toDataURL("image/jpeg", 0.92);
      close();
    };
    cancel.onclick = close;
    controls.append(capture, cancel);
    panel.append(video, controls);
    overlay.append(panel);
    document.body.append(overlay);
    await video.play();
    return true;
  } catch (error) {
    stream?.getTracks().forEach(track => track.stop());
    return false;
  }
};

document.addEventListener("click", async event => {
  const button = event.target.closest?.("[data-mobile-camera]");
  if (!button) return;
  const selector = button.getAttribute("data-mobile-camera") || "[data-mobile-photo]";
  const image = document.querySelector(selector);
  try {
    const camera = window.Capacitor?.Plugins?.Camera;
    if (isNativeApp && camera) {
      const photo = await camera.getPhoto({ quality: 90, resultType: "uri", source: "camera" });
      if (image) image.src = photo.webPath || photo.path;
      return;
    }
    if (await openWebCamera(image)) return;
    const input = document.createElement("input");
    input.type = "file";
    input.accept = "image/*";
    input.setAttribute("capture", "environment");
    input.hidden = true;
    input.onchange = () => {
      if (image && input.files?.[0]) image.src = URL.createObjectURL(input.files[0]);
      input.remove();
    };
    document.body.append(input);
    input.click();
  } catch (error) {
    window.dispatchEvent(new CustomEvent("wrnexus:camera-error", { detail: error }));
  }
});

const nativeApp = window.Capacitor?.Plugins?.App;
nativeApp?.addListener?.("backButton", ({ canGoBack }) => {
  if (canGoBack || history.length > 1) history.back();
  // At the root screen intentionally do nothing instead of closing the app.
});`;

function revealMobileOnlyHtml(html: string): string {
  return html.replace(/<[^>]*\bdata-mobile-only\b[^>]*>/gi, (tag) =>
    tag.replace(/\s+hidden(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+))?/i, ""),
  );
}

/** The global stylesheet URL (served by the asset server when styles exist). */
export const STYLES_HREF = "/__wrnexus/styles.css";

/**
 * Inline HMR client (WebSocket). Goals:
 *  - CSS change  -> hot-swap the stylesheet, zero reload, zero flash.
 *  - markup/page -> ask over the existing HMR WebSocket for fresh HTML and
 *    MORPH the live DOM in place, without restarting the server.
 *  - island code -> same WebSocket sync path; no location.reload().
 */
export const HMR_CLIENT_JS = `
(function () {
  if (!("WebSocket" in window)) return;
  var proto = location.protocol === "https:" ? "wss" : "ws";
  var url = proto + "://" + location.host + "/__wrnexus/hmr";
  var ws, openedBefore = false, timer, pendingSync = false;

  function connect() {
    ws = new WebSocket(url);
    ws.onopen = function () {
      if (openedBefore) requestSync();   // reconnect == server restarted
      openedBefore = true;
    };
    ws.onmessage = function (e) {
      var msg; try { msg = JSON.parse(e.data); } catch (_) { return; }
      if (msg.type === "css") swapCss(msg.version);
      else if (msg.type === "reload") requestSync();
      else if (msg.type === "html") applyHtml(msg.html);
      else if (msg.type === "error") console.error("[wrnexus] HMR update failed:", msg.message);
    };
    ws.onclose = function () { clearTimeout(timer); timer = setTimeout(connect, 400); };
    ws.onerror = function () { try { ws.close(); } catch (_) {} };
  }

  function send(message) {
    if (!ws || ws.readyState !== WebSocket.OPEN) return false;
    ws.send(JSON.stringify(message));
    return true;
  }

  function requestSync() {
    if (pendingSync) return;
    pendingSync = true;
    if (!send({ type: "sync", path: location.pathname + location.search })) {
      pendingSync = false;
    }
  }

  function swapCss(version) {
    var links = document.querySelectorAll('link[rel="stylesheet"]');
    for (var i = 0; i < links.length; i++) (function (link) {
      var href = link.getAttribute("href");
      if (!href || href.charAt(0) !== "/") return;       // skip CDN links
      var next = href.split("?")[0] + "?hmr=" + encodeURIComponent(String(version || "css"));
      var copy = link.cloneNode(false);
      copy.setAttribute("href", next);
      copy.addEventListener("load", function () {
        if (link.parentNode) link.parentNode.removeChild(link);
      });
      link.parentNode.insertBefore(copy, link.nextSibling);
    })(links[i]);
  }

  function applyHtml(html) {
    pendingSync = false;
    var doc = new DOMParser().parseFromString(html, "text/html");
    if (doc.title) document.title = doc.title;
    var from = document.getElementById("app");
    var to = doc.getElementById("app");
    if (from && to) morph(from, to);
    swapCss("html");
    // Components are re-rendered server-side into the new HTML; re-hydrate
    // their reactive scopes (and any browser-side API fetches) in place.
    if (window.__wrnexusHydrateScopes) window.__wrnexusHydrateScopes(document);
    if (window.__wrnexusHydrateCsrFetches) window.__wrnexusHydrateCsrFetches(document);
  }

  // Minimal index-based DOM morph: preserve matching nodes (keeps state/focus),
  // patch text and attributes, clone genuinely new nodes, drop removed ones.
  // Hydrated subtrees (reactive scopes) are CLIENT-OWNED and left untouched,
  // so live state (e.g. a counter at 5) is never reset to the SSR 0.
  function morph(from, to) {
    if (from.__wrnexusHydrated) return;
    syncAttrs(from, to);
    var fc = from.childNodes, tc = to.childNodes, i;
    for (i = 0; i < tc.length; i++) {
      var t = tc[i], f = fc[i];
      if (!f) { from.appendChild(t.cloneNode(true)); continue; }
      if (f.nodeType !== t.nodeType || (f.nodeType === 1 && f.nodeName !== t.nodeName)) {
        from.replaceChild(t.cloneNode(true), f); continue;
      }
      if (f.nodeType === 3 || f.nodeType === 8) { if (f.nodeValue !== t.nodeValue) f.nodeValue = t.nodeValue; continue; }
      if (f.nodeType === 1) morph(f, t);
    }
    while (from.childNodes.length > tc.length) from.removeChild(from.lastChild);
  }
  function syncAttrs(from, to) {
    var ta = to.attributes, fa = from.attributes, i;
    for (i = 0; i < ta.length; i++) if (from.getAttribute(ta[i].name) !== ta[i].value) from.setAttribute(ta[i].name, ta[i].value);
    for (i = fa.length - 1; i >= 0; i--) if (!to.hasAttribute(fa[i].name)) from.removeAttribute(fa[i].name);
  }

  // Exposed for tests; harmless (the client is injected only in dev).
  window.__wrnexusHmr = { morph: morph, swapCss: swapCss, requestSync: requestSync, applyHtml: applyHtml };

  connect();
})();
`;

/** A base64 CSP nonce for this request's inline scripts. */
function randomNonce(): string {
  const bytes = new Uint8Array(16);
  crypto.getRandomValues(bytes);
  let bin = "";
  for (const b of bytes) bin += String.fromCharCode(b);
  return btoa(bin);
}

/** The dev HMR client as a nonce-tagged inline script (strict-CSP friendly). */
function hmrClientTag(nonce: string): string {
  return `<script nonce="${nonce}">${HMR_CLIENT_JS}</script>`;
}

/** 403 for a rejected cross-site WebSocket handshake. */
function forbiddenOrigin(): Response {
  return new Response("Forbidden WebSocket origin", { status: 403 });
}

export interface UpgradeServer {
  upgrade(req: Request, opts: { data: WsData }): boolean;
  /** Bun's per-request socket peer address (used for the non-spoofable client IP). */
  requestIP?(req: Request): { address: string } | null;
}

/** The subset of Bun's ServerWebSocket the runtime touches. */
export interface Ws {
  data: WsData;
  send(data: string | Uint8Array): unknown;
  close(code?: number, reason?: string): void;
}

export interface Handlers {
  fetch(req: Request, server: UpgradeServer): Promise<Response | undefined>;
  websocket: {
    open(ws: Ws): void;
    message(ws: Ws, message: string | Uint8Array): void;
    close(ws: Ws, code?: number, reason?: string): void;
    drain(ws: Ws): void;
  };
}

/** Build the fetch + websocket handlers from a set of dependencies. */
export function createHandlers(deps: RuntimeDeps): Handlers {
  const { mode, hmr, router, loadModule, getMiddleware, assets } = deps;
  const maxBodyBytes = deps.maxBodyBytes ?? 10 * 1024 * 1024; // 10 MB default

  // Server-side realtime room manager (shared by every `defineRoom` connection).
  const realtime = createRealtimeRegistry();
  // Bridge across processes/instances when a pub/sub bus is provided, so room
  // broadcasts reach clients on every app run sharing the bus (Redis in prod).
  if (deps.realtimeBus) bridgeRealtime(realtime, deps.realtimeBus);

  // Global <head> additions, identical on every page (styles are global).
  // Theme tokens load first so global.css and component styles can override them.
  // Order: theme tokens, then Wire UI, then the app stylesheet — so the app's
  // own CSS (loaded last) can override both the tokens and the UI classes.
  const headParts: string[] = [];
  if (deps.hasFrameworkStyles && !deps.stylesIncludeFramework) {
    headParts.push(
      `<link rel="stylesheet" href="${versionAssetUrl("/__wrnexus/framework.css", deps.assetVersion)}" />`,
    );
  } else if (deps.theme && !deps.stylesIncludeFramework) {
    const themeHref = versionAssetUrl(THEME_CSS_HREF, deps.assetVersion);
    headParts.push(`<link rel="stylesheet" href="${themeHref}" />`);
  }
  if (deps.hasUi && !deps.hasFrameworkStyles && !deps.stylesIncludeFramework) {
    headParts.push(
      `<link rel="stylesheet" href="${versionAssetUrl("/__wrnexus/ui.css", deps.assetVersion)}" />`,
    );
  }
  if (deps.inlineStyles) {
    headParts.push(`<style data-wrnexus-global>${escapeStyleContent(deps.inlineStyles)}</style>`);
  } else if (deps.hasStyles) {
    const stylesHref = versionAssetUrl(STYLES_HREF, deps.assetVersion);
    headParts.push(`<link rel="preload" href="${stylesHref}" as="style" />`);
    headParts.push(`<link rel="stylesheet" href="${stylesHref}" />`);
  }
  if (deps.head) headParts.push(deps.head);
  const extraHead = headParts.join("\n    ") || undefined;
  const pwaEnabled = deps.pwa !== false && deps.pwa?.enabled !== false;
  const pwaConfig: PwaConfig = deps.pwa && typeof deps.pwa === "object" ? deps.pwa : {};
  const pwaServiceWorkerEnabled = pwaEnabled && pwaConfig.serviceWorker !== false;

  const configuredPermissions = deps.security?.permissionsPolicy;
  const runtimeSecurity: SecurityConfig | undefined =
    deps.mobile?.enabled === false
      ? deps.security
      : {
          ...deps.security,
          permissionsPolicy:
            configuredPermissions === false
              ? false
              : { camera: ["self"], ...(configuredPermissions ?? {}) },
        };

  async function fetchHandler(req: Request, server: UpgradeServer): Promise<Response | undefined> {
    // Behind a trusted proxy (nginx / gateway), honor X-Forwarded-Proto/Host so
    // ctx.url reflects the external HTTPS scheme — makes CSRF/session cookies Secure.
    const url = resolveRequestUrl(req, deps.security?.trustProxy);
    const nonce = randomNonce();
    const secure = (res: Response): Response =>
      withSecurityHeaders(req, res, mode, runtimeSecurity, nonce);

    const preflight = createCorsPreflightResponse(req, deps.security);
    if (preflight) return secure(preflight);

    // Health check — unauthenticated, skips the middleware pipeline.
    if (url.pathname === "/healthz" || url.pathname === "/__wrnexus/health") {
      return secure(Response.json({ status: "ok" }));
    }

    if (url.pathname === "/site.webmanifest" && pwaEnabled) {
      const pwa = pwaConfig;
      return secure(
        Response.json(
          {
            id: pwa.id ?? pwa.startUrl ?? "/",
            name: pwa.name ?? deps.seo?.title ?? "WrNexus App",
            short_name: pwa.shortName ?? pwa.name ?? deps.seo?.title ?? "WrNexus",
            description: pwa.description ?? deps.seo?.description,
            start_url: pwa.startUrl ?? "/",
            scope: pwa.scope ?? "/",
            lang: pwa.lang ?? "en",
            display: pwa.display ?? "standalone",
            orientation: pwa.orientation ?? "any",
            theme_color: pwa.themeColor ?? deps.seo?.themeColor ?? "#6c8cff",
            background_color: pwa.backgroundColor ?? "#0f172a",
            icons: pwa.icons ?? [
              {
                src: "/__wrnexus/pwa-icon.svg",
                sizes: "any",
                type: "image/svg+xml",
                purpose: "any maskable",
              },
            ],
            categories: pwa.categories,
            screenshots: pwa.screenshots?.map((screenshot) => ({
              src: screenshot.src,
              sizes: screenshot.sizes,
              type: screenshot.type,
              form_factor: screenshot.formFactor,
              label: screenshot.label,
            })),
            shortcuts: pwa.shortcuts?.map((shortcut) => ({
              name: shortcut.name,
              short_name: shortcut.shortName,
              description: shortcut.description,
              url: shortcut.url,
              icons: shortcut.icons,
            })),
          },
          { headers: { "content-type": "application/manifest+json", "cache-control": "no-cache" } },
        ),
      );
    }
    if (url.pathname === "/__wrnexus/pwa-icon.svg" && pwaEnabled) {
      return secure(
        new Response(DEFAULT_PWA_ICON, {
          headers: { "content-type": "image/svg+xml", "cache-control": "public, max-age=86400" },
        }),
      );
    }
    if (url.pathname === "/sw.js" && pwaServiceWorkerEnabled) {
      return secure(
        new Response(renderPwaServiceWorker(pwaConfig), {
          headers: {
            "content-type": "text/javascript; charset=utf-8",
            "cache-control": "no-cache",
          },
        }),
      );
    }
    if (url.pathname === "/__wrnexus/pwa.js" && pwaServiceWorkerEnabled) {
      return secure(
        new Response(PWA_CLIENT, {
          headers: {
            "content-type": "text/javascript; charset=utf-8",
            "cache-control": url.searchParams.has("v")
              ? "public, max-age=31536000, immutable"
              : "no-cache",
          },
        }),
      );
    }
    if (url.pathname === "/__wrnexus/mobile.js" && deps.mobile?.enabled !== false) {
      return secure(
        new Response(MOBILE_CLIENT, {
          headers: {
            "content-type": "text/javascript; charset=utf-8",
            "cache-control": url.searchParams.has("v")
              ? "public, max-age=31536000, immutable"
              : "no-cache",
          },
        }),
      );
    }

    // Reject oversized request bodies early (DoS guard).
    const contentLength = Number(req.headers.get("content-length") ?? 0);
    if (contentLength > maxBodyBytes) {
      return secure(new Response("Payload Too Large", { status: 413 }));
    }

    // --- HMR socket (dev only): upgrade before anything else. ---
    if (hmr && url.pathname === "/__wrnexus/hmr") {
      if (!isWebSocketOriginAllowed(req, deps.security)) return secure(forbiddenOrigin());
      const headers = Array.from(req.headers.entries()).filter(
        ([name]) => !/^sec-websocket-|^connection$|^upgrade$/i.test(name),
      );
      if (server.upgrade(req, { data: { kind: "hmr", baseUrl: url.origin, headers } })) {
        return undefined;
      }
      return secure(new Response("Expected a WebSocket upgrade request", { status: 426 }));
    }

    // --- Realtime: try to upgrade BEFORE the normal HTTP pipeline. ---
    const rt = router.matchRealtime(url.pathname);
    if (rt) {
      // Reject cross-site WebSocket handshakes (CSWSH) before doing any work.
      if (!isWebSocketOriginAllowed(req, deps.security)) return secure(forbiddenOrigin());
      const mod = await loadModule(rt.route.file);
      let data: WsData;
      if (isRoomDefinition(mod.default)) {
        // Identify the connection: an authenticated session user id, else ?user=.
        const wsCtx = createContext(req, url);
        const sessionUser = wsCtx.session.get<{ id?: unknown }>("user");
        const user =
          sessionUser && typeof sessionUser === "object" && sessionUser.id != null
            ? String(sessionUser.id)
            : (url.searchParams.get("user") ?? undefined);
        const query = Object.fromEntries(url.searchParams);
        // Room-level gate (e.g. require auth) — reject before accepting the socket.
        const allowed = await mod.default.handlers.authorize?.({
          user,
          query,
          headers: req.headers,
        });
        if (allowed === false) {
          return secure(new Response("Forbidden", { status: 403 }));
        }
        data = { kind: "room", meta: { room: url.pathname, def: mod.default, query, user } };
      } else if (mod.websocket && typeof mod.websocket === "object") {
        data = { kind: "realtime", handler: mod.websocket as WsHandler };
      } else {
        return secure(
          new Response("Realtime route needs a default defineRoom() or a `websocket` export", {
            status: 500,
          }),
        );
      }
      const upgraded = server.upgrade(req, { data });
      if (upgraded) return undefined; // Bun takes over the socket.
      return secure(new Response("Expected a WebSocket upgrade request", { status: 426 }));
    }

    try {
      const ctx = createContext(req, url);
      ctx.ip = server.requestIP?.(req)?.address ?? undefined;
      ctx.locals.cspNonce = nonce; // available to pages for their own inline scripts
      // Resolve the request language so both pages and API can translate.
      if (deps.i18n) {
        ctx.lang = resolveLang(
          deps.i18n,
          ctx.cookies.get(LANG_COOKIE),
          req.headers.get("accept-language"),
        );
        ctx.t = makeT(deps.i18n, ctx.lang);
      }
      const mws = await getMiddleware();
      const res = secure(
        withContextHeaders(ctx, await runMiddleware(mws, ctx, () => dispatch(ctx))),
      );
      return compressResponse(req, res);
    } catch (err) {
      if (mode === "development") console.error(err);
      return compressResponse(req, secure(renderError(err, mode)));
    }
  }

  async function dispatch(ctx: Context): Promise<Response> {
    const { pathname } = ctx.url;

    // Framework-owned assets (island chunks, reactive runtime, HMR stream).
    if (pathname === "/__wrnexus/csr") {
      return handleCsrBinding(ctx);
    }

    if (pathname.startsWith("/__wrnexus/")) {
      const res = await assets.serve(pathname);
      return res ?? new Response("Not Found", { status: 404 });
    }

    if (pathname === "/api" || pathname.startsWith("/api/")) {
      return handleApi(ctx);
    }

    const publicAsset = await assets.serve(pathname);
    if (publicAsset) return publicAsset;

    return handlePage(ctx);
  }

  async function handleCsrBinding(ctx: Context): Promise<Response> {
    const routePath = ctx.url.searchParams.get("route") ?? "";
    const id = ctx.url.searchParams.get("id") ?? "";

    if (
      !routePath.startsWith("/") ||
      routePath.startsWith("/__wrnexus/") ||
      !isSafeRequestPath(routePath) ||
      !/^[A-Za-z0-9_-]+$/.test(id)
    ) {
      return new Response("Not Found", { status: 404 });
    }

    const page = router.matchPage(routePath);
    if (!page) return new Response("Not Found", { status: 404 });

    const pageModule = await loadModule(page.route.file);
    const bindings = pageModule.__wrnexusCsr;
    if (!Array.isArray(bindings)) return new Response("Not Found", { status: 404 });

    const binding = bindings.find((item): item is CsrBinding => {
      if (!item || typeof item !== "object") return false;
      const candidate = item as Partial<CsrBinding>;
      return (
        candidate.id === id &&
        typeof candidate.path === "string" &&
        (candidate.method === undefined || typeof candidate.method === "string") &&
        (typeof candidate.body === "string" || typeof candidate.expr === "string") &&
        (candidate.helpers === undefined || typeof candidate.helpers === "string")
      );
    });
    if (!binding || !isSafeApiPath(binding.path)) return new Response("Not Found", { status: 404 });

    const data = await callApiFromContext(ctx, binding.path, binding.method ?? "GET");
    const value = evalData(ctx, data, binding.body ?? binding.expr ?? "", binding.helpers ?? "");

    return new Response(String(value), {
      headers: { "content-type": "text/plain; charset=utf-8" },
    });
  }

  async function callApiFromContext(ctx: Context, path: string, method = "GET"): Promise<unknown> {
    if (!isSafeApiPath(path)) {
      throw new Error("Unsafe framework API path");
    }

    const normalizedMethod = method.toUpperCase();
    if (!HTTP_METHODS.includes(normalizedMethod as (typeof HTTP_METHODS)[number])) {
      throw new Error(`Unsupported framework API method: ${normalizedMethod}`);
    }

    const apiUrl = new URL(path, ctx.req.url);
    const apiReq = new Request(apiUrl, {
      method: normalizedMethod,
      headers: ctx.req.headers,
    });
    const apiCtx = createContext(apiReq, apiUrl);
    apiCtx.locals = ctx.locals;
    apiCtx.cookies = ctx.cookies;
    apiCtx.session = ctx.session;
    apiCtx.localStorage = ctx.localStorage;

    const apiRes = await handleApi(apiCtx);
    if (!apiRes.ok) {
      throw new Error(`API route ${apiUrl.pathname} returned ${apiRes.status}`);
    }

    const contentType = apiRes.headers.get("content-type") ?? "";
    return contentType.includes("application/json") ? await apiRes.json() : await apiRes.text();
  }

  function isSafeApiPath(pathname: string): boolean {
    return (
      (pathname === "/api" || pathname.startsWith("/api/")) &&
      isSafeRequestPath(pathname) &&
      !pathname.includes("?") &&
      !pathname.includes("#")
    );
  }

  function evalData(ctx: Context, data: unknown, body: string, helpers = ""): unknown {
    const adapters = {
      cookies: ctx.cookies,
      session: ctx.session,
      localStorage: ctx.localStorage,
    };
    return new Function(
      "$data",
      "$adapters",
      "const cookies = $adapters.cookies;\nconst session = $adapters.session;\nconst localStorage = $adapters.localStorage;\nwith ($data ?? {}) {\n" +
        helpers +
        "\n" +
        body +
        "\n}",
    )(data, adapters);
  }

  async function handleApi(ctx: Context): Promise<Response> {
    const matched = router.matchApi(ctx.url.pathname);
    if (!matched) return Response.json({ error: "Not Found" }, { status: 404 });

    const mod = await loadModule(matched.route.file);
    const method = ctx.req.method.toUpperCase();
    const embeddedApi = mod.__wrnexusApi as ApiRegistry | undefined;
    const handler = embeddedApi?.[`${method} ${matched.route.raw}`] ?? mod[method];

    if (typeof handler !== "function") {
      const allowed = HTTP_METHODS.filter(
        (m) =>
          typeof mod[m] === "function" ||
          typeof embeddedApi?.[`${m} ${matched.route.raw}`] === "function",
      );
      return new Response("Method Not Allowed", {
        status: 405,
        headers: { Allow: allowed.join(", ") },
      });
    }

    ctx.params = matched.params;
    return (await handler(ctx)) as Response;
  }

  /**
   * Resolve `<tag data-component="name" ...props></tag>` mounts by rendering the
   * matching `.wrn` component on the server and splicing its HTML in place.
   * Runs before script collection so the injected `data-scope` is visible.
   * Recurses so components can mount other components.
   */
  async function renderComponents(
    body: string,
    translate: TFunction = (key) => key,
    depth = 0,
  ): Promise<string> {
    if (depth > 15 || router.components.length === 0 || !body.includes("data-component=")) {
      return body;
    }

    let result = "";
    let i = 0;
    for (;;) {
      const m = MOUNT_OPEN_RE.exec(body.slice(i));
      if (!m) {
        result += body.slice(i);
        break;
      }
      const tagStart = i + m.index;
      result += body.slice(i, tagStart);

      const [open, tag, attrStr, name, selfClose] = m;
      const openEnd = tagStart + open.length;
      const { inner, end } =
        selfClose === "/" ? { inner: "", end: openEnd } : readElementBody(body, tag!, openEnd);
      i = end;

      const component = router.components.find((c) => c.name === name);
      if (!component) {
        console.warn(`[wrnexus] no component registered for '${name}'`);
        result += body.slice(tagStart, end);
        continue;
      }
      try {
        const mod = await loadModule(component.file);
        const render = (mod as { render?: (props: Record<string, string>) => string }).render;
        if (typeof render !== "function") {
          console.warn(`[wrnexus] component '${name}' has no render() export`);
          result += body.slice(tagStart, end);
          continue;
        }
        // Render, drop the mount's children into the component's <slot>, then
        // recurse so components-in-the-view and components-in-the-children resolve.
        // Props may carry `{t:key}` i18n markers — resolve them for the active
        // language before handing them to the component.
        const props = resolveTProps(parseComponentProps(attrStr!), translate);
        const rendered = fillSlots(String(render(props)), inner);
        result += await renderComponents(rendered, translate, depth + 1);
      } catch (err) {
        console.error(`[wrnexus] component '${name}' failed to render`, err);
        result += body.slice(tagStart, end);
      }
    }
    return result;
  }

  async function handlePage(ctx: Context): Promise<Response> {
    const isMobileRequest =
      ctx.req.headers.get("x-wrnexus-mobile") === "1" ||
      new RegExp(deps.mobile?.userAgent ?? "WrNexusMobile", "i").test(
        ctx.req.headers.get("user-agent") ?? "",
      );
    const matched = router.matchPage(ctx.url.pathname);
    if (!matched) {
      const response = renderNotFound();
      if (!isMobileRequest) return response;
      const headers = new Headers(response.headers);
      headers.set("x-wrnexus-original-status", "404");
      // Android WebView may replace a main-frame HTTP error with its own error
      // surface. Keep the WrNexus 404 document renderable inside Capacitor.
      return new Response(response.body, { status: 200, headers });
    }

    // Issue the CSRF token cookie so forms on this page can echo it back.
    csrfToken(ctx);

    const mod = await loadModule(matched.route.file);
    const component = mod.default;
    if (typeof component !== "function") {
      throw new Error(`Page ${matched.route.file} has no default export`);
    }

    ctx.params = matched.params;
    const meta = (mod.meta ?? {}) as PageMeta;
    const pageCtx = ctx as Context & {
      __wrnexusCallApi?: (path: string, method?: string) => Promise<unknown>;
    };
    pageCtx.__wrnexusCallApi = (path, method = "GET") => callApiFromContext(ctx, path, method);
    let body = await renderComponents(String(await component(pageCtx)), ctx.t);

    // Page layout: a page selects one by exporting `layout = "<name>"`
    // (app/layouts/<name>.wrn), else falls back to a `default` layout if one
    // exists. `layout = "none"` opts out. The layout wraps the body via <slot>.
    const layoutName =
      (isMobileRequest && deps.mobile?.layout
        ? deps.mobile.layout
        : typeof mod.layout === "string"
          ? mod.layout
          : undefined) ?? "default";
    const layout =
      layoutName === "none" ? undefined : router.layouts.find((l) => l.name === layoutName);
    if (layout) {
      try {
        const layoutMod = await loadModule(layout.file);
        const layoutRender = (layoutMod as { render?: (p: Record<string, string>) => string })
          .render;
        if (typeof layoutRender === "function") {
          body = await renderComponents(fillSlots(String(layoutRender({})), body), ctx.t);
        }
      } catch (err) {
        console.error(`[wrnexus] layout '${layoutName}' failed to render`, err);
      }
    }

    if (isMobileRequest) body = revealMobileOnlyHtml(body);

    // i18n: resolve `{t:key}` / `t:attr` markers against the request language.
    if (deps.i18n) body = translateHtml(body, ctx.t);

    // Point 3: only ship the JS this page actually uses.
    const scripts = collectScripts(body).map((src) => versionAssetUrl(src, deps.assetVersion));
    if (pwaServiceWorkerEnabled)
      scripts.push(versionAssetUrl("/__wrnexus/pwa.js", deps.assetVersion));
    if (deps.mobile?.enabled !== false && usesMobileRuntime(body))
      scripts.push(versionAssetUrl("/__wrnexus/mobile.js", deps.assetVersion));

    // <html> attributes: no-flash theme (from cookie, validated) + active lang.
    const attrs: string[] = [];
    if (deps.theme)
      attrs.push(`data-theme="${resolveThemeName(ctx.cookies.get(THEME_COOKIE), deps.theme)}"`);
    const language = deps.i18n && ctx.lang ? ctx.lang : (meta.lang ?? deps.seo?.lang ?? "en");
    attrs.push(`lang="${safeLanguageTag(language)}"`);
    const htmlAttrs = attrs.length ? ` ${attrs.join(" ")}` : undefined;

    const html = renderDocument({
      meta,
      seo: deps.seo,
      url: ctx.url,
      body,
      scripts,
      extraHead: [
        pwaEnabled ? `<link rel="manifest" href="/site.webmanifest" />` : "",
        pwaEnabled ? `<meta name="mobile-web-app-capable" content="yes" />` : "",
        pwaEnabled ? `<meta name="apple-mobile-web-app-capable" content="yes" />` : "",
        extraHead,
      ]
        .filter(Boolean)
        .join("\n    "),
      extraBody: hmr ? hmrClientTag((ctx.locals.cspNonce as string) ?? "") : undefined,
      htmlAttrs,
    });
    // Conditional GET: hash the page CONTENT (`body`), not the assembled shell —
    // the shell carries a per-request CSP nonce in dev, which would otherwise make
    // the ETag change every request. Same content → same ETag → 304 on revalidate.
    const tag = etag(`${htmlAttrs ?? ""}\n${scripts.join(",")}\n${body}`);
    const method = ctx.req.method.toUpperCase();
    if ((method === "GET" || method === "HEAD") && notModified(ctx.req, tag)) {
      return new Response(null, {
        status: 304,
        headers: { etag: tag, "cache-control": "private, no-cache" },
      });
    }
    return new Response(html, {
      headers: {
        "content-type": "text/html; charset=utf-8",
        etag: tag,
        "cache-control": "private, no-cache",
      },
    });
  }

  const hub = deps.hub;

  async function handleHmrMessage(ws: Ws, message: string | Uint8Array): Promise<void> {
    let msg: { type?: unknown; path?: unknown };
    try {
      msg = JSON.parse(typeof message === "string" ? message : new TextDecoder().decode(message));
    } catch {
      return;
    }
    if (msg.type !== "sync") return;
    const path = typeof msg.path === "string" ? msg.path : "/";
    let url: URL;
    try {
      url = new URL(path, ws.data.kind === "hmr" ? ws.data.baseUrl : "http://localhost");
    } catch {
      ws.send(JSON.stringify({ type: "error", message: "Invalid HMR sync path" }));
      return;
    }
    if (
      url.origin !== (ws.data.kind === "hmr" ? ws.data.baseUrl : url.origin) ||
      !url.pathname.startsWith("/") ||
      url.pathname.startsWith("/__wrnexus/") ||
      !isSafeRequestPath(url.pathname)
    ) {
      ws.send(JSON.stringify({ type: "error", message: "Unsafe HMR sync path" }));
      return;
    }

    try {
      const headers = new Headers(ws.data.kind === "hmr" ? ws.data.headers : undefined);
      headers.set("accept", "text/html");
      headers.set("x-wrnexus-hmr", "1");
      const req = new Request(url, { headers });
      const ctx = createContext(req, url);
      ctx.locals.cspNonce = randomNonce();
      if (deps.i18n) {
        ctx.lang = resolveLang(
          deps.i18n,
          ctx.cookies.get(LANG_COOKIE),
          req.headers.get("accept-language"),
        );
        ctx.t = makeT(deps.i18n, ctx.lang);
      }
      const res = withContextHeaders(
        ctx,
        await runMiddleware(await getMiddleware(), ctx, () => dispatch(ctx)),
      );
      const html = await res.text();
      ws.send(JSON.stringify({ type: "html", html }));
    } catch (err) {
      if (mode === "development") console.error(err);
      ws.send(
        JSON.stringify({
          type: "error",
          message: err instanceof Error ? err.message : "Unknown HMR sync error",
        }),
      );
    }
  }

  return {
    fetch: fetchHandler,
    websocket: {
      open(ws) {
        if (ws.data?.kind === "hmr") hub?.add(ws);
        else if (ws.data?.kind === "room") void realtime.open(ws, ws.data.meta);
        else ws.data?.handler?.open?.(ws);
      },
      message(ws, message) {
        if (ws.data?.kind === "hmr") {
          void handleHmrMessage(ws, message);
          return;
        }
        if (ws.data?.kind === "room") void realtime.message(ws, message);
        else ws.data?.handler?.message?.(ws, message);
      },
      close(ws, code, reason) {
        if (ws.data?.kind === "hmr") hub?.remove(ws);
        else if (ws.data?.kind === "room") void realtime.close(ws);
        else ws.data?.handler?.close?.(ws, code, reason);
      },
      drain(ws) {
        if (ws.data?.kind === "hmr" || ws.data?.kind === "room") return;
        ws.data?.handler?.drain?.(ws);
      },
    },
  };
}

/**
 * Decode the handful of HTML entities the page compiler emits when escaping
 * attribute values, so component props arrive as the author wrote them.
 */
function decodeHtmlEntities(value: string): string {
  return value
    .replace(/&quot;/g, '"')
    .replace(/&#39;/g, "'")
    .replace(/&lt;/g, "<")
    .replace(/&gt;/g, ">")
    .replace(/&amp;/g, "&");
}

/** Content types worth gzipping (text + text-like application types). */
const COMPRESSIBLE_TYPE =
  /^(?:text\/|application\/(?:json|xml|javascript|manifest\+json)|image\/svg\+xml)/i;
/** Below this size gzip's overhead isn't worth it. */
const COMPRESS_MIN_BYTES = 1024;

/**
 * Gzip a response when the client accepts it and the body is a compressible,
 * buffered (non-streaming) payload. Streaming/SSE responses opt out via
 * `Cache-Control: no-transform`, so they are never buffered here.
 */
async function compressResponse(req: Request, res: Response): Promise<Response> {
  const accept = req.headers.get("accept-encoding") ?? "";
  if (!accept.toLowerCase().includes("gzip")) return res;
  if (res.headers.get("content-encoding")) return res;
  if (res.status === 204 || res.status === 304) return res;
  if (!COMPRESSIBLE_TYPE.test(res.headers.get("content-type") ?? "")) return res;
  if ((res.headers.get("cache-control") ?? "").includes("no-transform")) return res;

  const body = new Uint8Array(await res.arrayBuffer());
  if (body.length < COMPRESS_MIN_BYTES) {
    return new Response(body, {
      status: res.status,
      statusText: res.statusText,
      headers: res.headers,
    });
  }
  const gzipped = Bun.gzipSync(body);
  const headers = new Headers(res.headers);
  headers.set("content-encoding", "gzip");
  headers.set("content-length", String(gzipped.length));
  const vary = headers.get("Vary");
  if (!vary) headers.set("Vary", "Accept-Encoding");
  else if (!/\baccept-encoding\b/i.test(vary)) headers.set("Vary", `${vary}, Accept-Encoding`);
  return new Response(gzipped, { status: res.status, statusText: res.statusText, headers });
}

/** Opening tag of a component mount: captures tag, attrs, name, self-close. */
const MOUNT_OPEN_RE =
  /<([A-Za-z][A-Za-z0-9-]*)\b([^>]*?\bdata-component="([A-Za-z0-9_-]+)"[^>]*?)(\/?)>/;

/**
 * From `from` (just past a mount's opening `>`), return the element's inner HTML
 * and the index just past its matching close tag. Counts same-tag nesting so a
 * `<div data-component>` may contain other `<div>`s.
 */
/** Cache of per-tag "open tag" regexes so we don't recompile one per call. */
const OPEN_TAG_RE = new Map<string, RegExp>();
function openTagRe(tag: string): RegExp {
  let re = OPEN_TAG_RE.get(tag);
  if (!re) {
    re = new RegExp(`<${tag}\\b`, "g");
    OPEN_TAG_RE.set(tag, re);
  }
  return re;
}

export function readElementBody(
  html: string,
  tag: string,
  from: number,
): { inner: string; end: number } {
  const openRe = openTagRe(tag);
  const closeTag = `</${tag}>`;
  let depth = 1;
  let i = from;
  while (i < html.length) {
    const nextClose = html.indexOf(closeTag, i);
    if (nextClose === -1) return { inner: html.slice(from), end: html.length };
    openRe.lastIndex = i;
    const openMatch = openRe.exec(html);
    if (openMatch && openMatch.index < nextClose) {
      const gt = html.indexOf(">", openMatch.index);
      if (gt !== -1 && html[gt - 1] === "/") {
        i = gt + 1; // self-closing child: no depth change
      } else {
        depth++;
        i = gt === -1 ? html.length : gt + 1;
      }
    } else {
      depth--;
      if (depth === 0)
        return { inner: html.slice(from, nextClose), end: nextClose + closeTag.length };
      i = nextClose + closeTag.length;
    }
  }
  return { inner: html.slice(from), end: html.length };
}

/**
 * Split a mount's children into named slots and the default slot. Content inside
 * `<tag data-slot="name">…</tag>` goes to that named slot (the wrapper element is
 * dropped); everything else is the default slot.
 */
function extractSlots(inner: string): { named: Record<string, string>; def: string } {
  const named: Record<string, string> = {};
  if (!inner.includes("data-slot=")) return { named, def: inner };

  const SLOT_MOUNT_RE =
    /<([A-Za-z][A-Za-z0-9-]*)\b([^>]*?\bdata-slot="([A-Za-z0-9_-]+)"[^>]*?)(\/?)>/;
  let def = "";
  let i = 0;
  for (;;) {
    const m = SLOT_MOUNT_RE.exec(inner.slice(i));
    if (!m) {
      def += inner.slice(i);
      break;
    }
    const tagStart = i + m.index;
    def += inner.slice(i, tagStart);
    const [open, tag, , name, selfClose] = m;
    const openEnd = tagStart + open.length;
    const { inner: content, end } =
      selfClose === "/" ? { inner: "", end: openEnd } : readElementBody(inner, tag!, openEnd);
    named[name!] = content;
    i = end;
  }
  return { named, def };
}

/**
 * Replace `<slot>` elements in a component's output with the mount's children:
 *   <slot name="x">…</slot>  ← content from `data-slot="x"` on the mount
 *   <slot>…</slot>           ← everything else (the default slot)
 * A `<slot>fallback</slot>` keeps its fallback when nothing is provided.
 */
export function fillSlots(html: string, inner: string): string {
  if (!/<slot\b/.test(html)) return html;
  const { named, def } = extractSlots(inner);
  const defTrimmed = def.trim();

  return html
    .replace(
      /<slot\b[^>]*?\bname="([A-Za-z0-9_-]+)"[^>]*?\/>/g,
      (_m, name: string) => named[name] ?? "",
    )
    .replace(
      /<slot\b[^>]*?\bname="([A-Za-z0-9_-]+)"[^>]*?>([\s\S]*?)<\/slot>/g,
      (_m, name: string, fallback: string) =>
        named[name] != null && named[name]!.trim() ? named[name]! : fallback,
    )
    .replace(/<slot\s*\/>/g, defTrimmed ? def : "")
    .replace(/<slot\b[^>]*>([\s\S]*?)<\/slot>/g, (_m, fallback: string) =>
      defTrimmed ? def : fallback,
    );
}

/**
 * Extract props from a `data-component` mount's attribute string. Every quoted
 * attribute other than `data-component` becomes a (string) prop; the component's
 * declared defaults coerce them to their final types.
 */
export function parseComponentProps(attrStr: string): Record<string, string> {
  const props: Record<string, string> = {};
  for (const m of attrStr.matchAll(/([A-Za-z_][\w-]*)="([^"]*)"/g)) {
    const key = m[1]!;
    if (key === "data-component") continue;
    props[key] = decodeHtmlEntities(m[2]!);
  }
  return props;
}

/**
 * Resolve `{t:key}` i18n markers inside component prop values against the active
 * language, so authors can pass localized text into a component:
 * `<div data-component="badge" label="{t:status.new}">`.
 */
export function resolveTProps(
  props: Record<string, string>,
  translate: TFunction,
): Record<string, string> {
  for (const key of Object.keys(props)) {
    const value = props[key]!;
    if (value.includes("{t:")) {
      props[key] = value.replace(/\{t:\s*([^}]+?)\s*\}/g, (_m, tkey: string) =>
        translate(tkey.trim()),
      );
    }
  }
  return props;
}

/**
 * Decide which framework scripts a rendered page needs. Components are already
 * server-rendered into the HTML; the only script is the reactive runtime, and
 * only when the page actually contains a scope or a browser-side API fetch.
 */
export function collectScripts(body: string): string[] {
  // Client-side navigation is an app-wide progressive enhancement: it must load
  // on every page (you navigate *from* any page), and degrades to full loads.
  const scripts: string[] = ["/__wrnexus/nav.js"];
  if (/\bdata-scope=/.test(body) || /\bdata-wrnexus-csr=/.test(body)) {
    scripts.push("/__wrnexus/reactive.js");
  }
  // The theme runtime is only needed when the page can switch themes.
  if (/\bdata-wire-theme-(toggle|set)\b/.test(body)) {
    scripts.push(THEME_JS_HREF);
  }
  // Validation: schema descriptors + the generic validator, only for pages with a form.
  if (/\bdata-schema="[A-Za-z0-9_-]+"/.test(body)) {
    scripts.push("/__wrnexus/schemas.js", "/__wrnexus/validate.js");
  }
  // Language switcher runtime, only when the page has one.
  if (/\bdata-wire-lang-set\b/.test(body) || /\bselect[^>]*\bdata-wire-lang\b/.test(body)) {
    scripts.push(I18N_JS_HREF);
  }
  // Realtime client, only when the page declares a room.
  if (/\bdata-room=/.test(body)) {
    scripts.push("/__wrnexus/realtime.js");
  }
  // File-upload runtime (drag-drop + progress), only when a page has an uploader.
  if (/\bdata-uploader\b/.test(body)) {
    scripts.push("/__wrnexus/uploader.js");
  }
  return scripts;
}

/** Whether rendered markup needs the Capacitor/native browser bridge. */
export function usesMobileRuntime(body: string): boolean {
  return /\b(?:data-mobile-[\w-]+|data-native-(?:browser|mobile|only|requires|unsupported|options)|data-on-wrnexus-(?:browser|mobile)-[\w-]+)\b/.test(
    body,
  );
}

function safeLanguageTag(value: string): string {
  return /^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$/.test(value) ? value : "en";
}

function versionAssetUrl(src: string, version?: string): string {
  if (!version) return src;
  return `${src}${src.includes("?") ? "&" : "?"}v=${encodeURIComponent(version)}`;
}

function escapeStyleContent(css: string): string {
  return css.replace(/<\/style/gi, "<\\/style");
}
