import { createMindmapHostRuntime } from './document-mindmap-host-runtime.js';
import { createResourceTabRuntime } from './document-resource-tab-runtime.js';
import { createDocumentSessionRuntime } from './document-session-runtime.js';
import {
  installGlobalSlashMenuPositioning,
  markIntendedSlashRoot,
  observeSlashMenuPosition,
  scheduleSlashMenuPosition,
} from './document-slash-position-runtime.js';
import {
  buildPaneRuntime,
  clearSecondaryParams,
  createSecondaryPaneWidthController,
  currentUrl,
  installSecondaryPaneCloseButtons,
  installSecondaryPaneOpenListeners,
  pushUrlState,
  replaceUrlState,
  secondaryQueryParamNames,
} from './document-pane-runtime.js';
import {
  conflictDetectionKeyBelongsToSession,
  hydrateMindmapAttrsFromDom,
  legacyInlineContentToTiptap,
  toTiptapDocument,
} from './document-tiptap-conversion-runtime.js';

(() => {
  const PANES_BOOTSTRAP_ID = '__MNOTE_DOCUMENT_PANES_BOOTSTRAP__';
  const ROOT_SELECTOR = '[data-testid="mnote-leptos-tiptap-island-editor-root"]';
  const EVENT_PREFIX = 'mnote:leptos-tiptap-spike';
  const CHANGE_EVENT = `${EVENT_PREFIX}:change`;
  const SAVE_EVENT = `${EVENT_PREFIX}:save-request`;
  const READY_EVENT = `${EVENT_PREFIX}:ready`;
  const STATE_EVENT = `${EVENT_PREFIX}:state`;
  const ERROR_EVENT = `${EVENT_PREFIX}:error`;
  const COMMAND_EVENT = `${EVENT_PREFIX}:command`;
  const BRIDGE_PROTOCOL = 'mnote.leptos_tiptap.bridge.v1';

  const parseJsonScript = (id) => {
    const node = document.getElementById(id);
    if (!node) return null;
    try {
      return JSON.parse(node.textContent || 'null');
    } catch (error) {
      console.warn(`mnote rust-web editor bootstrap JSON 解析失败: ${id}`, error);
      return null;
    }
  };

  const panesBootstrap = parseJsonScript(PANES_BOOTSTRAP_ID);
  if (!panesBootstrap || !Array.isArray(panesBootstrap.panes)) return;

  if (panesBootstrap.secondaryInvalid === true) clearSecondaryParams();
  installSecondaryPaneOpenListeners();
  installSecondaryPaneCloseButtons();
  const { workspace, applyStoredSecondaryWidth } = createSecondaryPaneWidthController();
  applyStoredSecondaryWidth();

  const paneRuntimes = panesBootstrap.panes
    .map((paneDescriptor) => buildPaneRuntime(paneDescriptor, ROOT_SELECTOR))
    .filter(Boolean);

  const loadRuntime = async () => {
    if (window.__mnoteLeptosTiptapRuntimePromise) {
      return window.__mnoteLeptosTiptapRuntimePromise;
    }
    window.__mnoteLeptosTiptapRuntimePromise = (async () => {
      const manifestResponse = await fetch('/api/leptos-tiptap-runtime/manifest.json');
      if (!manifestResponse.ok) throw new Error(`manifest_failed_${manifestResponse.status}`);
      const manifest = await manifestResponse.json();
      if (!manifest.entryAssetPath) throw new Error('island manifest 缺少 entryAssetPath');
      const entryUrl = `/api/leptos-tiptap-runtime/${manifest.entryAssetPath}`;
      const wasmUrl = manifest.wasmAssetPath ? `/api/leptos-tiptap-runtime/${manifest.wasmAssetPath}` : undefined;
      const runtime = await import(entryUrl);
      if (typeof runtime.default !== 'function' || typeof runtime.mount !== 'function' || typeof runtime.unmount !== 'function') {
        throw new Error('island runtime 导出不完整');
      }
      await runtime.default({ module_or_path: wasmUrl });
      if (typeof runtime.mount_mindmap_shell === 'function' && typeof runtime.unmount_mindmap_shell === 'function') {
        window.__MNOTE_MINDMAP_RUST_SHELL__ = {
          mount: runtime.mount_mindmap_shell,
          unmount: runtime.unmount_mindmap_shell,
        };
      }
      return runtime;
    })();
    return window.__mnoteLeptosTiptapRuntimePromise;
  };

  // Tiptap/legacy conversion helpers live in document-tiptap-conversion-runtime.js.
  const normalizeBridgeValue = (value) => {
    if (value instanceof Map) {
      const out = {};
      for (const [key, item] of value.entries()) out[key] = normalizeBridgeValue(item);
      return out;
    }
    if (Array.isArray(value)) return value.map(normalizeBridgeValue);
    if (value && typeof value === 'object') {
      return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, normalizeBridgeValue(item)]));
    }
    return value;
  };

  const normalizeEnvelopePayload = (event) => {
    const detail = normalizeBridgeValue(event?.detail);
    if (!detail || typeof detail !== 'object') return null;
    const payload = detail.payload && typeof detail.payload === 'object' ? detail.payload : detail;
    return payload && typeof payload === 'object' ? payload : null;
  };

  const setStatus = (runtime, status, message) => {
    runtime.root.setAttribute('data-runtime-editor-status', status);
    if (message) {
      runtime.root.setAttribute('data-runtime-editor-error', message);
    } else {
      runtime.root.removeAttribute('data-runtime-editor-error');
    }
    if (runtime.observability instanceof HTMLElement) {
      runtime.observability.setAttribute('data-editor-host-status', status);
      runtime.observability.setAttribute('data-editor-host-active', 'leptos_tiptap_island');
    }
  };

  const enhanceEditorAttachmentLinksSoon = () => {
    const run = () => {
      if (typeof window.__mnoteEnhanceEditorAttachmentLinks === 'function') {
        window.__mnoteEnhanceEditorAttachmentLinks();
      }
    };
    run();
    [120, 500, 1200].forEach((delayMs) => window.setTimeout(run, delayMs));
  };

  const pageAggregateUrl = (bootstrap) => {
    const url = new URL(`/api/page-aggregate/${encodeURIComponent(bootstrap.documentId)}`, window.location.origin);
    url.searchParams.set('sourceKind', bootstrap.sourceKind || 'local_folder');
    if (bootstrap.workspaceId) url.searchParams.set('workspaceId', bootstrap.workspaceId);
    if (bootstrap.rootUri) url.searchParams.set('rootUri', bootstrap.rootUri);
    return url;
  };
  const pageAggregateUrlFromDescriptor = (descriptor) => {
    const url = new URL(`/api/page-aggregate/${encodeURIComponent(descriptor.documentId)}`, window.location.origin);
    url.searchParams.set('sourceKind', descriptor.sourceKind || 'convex_workspace');
    if (descriptor.workspaceId) url.searchParams.set('workspaceId', descriptor.workspaceId);
    if (descriptor.rootUri) url.searchParams.set('rootUri', descriptor.rootUri);
    return url;
  };
  const buildBootstrapFromAggregate = (aggregate, descriptor, paneRole) => ({
    schema: 'mnote.editor_bootstrap.v1',
    documentId: aggregate?.identity?.documentId || aggregate?.identity?.document_id || descriptor.documentId,
    workspaceId: aggregate?.identity?.workspaceId || aggregate?.identity?.workspace_id || descriptor.workspaceId || '',
    paneRole,
    sourceKind: descriptor.sourceKind || 'convex_workspace',
    rootUri: descriptor.rootUri || '',
    pageAggregateScriptId: paneRole === 'secondary' ? '__MNOTE_SECONDARY_PAGE_AGGREGATE__' : '__MNOTE_PAGE_AGGREGATE__',
    saveEndpoint: (descriptor.sourceKind || 'convex_workspace') === 'local_folder'
      ? '/api/page-body/write'
      : '/api/documents/save',
    titleEndpoint: '/api/documents/title',
    editorHostKind: 'leptos_tiptap_island',
  });
  const aggregateDocumentId = (aggregate) => {
    return String(aggregate?.identity?.documentId || aggregate?.identity?.document_id || aggregate?.pageId || aggregate?.page_id || '').trim();
  };
  const aggregatePageOptions = (aggregate) => {
    return aggregate?.layout?.pageOptions || aggregate?.layout?.page_options || aggregate?.layoutOptions || aggregate?.layout_options || null;
  };
  const withPageOptions = (aggregate, pageOptions) => {
    if (!aggregate || !pageOptions) return aggregate;
    const next = { ...aggregate };
    next.layout = next.layout && typeof next.layout === 'object' ? { ...next.layout } : {};
    next.layout.pageOptions = { ...pageOptions };
    next.layout_options = { ...pageOptions };
    next.layoutOptions = { ...pageOptions };
    return next;
  };
  const syncPageAggregateScript = (session, aggregate) => {
    if (!session || !aggregate) return;
    const scriptId = session.pageAggregateScriptId || '__MNOTE_PAGE_AGGREGATE__';
    let node = document.getElementById(scriptId);
    if (!node) {
      node = document.createElement('script');
      node.id = scriptId;
      node.type = 'application/json';
      document.body.appendChild(node);
    }
    try {
      let existingAggregate = null;
      try {
        existingAggregate = JSON.parse(node.textContent || 'null');
      } catch (_) {
        existingAggregate = null;
      }
      const localWriteAt = Number(node.getAttribute('data-mnote-page-options-local-write-at') || 0);
      const preserveRecentLocalOptions = localWriteAt > 0
        && Date.now() - localWriteAt < 15000
        && aggregateDocumentId(existingAggregate) === aggregateDocumentId(aggregate);
      const nextAggregate = preserveRecentLocalOptions
        ? withPageOptions(aggregate, aggregatePageOptions(existingAggregate))
        : aggregate;
      node.textContent = JSON.stringify(nextAggregate);
      node.setAttribute('data-mnote-page-aggregate-synced-at', String(Date.now()));
      window.dispatchEvent(new CustomEvent('mnote:page-aggregate-synced', {
        detail: { scriptId, documentId: aggregateDocumentId(nextAggregate) }
      }));
    } catch (error) {
      console.warn('mnote Page Aggregate script 同步失败', error);
    }
  };

  const paneViewRegistry = new Map();
  const currentWebShellWorkspaceId = () => {
    try {
      return currentUrl().searchParams.get('workspaceId') || '';
    } catch (_) {
      return '';
    }
  };

  const currentWebShellDocumentId = () => {
    const fromBody = document.body?.dataset?.documentId || '';
    if (fromBody) return String(fromBody).trim();
    const pageTab = document.querySelector('[data-mnote-main-tab="page"]');
    if (pageTab instanceof HTMLElement) return String(pageTab.getAttribute('data-document-id') || '').trim();
    return '';
  };

  let nextViewId = 1;
  const mindmapHost = createMindmapHostRuntime({
    loadRuntime,
    paneViewRegistry,
    unmountEditorViewBinding: (...args) => unmountEditorViewBinding(...args),
    pushUrlState,
    rootSelector: ROOT_SELECTOR,
    currentDocumentId: () => currentWebShellDocumentId(),
  });

  let resourceTabs = null;
  const syncResourceSessionTabGuards = (session) => {
    if (resourceTabs) resourceTabs.syncResourceSessionTabGuards(session);
  };

  const documentSessions = createDocumentSessionRuntime({
    bridgeProtocol: BRIDGE_PROTOCOL,
    commandEvent: COMMAND_EVENT,
    normalizeEnvelopePayload,
    pageAggregateUrl,
    setStatus,
    syncPageAggregateScript,
    syncResourceSessionTabGuards,
  });
  const documentSessionRegistry = documentSessions.documentSessionRegistry;
  const externalConflictMessage = documentSessions.externalConflictMessage;
  const sessionViews = (...args) => documentSessions.sessionViews(...args);
  const releaseDocumentSession = (...args) => documentSessions.releaseDocumentSession(...args);
  const scheduleDocumentSessionRelease = (...args) => documentSessions.scheduleDocumentSessionRelease(...args);
  const cancelDocumentSessionRelease = (...args) => documentSessions.cancelDocumentSessionRelease(...args);
  const currentEditorText = (...args) => documentSessions.currentEditorText(...args);
  const clearEmbeddedLocalDraft = (...args) => documentSessions.clearEmbeddedLocalDraft(...args);
  const setSessionStatus = (...args) => documentSessions.setSessionStatus(...args);
  const dispatchSessionContentToView = (...args) => documentSessions.dispatchSessionContentToView(...args);
  const broadcastSessionContent = (...args) => documentSessions.broadcastSessionContent(...args);
  const sessionHasRecentExternalSignal = (...args) => documentSessions.sessionHasRecentExternalSignal(...args);
  const sessionHasRecentLocalInput = (...args) => documentSessions.sessionHasRecentLocalInput(...args);
  const clearSessionConflictSurface = (...args) => documentSessions.clearSessionConflictSurface(...args);
  const markSessionExternalConflict = (...args) => documentSessions.markSessionExternalConflict(...args);
  const queueSessionSave = (...args) => documentSessions.queueSessionSave(...args);
  const refreshSessionFromExternalChange = (...args) => documentSessions.refreshSessionFromExternalChange(...args);
  const sessionMatchesDocumentWorkspace = (...args) => documentSessions.sessionMatchesDocumentWorkspace(...args);
  const getOrCreateDocumentSession = (...args) => documentSessions.getOrCreateDocumentSession(...args);

  const updatePaneChrome = (runtimeDescriptor) => {
    const pane = runtimeDescriptor.root.closest('[data-document-pane="true"]');
    if (!(pane instanceof HTMLElement)) return;
    const aggregate = runtimeDescriptor.aggregate || {};
    const bootstrap = runtimeDescriptor.bootstrap || {};
    const title = aggregate?.head?.title || '无标题';
    const documentId = bootstrap.documentId || aggregate?.identity?.documentId || '';
    const workspaceId = bootstrap.workspaceId || aggregate?.identity?.workspaceId || '';
    pane.setAttribute('data-pane-document-id', documentId);
    pane.setAttribute('data-pane-workspace-id', workspaceId);
    pane.setAttribute('data-pane-visible', 'true');
    pane.removeAttribute('data-mnote-side-target');
    pane.hidden = false;
    const shell = pane.querySelector('.document-shell');
    if (shell instanceof HTMLElement) {
      shell.setAttribute('data-document-id', documentId);
      shell.setAttribute('data-workspace-id', workspaceId);
      const options = aggregate?.layout?.pageOptions || {};
      shell.setAttribute('data-page-wide-layout', String(Boolean(options.wideLayout)));
      shell.setAttribute('data-page-small-text', String(Boolean(options.smallText)));
      shell.setAttribute('data-layout-density', String(options.layoutDensity || 'normal'));
      shell.setAttribute('data-page-font', String(options.pageFont || 'default'));
      shell.setAttribute('data-page-show-heading-numbers', String(Boolean(options.showHeadingNumbers)));
      const hideTitleHeader = Boolean(options.hideTitleHeader || options.hide_title_header);
      const header = shell.querySelector('.document-shell-header');
      if (header instanceof HTMLElement) {
        header.hidden = hideTitleHeader;
        header.setAttribute('data-page-title-hidden', String(hideTitleHeader));
      }
    }
    pane.querySelectorAll('[data-page-title-input="true"]').forEach((node) => {
      if (!(node instanceof HTMLTextAreaElement)) return;
      node.value = title;
      node.setAttribute('data-document-id', documentId);
      node.setAttribute('data-workspace-id', workspaceId);
      node.setAttribute('data-title-last-saved', title);
      node.setAttribute('data-title-save-status', 'saved');
      node.style.height = 'auto';
      node.style.height = `${Math.max(48, node.scrollHeight)}px`;
    });
    pane.querySelectorAll('[data-page-title-current="true"]').forEach((node) => {
      if (node instanceof HTMLElement) node.textContent = title;
    });
    const pageTab = document.querySelector(`[data-mnote-main-tab="page"][data-pane-role="${runtimeDescriptor.paneRole}"]`);
    if (pageTab instanceof HTMLElement) {
      pageTab.setAttribute('data-document-id', documentId);
      pageTab.setAttribute('data-workspace-id', workspaceId);
      const pageTabTitle = pageTab.querySelector('.mnote-main-tab-title');
      if (pageTabTitle instanceof HTMLElement) pageTabTitle.textContent = title;
    }
    if (runtimeDescriptor.paneRole === 'primary') {
      document.body.dataset.documentId = documentId;
      document.body.dataset.mnoteShell = 'document';
      delete document.body.dataset.mindmapId;
      document.title = title;
      const topbarCurrent = document.querySelector('.wolai-breadcrumb-current [data-page-title-current]');
      if (topbarCurrent instanceof HTMLElement) topbarCurrent.textContent = title;
      window.dispatchEvent(new CustomEvent('mnote:primary-document-activated', {
        detail: { documentId, workspaceId, title }
      }));
      document.documentElement.removeAttribute('data-mnote-side-target-unsupported');
      document.documentElement.removeAttribute('data-mnote-side-target-asset-id');
      document.querySelectorAll('.tree-row[data-active="true"], .tree-row[data-selected="true"]').forEach((row) => {
        if (row instanceof HTMLElement) {
          row.setAttribute('data-active', 'false');
          row.setAttribute('data-selected', 'false');
        }
      });
      const escapedId = window.CSS && typeof window.CSS.escape === 'function' ? window.CSS.escape(documentId) : String(documentId).replace(/["\\]/g, '\\$&');
      document.querySelectorAll(`.tree-row[data-node-id="${escapedId}"], .tree-row[data-document-id="${escapedId}"], .tree-row[data-doc-id="${escapedId}"]`).forEach((row) => {
        if (!(row instanceof HTMLElement)) return;
        if (row.getAttribute('data-shell-mode') === 'filetree') {
          row.setAttribute('data-selected', String(row.getAttribute('data-row-id') === `doc:${documentId}`));
        } else {
          row.setAttribute('data-active', 'true');
        }
      });
    }
    runtimeDescriptor.root.removeAttribute('data-mnote-object-editor');
    runtimeDescriptor.root.removeAttribute('data-mnote-object-identity');
    runtimeDescriptor.root.removeAttribute('data-mnote-mindmap-id');
    runtimeDescriptor.root.removeAttribute('data-mnote-side-target-unsupported');
    runtimeDescriptor.root.removeAttribute('data-mnote-side-target-asset-id');
  };

  const fetchPageAggregateForPane = async (descriptor) => {
    const response = await fetch(pageAggregateUrlFromDescriptor(descriptor).toString(), {
      cache: 'no-store',
      headers: { accept: 'application/json' },
    });
    if (!response.ok) throw new Error(`page_aggregate_failed_${response.status}`);
    const payload = await response.json();
    if (!payload?.result) throw new Error('page_aggregate_missing_result');
    return payload.result;
  };

  const replacePaneDocument = async (paneRole, descriptor, options = {}) => {
    mindmapHost.unmountMindmapPane(paneRole);
    const root = document.querySelector(`${ROOT_SELECTOR}[data-pane-role="${paneRole}"]`);
    const observability = document.querySelector(`[data-editor-host-observability][data-pane-role="${paneRole}"]`);
    if (!(root instanceof HTMLElement)) throw new Error(`pane_root_missing_${paneRole}`);
    const runtimePromise = loadRuntime();
    const aggregatePromise = options.aggregate
      ? Promise.resolve(options.aggregate)
      : fetchPageAggregateForPane(descriptor);
    const previousView = paneViewRegistry.get(paneRole);
    const [runtime, aggregate] = await Promise.all([runtimePromise, aggregatePromise]);
    if (previousView) {
      unmountEditorViewBinding(previousView);
      paneViewRegistry.delete(paneRole);
    }
    const bootstrap = buildBootstrapFromAggregate(aggregate, descriptor, paneRole);
    syncPageAggregateScript({ pageAggregateScriptId: bootstrap.pageAggregateScriptId }, aggregate);
    const runtimeDescriptor = { paneRole, root, observability, aggregate, bootstrap };
    updatePaneChrome(runtimeDescriptor);
    if (paneRole === 'secondary' && workspace instanceof HTMLElement) {
      workspace.setAttribute('data-has-secondary-pane', 'true');
      setSecondaryEditorHostVisible(true);
      const resizerNode = document.querySelector('[data-document-pane-resizer="true"]');
      if (resizerNode instanceof HTMLElement) resizerNode.hidden = false;
      applyStoredSecondaryWidth();
    }
    const session = getOrCreateDocumentSession(runtimeDescriptor);
    const view = createEditorViewBinding(session, runtime, runtimeDescriptor);
    const mountOptions = {
      documentId: session.documentId,
      workspaceId: session.workspaceId,
      title: session.title,
      content: session.currentTiptapDocument,
      revision: session.revision,
      conflictDetectionKey: session.conflictDetectionKey,
      readOnly: session.readOnly,
      editable: !session.readOnly,
      pageOptions: runtimeDescriptor.aggregate.layout?.pageOptions || {},
    };
    setStatus(runtimeDescriptor, 'loading-assets');
    clearEmbeddedLocalDraft(runtimeDescriptor);
    try {
      const mountId = runtime.mount(runtimeDescriptor.root, mountOptions);
      view.mountId = mountId;
      runtimeDescriptor.root.setAttribute('data-runtime-mount-id', String(mountId));
      runtimeDescriptor.root.setAttribute('data-editor-host-kind', 'leptos_tiptap_island');
      setStatus(runtimeDescriptor, 'mounting-editor');
      enhanceEditorAttachmentLinksSoon();
      paneViewRegistry.set(paneRole, view);
      return view;
    } catch (error) {
      unmountEditorViewBinding(view);
      throw error;
    }
  };

  const descriptorFromCurrentUrl = (paneRole, documentId, explicit = {}) => {
    const url = currentUrl();
    if (paneRole === 'secondary') {
      return {
        documentId,
        workspaceId: explicit.workspaceId || url.searchParams.get('workspaceId') || '',
        sourceKind: explicit.sourceKind || url.searchParams.get('secondarySourceKind') || url.searchParams.get('sourceKind') || 'convex_workspace',
        rootUri: explicit.rootUri || url.searchParams.get('secondaryRootUri') || url.searchParams.get('rootUri') || '',
      };
    }
    return {
      documentId,
      workspaceId: explicit.workspaceId || url.searchParams.get('workspaceId') || '',
      sourceKind: explicit.sourceKind || url.searchParams.get('sourceKind') || 'convex_workspace',
      rootUri: explicit.rootUri || url.searchParams.get('rootUri') || '',
    };
  };

  const updatePrimaryUrl = (descriptor, urlFromCaller) => {
    const url = urlFromCaller instanceof URL
      ? urlFromCaller
      : new URL(`/documents/${encodeURIComponent(descriptor.documentId)}`, window.location.origin);
    if (!url.searchParams.get('workspaceId') && descriptor.workspaceId) url.searchParams.set('workspaceId', descriptor.workspaceId);
    if (descriptor.sourceKind && descriptor.sourceKind !== 'convex_workspace') url.searchParams.set('sourceKind', descriptor.sourceKind);
    if (descriptor.rootUri) url.searchParams.set('rootUri', descriptor.rootUri);
    pushUrlState(url);
  };

  const setSecondaryEditorHostVisible = (visible) => {
    const host = document.querySelector('[data-testid="mnote-secondary-editor-tab-host"]');
    if (host instanceof HTMLElement) host.hidden = !visible;
  };

  const closeSecondaryPane = (url) => {
    const view = paneViewRegistry.get('secondary');
    if (view) {
      unmountEditorViewBinding(view);
      paneViewRegistry.delete('secondary');
    }
    const pane = document.querySelector('[data-document-pane="true"][data-pane-role="secondary"]');
    if (pane instanceof HTMLElement) {
      pane.hidden = true;
      pane.setAttribute('data-pane-visible', 'false');
      pane.removeAttribute('data-mnote-side-target');
      pane.removeAttribute('data-pane-document-id');
      pane.removeAttribute('data-pane-workspace-id');
    }
    const root = document.querySelector(`${ROOT_SELECTOR}[data-pane-role="secondary"]`);
    if (root instanceof HTMLElement) {
      root.replaceChildren();
      root.removeAttribute('data-mnote-side-target-unsupported');
      root.removeAttribute('data-mnote-side-target-asset-id');
    }
    if (resourceTabs) resourceTabs.closeResourceTabsForPane('secondary');
    if (workspace instanceof HTMLElement) {
      workspace.setAttribute('data-has-secondary-pane', 'false');
      workspace.style.removeProperty('grid-template-columns');
    }
    setSecondaryEditorHostVisible(false);
    const resizerNode = document.querySelector('[data-document-pane-resizer="true"]');
    if (resizerNode instanceof HTMLElement) resizerNode.hidden = true;
    document.documentElement.removeAttribute('data-mnote-side-target-unsupported');
    document.documentElement.removeAttribute('data-mnote-side-target-asset-id');
    replaceUrlState(url);
  };

  const handleSessionChange = (session, view, event) => {
    const payload = normalizeEnvelopePayload(event);
    if (!payload) return;
    const pendingExternalChange = session.sourceKind === 'local_folder' && session.externalChangePending;
    const recentExternalChange = sessionHasRecentExternalSignal(session);
    const recentLocalInput = sessionHasRecentLocalInput(session);
    const tiptapDocument = hydrateMindmapAttrsFromDom(toTiptapDocument(
      payload?.tiptapDocument || payload?.editorDocument || payload?.content,
      currentEditorText(view),
    ), view.runtimeDescriptor.root);
    const serialized = JSON.stringify(tiptapDocument);
    if (view.suppressedSerialized && view.suppressedSerialized === serialized) {
      view.suppressedSerialized = null;
      view.lastKnownSerialized = serialized;
      return;
    }
    const previousSerialized = session.currentSerialized;
    session.currentTiptapDocument = tiptapDocument;
    session.currentSerialized = serialized;
    view.lastKnownSerialized = serialized;
    if (typeof payload?.title === 'string' && payload.title.trim()) {
      session.title = payload.title.trim();
    }
    if (payload?.meta && typeof payload.meta === 'object') {
      if (Number.isInteger(payload.meta.revision)) session.revision = payload.meta.revision;
      if (typeof payload.meta.conflictDetectionKey === 'string' && payload.meta.conflictDetectionKey.trim()) {
        const nextMetaConflictKey = payload.meta.conflictDetectionKey.trim();
        if (conflictDetectionKeyBelongsToSession(session, nextMetaConflictKey)) {
          session.conflictDetectionKey = nextMetaConflictKey;
        }
      }
      if (typeof payload.meta.readOnly === 'boolean') {
        session.readOnly = payload.meta.readOnly;
      }
    }
    session.dirty = session.currentSerialized !== session.lastPersistedSerialized;
    syncResourceSessionTabGuards(session);
    if (serialized !== previousSerialized) {
      broadcastSessionContent(session, view);
    }
    if ((pendingExternalChange || recentExternalChange) && (session.dirty || recentLocalInput)) {
      markSessionExternalConflict(session, externalConflictMessage);
      return;
    }
    if (session.hasExternalConflict) {
      setSessionStatus(session, 'external-change-conflict', externalConflictMessage);
      return;
    }
    if (session.dirty) {
      queueSessionSave(session);
    } else {
      setSessionStatus(session, 'saved');
    }
  };

  const unmountEditorViewBinding = (view, options = {}) => {
    if (!view || view.disposed) return;
    view.disposed = true;
    const scheduleRelease = options.scheduleRelease !== false;
    const root = view.runtimeDescriptor.root;
    if (typeof view.disconnectObserver === 'function') {
      view.disconnectObserver();
      view.disconnectObserver = null;
    }
    if (view.onReady) root.removeEventListener(READY_EVENT, view.onReady);
    if (view.onError) root.removeEventListener(ERROR_EVENT, view.onError);
    if (view.onChange) root.removeEventListener(CHANGE_EVENT, view.onChange);
    if (view.onSave) root.removeEventListener(SAVE_EVENT, view.onSave);
    if (view.onState) root.removeEventListener(STATE_EVENT, view.onState);
    if (view.onKeydown) root.removeEventListener('keydown', view.onKeydown, true);
    if (view.onInput) root.removeEventListener('input', view.onInput);
    if (view.disconnectSlashObserver) {
      view.disconnectSlashObserver();
      view.disconnectSlashObserver = null;
    }
    clearSessionConflictSurface(view.session);
    if (view.mountId != null && typeof view.runtime?.unmount === 'function') {
      try {
        view.runtime.unmount(view.mountId);
      } catch (error) {
        console.warn('mnote editor view unmount failed', error);
      }
    }
    view.mountId = null;
    root.removeAttribute('data-runtime-mount-id');
    if (view.session.views.get(view.id) === view) {
      view.session.views.delete(view.id);
    }
    if (scheduleRelease) {
      scheduleDocumentSessionRelease(view.session);
    }
  };

  const observeEditorViewBinding = (view) => {
    if (typeof MutationObserver !== 'function' || !(document.body instanceof HTMLElement)) {
      return;
    }
    const observer = new MutationObserver(() => {
      if (!view.disposed && !view.runtimeDescriptor.root.isConnected) {
        unmountEditorViewBinding(view);
      }
    });
    observer.observe(document.body, { childList: true, subtree: true });
    view.disconnectObserver = () => observer.disconnect();
  };

  installGlobalSlashMenuPositioning();

  const createEditorViewBinding = (session, runtime, runtimeDescriptor) => {
    cancelDocumentSessionRelease(session);
    const view = {
      id: nextViewId++,
      mountId: null,
      ready: false,
      disposed: false,
      suppressedSerialized: null,
      lastKnownSerialized: session.currentSerialized,
      runtime,
      runtimeDescriptor,
      session,
      disconnectObserver: null,
      onReady: null,
      onError: null,
      onChange: null,
      onSave: null,
      onState: null,
      onKeydown: null,
      onInput: null,
      disconnectSlashObserver: null,
    };

    view.onReady = () => {
      view.ready = true;
      if (view.lastKnownSerialized !== session.currentSerialized) {
        dispatchSessionContentToView(session, view, 'mnote-web-document-session-ready-sync');
      }
      if (session.status === 'dirty' || session.status === 'saving' || session.status === 'saved' || session.status === 'error' || session.status === 'external-change-conflict' || session.status === 'synced-external-change') {
        setStatus(runtimeDescriptor, session.status, session.error);
      } else {
        setStatus(runtimeDescriptor, 'ready');
      }
      enhanceEditorAttachmentLinksSoon();
    };
    view.onError = (event) => {
      const payload = normalizeEnvelopePayload(event);
      setStatus(runtimeDescriptor, 'error', payload?.message || 'runtime_error');
    };
    view.onChange = (event) => {
      scheduleSlashMenuPosition(runtimeDescriptor.root);
      handleSessionChange(session, view, event);
      enhanceEditorAttachmentLinksSoon();
    };
    view.onSave = (event) => {
      handleSessionChange(session, view, event);
      enhanceEditorAttachmentLinksSoon();
    };
    view.onState = () => {
      scheduleSlashMenuPosition(runtimeDescriptor.root);
      enhanceEditorAttachmentLinksSoon();
    };
    view.onKeydown = (event) => {
      if (event.key === '/') scheduleSlashMenuPosition(runtimeDescriptor.root);
    };
    view.onInput = (event) => {
      const target = event.target;
      if (!(target instanceof Element) || !target.closest('.ProseMirror')) return;
      session.lastUserInputAt = Date.now();
      scheduleSlashMenuPosition(runtimeDescriptor.root);
    };

    runtimeDescriptor.root.addEventListener(READY_EVENT, view.onReady);
    runtimeDescriptor.root.addEventListener(ERROR_EVENT, view.onError);
    runtimeDescriptor.root.addEventListener(CHANGE_EVENT, view.onChange);
    runtimeDescriptor.root.addEventListener(SAVE_EVENT, view.onSave);
    runtimeDescriptor.root.addEventListener(STATE_EVENT, view.onState);
    runtimeDescriptor.root.addEventListener('keydown', view.onKeydown, true);
    runtimeDescriptor.root.addEventListener('input', view.onInput);
    session.views.set(view.id, view);
    observeSlashMenuPosition(view);
    observeEditorViewBinding(view);
    return view;
  };

  resourceTabs = createResourceTabRuntime({
    loadRuntime,
    createEditorViewBinding,
    unmountEditorViewBinding,
    setStatus,
    documentSessionRegistry,
    sessionHasRecentLocalInput,
    currentUrl,
    replaceUrlState,
    applyStoredSecondaryWidth,
    workspace,
    setSecondaryEditorHostVisible,
    markIntendedSlashRoot,
    toTiptapDocument,
    openMindmapResourceTab: (...args) => mindmapHost.openMindmapResourceTab(...args),
    unmountMindmapPane: (...args) => mindmapHost.unmountMindmapPane(...args),
    paneViewRegistry,
    rootSelector: ROOT_SELECTOR,
    currentDocumentId: () => currentWebShellDocumentId(),
    currentWorkspaceId: () => currentWebShellWorkspaceId(),
    secondaryQueryParamNames,
  });
  const normalizePaneRole = (...args) => resourceTabs.normalizePaneRole(...args);
  const resolveResourceOpen = (...args) => resourceTabs.resolveResourceOpen(...args);
  const openResourceInActiveTab = (...args) => resourceTabs.openResourceInActiveTab(...args);
  const activateMainEditorTab = (...args) => resourceTabs.activateMainEditorTab(...args);
  const bindMainEditorPageTab = (...args) => resourceTabs.bindMainEditorPageTab(...args);
  const buildOpenEditorsSnapshot = (...args) => resourceTabs.buildOpenEditorsSnapshot(...args);

  const mountPane = async (runtimeDescriptor) => {
    const runtime = await loadRuntime();
    const session = getOrCreateDocumentSession(runtimeDescriptor);
    const view = createEditorViewBinding(session, runtime, runtimeDescriptor);

    const mountOptions = {
      documentId: session.documentId,
      workspaceId: session.workspaceId,
      title: session.title,
      content: session.currentTiptapDocument,
      revision: session.revision,
      conflictDetectionKey: session.conflictDetectionKey,
      readOnly: session.readOnly,
      editable: !session.readOnly,
      pageOptions: runtimeDescriptor.aggregate.layout?.pageOptions || {},
    };

    setStatus(runtimeDescriptor, 'loading-assets');
    clearEmbeddedLocalDraft(runtimeDescriptor);
    try {
      const mountId = runtime.mount(runtimeDescriptor.root, mountOptions);
      view.mountId = mountId;
      runtimeDescriptor.root.setAttribute('data-runtime-mount-id', String(mountId));
      runtimeDescriptor.root.setAttribute('data-editor-host-kind', 'leptos_tiptap_island');
      setStatus(runtimeDescriptor, 'mounting-editor');
      enhanceEditorAttachmentLinksSoon();
      paneViewRegistry.set(runtimeDescriptor.paneRole, view);
    } catch (error) {
      unmountEditorViewBinding(view);
      throw error;
    }
  };

  window.__mnoteDocumentPaneRuntime = {
    openPrimaryDocument: async ({ documentId, workspaceId, sourceKind, rootUri, url } = {}) => {
      const id = typeof documentId === 'string' ? documentId.trim() : '';
      if (!id) return false;
      const descriptor = descriptorFromCurrentUrl('primary', id, { workspaceId, sourceKind, rootUri });
      await replacePaneDocument('primary', descriptor);
      activateMainEditorTab('', 'primary');
      updatePrimaryUrl(descriptor, url instanceof URL ? url : null);
      return true;
    },
    openPrimaryMindmap: async ({ documentId, mindmapId, workspaceId, url } = {}) => {
      const docId = typeof documentId === 'string' ? documentId.trim() : '';
      const mapId = typeof mindmapId === 'string' ? mindmapId.trim() : '';
      if (!docId || !mapId) return false;
      await mindmapHost.replacePrimaryPaneMindmap({
        documentId: docId,
        mindmapId: mapId,
        workspaceId: typeof workspaceId === 'string' ? workspaceId.trim() : '',
        url,
      });
      return true;
    },
    openSecondaryDocument: async ({ documentId, workspaceId, sourceKind, rootUri, url } = {}) => {
      const id = typeof documentId === 'string' ? documentId.trim() : '';
      if (!id) return false;
      const descriptor = descriptorFromCurrentUrl('secondary', id, { workspaceId, sourceKind, rootUri });
      await replacePaneDocument('secondary', descriptor);
      activateMainEditorTab('', 'secondary');
      if (url instanceof URL) replaceUrlState(url);
      return true;
    },
    resolveResourceOpen: (input) => resolveResourceOpen(input),
    openResourceInActiveTab: openResourceInActiveTab,
    activatePageTab: ({ paneRole } = {}) => {
      const role = normalizePaneRole(paneRole || 'primary');
      bindMainEditorPageTab(role);
      activateMainEditorTab('', role);
      return true;
    },
    openResourceAsSideTarget: async (input = {}) => {
      return openResourceInActiveTab({ ...input, paneRole: 'secondary', openTarget: 'active-tab' });
    },
    closeSecondaryDocument: ({ url } = {}) => {
      closeSecondaryPane(url instanceof URL ? url : currentUrl());
      return true;
    },
    getOpenEditorsSnapshot: () => buildOpenEditorsSnapshot(),
    refreshDocument: async ({ documentId, workspaceId, source } = {}) => {
      const targets = Array.from(documentSessionRegistry.values()).filter((session) => (
        sessionMatchesDocumentWorkspace(session, documentId, workspaceId)
      ));
      await Promise.all(targets.map((session) => refreshSessionFromExternalChange(
        session,
        source || 'mnote-web-programmatic-refresh',
      )));
      return targets.length;
    },
    refreshPrimaryDocument: async ({ documentId, workspaceId, source } = {}) => {
      const primary = paneViewRegistry.get('primary');
      if (!primary?.session) return 0;
      if (!sessionMatchesDocumentWorkspace(primary.session, documentId, workspaceId)) return 0;
      await refreshSessionFromExternalChange(primary.session, source || 'mnote-web-programmatic-refresh');
      return 1;
    },
  };

  bindMainEditorPageTab('primary');
  bindMainEditorPageTab('secondary');

  const shouldPrewarmPrimaryRuntime = paneRuntimes.length === 0
    && document.querySelector('.mnote-workspace-empty-editor-host')
    && document.querySelector(`${ROOT_SELECTOR}[data-pane-role="primary"]`);
  if (shouldPrewarmPrimaryRuntime) {
    const prewarmRuntime = () => {
      loadRuntime().catch((error) => {
        console.warn('mnote editor runtime prewarm failed', error);
      });
    };
    if (typeof window.requestIdleCallback === 'function') {
      window.requestIdleCallback(prewarmRuntime, { timeout: 2000 });
    } else {
      window.setTimeout(prewarmRuntime, 800);
    }
  }

  window.addEventListener('pagehide', () => {
    Array.from(documentSessionRegistry.values()).forEach((session) => {
      sessionViews(session).forEach((view) => {
        unmountEditorViewBinding(view, { scheduleRelease: false });
      });
      releaseDocumentSession(session);
    });
  }, { once: true });

  if (paneRuntimes.length) {
    Promise.all(paneRuntimes.map((paneRuntime) => mountPane(paneRuntime))).catch((error) => {
      console.error('mnote multi-pane editor mount failed', error);
    });
  }
})();
