import { act } from "react";
import { createRoot, type Root } from "react-dom/client";
import { renderToString } from "react-dom/server";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import type { PageTreeProjectionItem } from "@/lib/tree-projection";
import type { KernelFileTreeProjectionItem } from "@/lib/kernel-file-tree";
import type { SidebarTreeNode } from "@/lib/kernel-sidebar";
import {
  TreeShellIframeHost,
  type TreeShellPickerItem,
  buildTreeShellInlineKernelFileTreeItems,
  buildTreeShellInlinePageItems,
  buildTreeShellIframeSrc,
  buildTreeShellInlinePickerItems,
  injectTreeShellInlineOverrides,
} from "./tree-shell-iframe-host";

(globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;

function readTreeShellState(srcDoc: string | null | undefined) {
  const match = (srcDoc ?? "").match(
    /<script id="tree-shell-state" type="application\/json">([^<]*)<\/script>/,
  );
  if (!match) {
    throw new Error("missing tree shell state");
  }
  return JSON.parse(match[1] ?? "{}") as {
    items?: unknown[];
    rendererInput?: {
      projectionItemIds?: string[];
      expandedIds?: string[];
      activePickerItem?: string | null;
      excludedPickerIds?: string[];
    };
  };
}

describe("tree-shell-iframe-host", () => {
  let container: HTMLDivElement;
  let root: Root;

  beforeEach(() => {
    container = document.createElement("div");
    document.body.appendChild(container);
    root = createRoot(container);
  });

  afterEach(() => {
    act(() => {
      root.unmount();
    });
    vi.restoreAllMocks();
    container.remove();
  });

  it("应构造同源 tree shell iframe 地址，并透传 picker 所需 query", () => {
    const src = buildTreeShellIframeSrc({
      mode: "picker",
      workspaceId: "ws_picker",
      activeDocumentId: "doc_active",
      focusedDocumentId: "doc_focus",
      activePickerItemKey: "__root__",
      allowRootPick: true,
      excludeIds: ["doc_hidden", "doc_other"],
      channel: "tree-picker-surface",
      host: "tree-picker-surface",
    });
    const url = new URL(src, "http://127.0.0.1:3000");

    expect(url.pathname).toBe("/api/tree/shell");
    expect(url.searchParams.get("workspaceId")).toBe("ws_picker");
    expect(url.searchParams.get("mode")).toBe("picker");
    expect(url.searchParams.get("activeDocumentId")).toBe("doc_active");
    expect(url.searchParams.get("focusedDocumentId")).toBe("doc_focus");
    expect(url.searchParams.get("activePickerItemKey")).toBe("__root__");
    expect(url.searchParams.get("allowRootPick")).toBe("1");
    expect(url.searchParams.get("excludeIds")).toBe("doc_hidden,doc_other");
    expect(url.searchParams.get("channel")).toBe("tree-picker-surface");
    expect(url.searchParams.get("host")).toBe("tree-picker-surface");
  });

  it("未提供 inline projection 时也应使用本地 srcDoc，避免默认回源 3104 显示 fetch failed", async () => {
    const fetchMock = vi.spyOn(globalThis, "fetch").mockRejectedValue(new Error("fetch failed"));

    await act(async () => {
      root.render(
        <TreeShellIframeHost
          mode="page"
          surfaceTestId="sidebar-page-tree-shell"
          workspaceId="ws_1"
          activeDocumentId="doc_1"
        />,
      );
    });

    await act(async () => {
      await Promise.resolve();
    });

    const iframe = container.querySelector('[data-testid="sidebar-page-tree-shell-rust-iframe"]');
    expect(fetchMock).not.toHaveBeenCalled();
    expect(iframe?.getAttribute("src")).toBeNull();
    expect(iframe?.getAttribute("data-tree-shell-inline")).toBe("1");
    expect(iframe?.getAttribute("srcdoc")).not.toContain("<script>window.__MNOTE_TREE_SHELL_OVERRIDE__ =");
    expect(iframe?.getAttribute("srcdoc")).toContain('data-rust-page-renderer="initial_v1"');
    expect(iframe?.getAttribute("srcdoc")).not.toContain("fetch failed");
  });

  it("应能把 picker 搜索结果转换为 inline shell items 并注入到 HTML", () => {
    const pickerItems = buildTreeShellInlinePickerItems([
      { kind: "doc", id: "doc_target", title: "目标页面", depth: 0 },
      { kind: "doc", id: "doc_recent", title: "最近</script>打开", depth: 0 },
    ]);

    expect(pickerItems).toEqual([
      expect.objectContaining({
        nodeId: "doc_target",
        parentNodeId: null,
        title: "目标页面",
        depth: 0,
        childCount: 0,
      }),
      expect.objectContaining({
        nodeId: "doc_recent",
        parentNodeId: null,
        title: "最近</script>打开",
        depth: 0,
        childCount: 0,
      }),
    ]);

    const html = injectTreeShellInlineOverrides(
      '<!doctype html><html><body><script id="tree-shell-state" type="application/json">{}</script></body></html>',
      { items: pickerItems },
    );

    expect(html).toContain("__MNOTE_TREE_SHELL_OVERRIDE__");
    expect(html).toContain("\\u003c/script\\u003e");
    expect(html.indexOf("__MNOTE_TREE_SHELL_OVERRIDE__")).toBeGreaterThan(-1);
    expect(html.indexOf("__MNOTE_TREE_SHELL_OVERRIDE__")).toBeLessThan(
      html.indexOf('id="tree-shell-state"'),
    );
  });

  it("page tree 提供 inline items 时应直接生成 srcDoc，不再 fetch /api/tree/shell", async () => {
    const fetchMock = vi.spyOn(globalThis, "fetch").mockResolvedValue(
      new Response(
        '<!doctype html><html><body><script id="tree-shell-state" type="application/json">{}</script></body></html>',
        { status: 200, headers: { "content-type": "text/html" } },
      ),
    );
    const pageItems: PageTreeProjectionItem[] = [
      {
        rowId: "page:doc_parent",
        nodeId: "doc_parent",
        parentNodeId: null,
        nodeType: "page",
        projectionKind: "page_tree",
        depth: 0,
        position: 0,
        title: "父页面",
        childCount: 1,
        expandable: true,
        expandedByDefault: false,
        capabilities: ["expand", "open"],
        resourceMeta: {
          resourceKind: "document",
          documentId: "doc_parent",
          workspaceId: "ws_1",
          iconHint: "page",
        },
        iconHint: "page",
        node: {
          id: "doc_parent",
          title: "父页面",
          workspace_id: "ws_1",
          parent_id: null,
          sort_order: 0,
          is_archived: false,
          is_deleted: false,
          is_published: false,
          is_starred: false,
          access_scope: "private",
          created_at: "2026-04-24T00:00:00.000Z",
          updated_at: "2026-04-24T00:00:00.000Z",
          children: [],
          kernel: {
            nodeType: "page",
            depth: 0,
            position: 0,
            childCount: 1,
            expandedByDefault: false,
          },
        } as unknown as SidebarTreeNode,
      },
      {
        rowId: "page:doc_child",
        nodeId: "doc_child",
        parentNodeId: "doc_parent",
        nodeType: "page",
        projectionKind: "page_tree",
        depth: 1,
        position: 0,
        title: "子页面",
        childCount: 0,
        expandable: false,
        expandedByDefault: false,
        capabilities: ["open"],
        resourceMeta: {
          resourceKind: "document",
          documentId: "doc_child",
          workspaceId: "ws_1",
          iconHint: "page",
        },
        iconHint: "page",
        node: {
          id: "doc_child",
          title: "子页面",
          workspace_id: "ws_1",
          parent_id: "doc_parent",
          sort_order: 0,
          is_archived: false,
          is_deleted: false,
          is_published: false,
          is_starred: false,
          access_scope: "private",
          created_at: "2026-04-24T00:00:00.000Z",
          updated_at: "2026-04-24T00:00:00.000Z",
          children: [],
          kernel: {
            nodeType: "page",
            depth: 1,
            position: 0,
            childCount: 0,
            expandedByDefault: false,
          },
        } as unknown as SidebarTreeNode,
      },
    ];

    expect(buildTreeShellInlinePageItems(pageItems, new Set(["doc_parent"]))).toEqual([
      expect.objectContaining({
        nodeId: "doc_parent",
        parentNodeId: null,
        title: "父页面",
        childCount: 1,
        expandedByDefault: true,
      }),
      expect.objectContaining({
        nodeId: "doc_child",
        parentNodeId: "doc_parent",
        title: "子页面",
        childCount: 0,
        expandedByDefault: false,
      }),
    ]);

    await act(async () => {
      root.render(
        <TreeShellIframeHost
          mode="page"
          surfaceTestId="sidebar-page-tree-shell"
          workspaceId="ws_1"
          activeDocumentId="doc_parent"
          pageTreeItems={pageItems}
        />,
      );
    });

    await act(async () => {
      await Promise.resolve();
    });

    const iframe = container.querySelector('[data-testid="sidebar-page-tree-shell-rust-iframe"]');
    expect(fetchMock).not.toHaveBeenCalled();
    expect(iframe?.getAttribute("data-tree-shell-inline")).toBe("1");
    expect(iframe?.getAttribute("srcdoc")).not.toContain("<script>window.__MNOTE_TREE_SHELL_OVERRIDE__ =");
    expect(iframe?.getAttribute("srcdoc")).toContain("父页面");
    expect(iframe?.getAttribute("srcdoc")).toContain("tree-action-menu");
    expect(iframe?.getAttribute("srcdoc")).toContain("tree-node-toggle");
    expect(iframe?.getAttribute("srcdoc")).toContain('data-rust-action="toggle"');
    expect(iframe?.getAttribute("srcdoc")).toContain("tree-action-create");
    expect(iframe?.getAttribute("srcdoc")).toContain("tree-action-rename");
    expect(iframe?.getAttribute("srcdoc")).toContain("/api/tree/commands");
    expect(iframe?.getAttribute("srcdoc")).toContain("patchPageTreeActiveDom();");
    expect(iframe?.getAttribute("srcdoc")).toContain("patchPageTreeExpansionDom(nodeId)");
    expect(iframe?.getAttribute("srcdoc")).toContain('"contractName":"rust_page_focus_keyboard_reducer_v1"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"contractName":"rust_tree_shell_runtime_artifact_v1"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"family":"rust_family"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"executionStrategy":"browser_bridge"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"browserBridge":"iframe_srcdoc"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"wasmModuleUrl":"/api/tree-shell-runtime/mnote-tree-shell-runtime_bg.wasm"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"jsGlueUrl":"/api/tree-shell-runtime/mnote-tree-shell-runtime.js"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"inputFields":["rendererInput","projectionItems","expandedIds","selectedRowIds","activePickerItem","focusedId"]');
    expect(iframe?.getAttribute("srcdoc")).toContain('"outputChannels":["domPatch","intentEvent","commandDispatchEvent"]');
    expect(iframe?.getAttribute("srcdoc")).toContain('"runtimeApi":{"requestContract":"TreeShellRuntimeRequest","resultContract":"TreeShellRuntimeResult"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"reduceEndpoint":"/api/tree/runtime/reduce"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"domPatchKinds":["pageState","fileTreeState","pickerState"]');
    expect(iframe?.getAttribute("srcdoc")).toContain('"hostEventKinds":["pageOpen","pageContextMenu","fileTreeOpen","fileTreeContextMenu","fileTreeInternalDrop","fileTreeExternalDrop","fileTreeKeyboardCommand","pickerPickRoot","pickerPickDocument"]');
    expect(iframe?.getAttribute("srcdoc")).toContain('"commandEventKinds":["createNode","renameNode","moveSubtree","copyResource","moveResource","uploadResource"]');
    expect(iframe?.getAttribute("srcdoc")).toContain('"eventKinds":["focus","keyboard","expandCollapse","selection","contextMenu","dragDrop","pick"]');
    expect(iframe?.getAttribute("srcdoc")).toContain("applyPageKeyboardAction");
    expect(iframe?.getAttribute("srcdoc")).toContain("loadTreeShellWasmRuntime");
    expect(iframe?.getAttribute("srcdoc")).toContain("reduceTreeShellRuntimeViaWasm");
    expect(iframe?.getAttribute("srcdoc")).toContain("reducePageActionWithRuntime");
    expect(iframe?.getAttribute("srcdoc")).toContain("reduceTreeShellRuntimeWithArtifact");
    expect(iframe?.getAttribute("srcdoc")).toContain('mode: "page"');
    expect(iframe?.getAttribute("srcdoc")).toContain("buildPageRuntimeEnvironment");
    expect(iframe?.getAttribute("srcdoc")).toContain("rows: normalizedItems.map((entry) => ({nodeId: entry.nodeId,parentNodeId: normalizeText(entry.parentNodeId) || null,position: normalizeNumber(entry.position, 0),}))");
    expect(iframe?.getAttribute("srcdoc")).toContain("moveNext");
    expect(iframe?.getAttribute("srcdoc")).toContain("openFocused");
    expect(iframe?.getAttribute("srcdoc")).toContain("contextMenuFocused");
    expect(iframe?.getAttribute("srcdoc")).toContain("updateDropFeedback");
    expect(iframe?.getAttribute("srcdoc")).toContain("updateDropFeedbackForTarget");
    expect(iframe?.getAttribute("srcdoc")).toContain("dispatchMove");
    expect(iframe?.getAttribute("srcdoc")).toContain("dispatchMoveToTarget");
    expect(iframe?.getAttribute("srcdoc")).toContain("commandEvent.sortOrder");
    expect(iframe?.getAttribute("srcdoc")).toContain("runtimeRequired: true");
    expect(iframe?.getAttribute("srcdoc")).not.toContain("canAcceptPageDrop");
    expect(iframe?.getAttribute("srcdoc")).toContain("dispatchCreate");
    expect(iframe?.getAttribute("srcdoc")).toContain("dispatchRename");
    expect(iframe?.getAttribute("srcdoc")).toContain("pageOpen");
    expect(iframe?.getAttribute("srcdoc")).toContain("replayPageRuntimeHostEvents");
    expect(iframe?.getAttribute("srcdoc")).toContain("replayPageRuntimeCommandEvents");
    expect(iframe?.getAttribute("srcdoc")).toContain("commandEvents");
    expect(iframe?.getAttribute("srcdoc")).toContain('event.kind === "createNode"');
    expect(iframe?.getAttribute("srcdoc")).toContain('event.kind === "renameNode"');
    expect(iframe?.getAttribute("srcdoc")).toContain("dropFeedback");
    expect(iframe?.getAttribute("srcdoc")).toContain("pagePatch?.dropFeedback");
    expect(iframe?.getAttribute("srcdoc")).toContain("stateSnapshot?.dropFeedback");
    expect(iframe?.getAttribute("srcdoc")).toContain("data-drop-feedback");
    expect(iframe?.getAttribute("srcdoc")).toContain('event.kind === "moveSubtree"');
    expect(iframe?.getAttribute("srcdoc")).toContain('postToHost("tree.subtree.moved"');
    expect(iframe?.getAttribute("srcdoc")).toContain('if (mode === "page" && usedRustInitialRenderer) {patchPageTreeActiveDom();syncPageDropFeedbackDom();if (focusedNodeId) focusRowElement(focusedNodeId);return;}');
    expect(readTreeShellState(iframe?.getAttribute("srcdoc")).items).toEqual([
      expect.objectContaining({ nodeId: "doc_parent" }),
      expect.objectContaining({ nodeId: "doc_child", parentNodeId: "doc_parent" }),
    ]);
    expect(iframe?.getAttribute("srcdoc")).not.toContain("\n");
  });

  it("SSR 时 inline tree shell 应先输出稳定 loading srcDoc，避免大模板属性水合不一致", () => {
    const pageItems: PageTreeProjectionItem[] = [
      {
        rowId: "page:doc_parent",
        nodeId: "doc_parent",
        parentNodeId: null,
        nodeType: "page",
        projectionKind: "page_tree",
        depth: 0,
        position: 0,
        title: "父页面",
        childCount: 0,
        expandable: false,
        expandedByDefault: false,
        capabilities: ["open"],
        resourceMeta: {
          resourceKind: "document",
          documentId: "doc_parent",
          workspaceId: "ws_1",
          iconHint: "page",
        },
        iconHint: "page",
        node: {} as SidebarTreeNode,
      },
    ];

    const html = renderToString(
      <TreeShellIframeHost
        mode="page"
        surfaceTestId="sidebar-page-tree-shell"
        workspaceId="ws_1"
        activeDocumentId="doc_parent"
        pageTreeItems={pageItems}
      />,
    );

    expect(html).toContain("Tree Shell Loading");
    expect(html).not.toContain("__MNOTE_TREE_SHELL_OVERRIDE__");
    expect(html).not.toContain("父页面");
  });

  it("page tree 提供空 inline items 时也应直接生成 srcDoc", async () => {
    const fetchMock = vi.spyOn(globalThis, "fetch").mockResolvedValue(
      new Response(
        '<!doctype html><html><body><script id="tree-shell-state" type="application/json">{}</script></body></html>',
        { status: 200, headers: { "content-type": "text/html" } },
      ),
    );

    await act(async () => {
      root.render(
        <TreeShellIframeHost
          mode="page"
          surfaceTestId="sidebar-page-tree-shell"
          workspaceId="ws_1"
          activeDocumentId={null}
          pageTreeItems={[]}
        />,
      );
    });

    await act(async () => {
      await Promise.resolve();
    });

    const iframe = container.querySelector('[data-testid="sidebar-page-tree-shell-rust-iframe"]');
    expect(fetchMock).not.toHaveBeenCalled();
    expect(iframe?.getAttribute("data-tree-shell-inline")).toBe("1");
    expect(iframe?.getAttribute("srcdoc")).not.toContain("<script>window.__MNOTE_TREE_SHELL_OVERRIDE__ =");
    expect(readTreeShellState(iframe?.getAttribute("srcdoc")).items).toEqual([]);
  });

  it("file tree 提供应直接消费的 kernel items 时，应本地生成 shell 并注入正式 item contract", async () => {
    const fetchMock = vi.spyOn(globalThis, "fetch").mockResolvedValue(
      new Response(
        '<!doctype html><html><body><script id="tree-shell-state" type="application/json">{}</script></body></html>',
        { status: 200, headers: { "content-type": "text/html" } },
      ),
    );
    const fileTreeItems: KernelFileTreeProjectionItem[] = [
      {
        rowId: "doc:doc_a",
        rowKind: "document",
        nodeId: "doc_a",
        parentNodeId: null,
        nodeType: "page",
        projectionKind: "file_tree",
        title: "文档 A",
        depth: 0,
        position: 0,
        childCount: 2,
        expandable: true,
        expandedByDefault: true,
        capabilities: ["expand", "open", "select"],
        resourceMeta: {
          resourceKind: "document",
          documentId: "doc_a",
          workspaceId: "ws_1",
          iconHint: "page",
        },
        iconHint: "page",
      },
      {
        rowId: "asset:asset_pdf",
        rowKind: "asset",
        nodeId: "asset:asset_pdf",
        parentNodeId: "doc_a",
        nodeType: "pdf",
        projectionKind: "file_tree",
        title: "guide.pdf",
        depth: 1,
        position: 1,
        childCount: 0,
        expandable: false,
        expandedByDefault: false,
        capabilities: ["open-asset", "select"],
        resourceMeta: {
          resourceKind: "pdf",
          documentId: "doc_a",
          assetId: "asset_pdf",
          workspaceId: "ws_1",
          assetKind: "pdf",
          iconHint: "pdf",
        },
        iconHint: "pdf",
      },
    ];

    expect(buildTreeShellInlineKernelFileTreeItems(fileTreeItems)).toEqual([
      expect.objectContaining({
        nodeId: "doc_a",
        rowKind: "document",
        iconHint: "page",
      }),
      expect.objectContaining({
        nodeId: "asset:asset_pdf",
        rowKind: "asset",
        iconHint: "pdf",
      }),
    ]);

    await act(async () => {
      root.render(
        <TreeShellIframeHost
          mode="filetree"
          surfaceTestId="sidebar-file-tree-shell"
          workspaceId="ws_1"
          activeDocumentId="doc_a"
          inlineFileTreeItems={fileTreeItems}
        />,
      );
    });

    await act(async () => {
      await Promise.resolve();
    });

    const iframe = container.querySelector('[data-testid="sidebar-file-tree-shell-rust-iframe"]');
    expect(fetchMock).not.toHaveBeenCalled();
    expect(iframe?.getAttribute("data-tree-shell-inline")).toBe("1");
    expect(iframe?.getAttribute("srcdoc")).not.toContain("<script>window.__MNOTE_TREE_SHELL_OVERRIDE__ =");
    expect(iframe?.getAttribute("srcdoc")).toContain('"rowKind":"asset"');
    expect(iframe?.getAttribute("srcdoc")).toContain('data-rust-filetree-renderer="initial_v1"');
    expect(iframe?.getAttribute("srcdoc")).toContain('data-rust-rendered-row="filetree"');
    expect(iframe?.getAttribute("srcdoc")).toContain("hydrateInitialFileTree");
    expect(iframe?.getAttribute("srcdoc")).toContain("const usedRustInitialRenderer = hydrateInitialRenderer();");
    expect(iframe?.getAttribute("srcdoc")).toContain("patchFileTreeActiveDom();");
    expect(iframe?.getAttribute("srcdoc")).toContain('if (mode === "filetree" && usedRustInitialRenderer) {patchFileTreeActiveDom();syncFileTreeSelectionDom();syncFileTreeDropTargetDom();return;}');
    expect(iframe?.getAttribute("srcdoc")).toContain('"rendererInput"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"mode":"fileTree"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"expandedIds":["doc_a"]');
    expect(iframe?.getAttribute("srcdoc")).toContain("const rendererExpandedIds = normalizeStringArray(rendererInput.expandedIds);");
    expect(iframe?.getAttribute("srcdoc")).toContain('"filetreeSelection"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"selectedRowIds":["index:doc_a"]');
    expect(iframe?.getAttribute("srcdoc")).toContain('"focusedRowId":"index:doc_a"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"contractName":"rust_filetree_selection_reducer_v1"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"contractName":"rust_tree_shell_runtime_artifact_v1"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"family":"rust_family"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"executionStrategy":"browser_bridge"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"browserBridge":"iframe_srcdoc"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"wasmModuleUrl":"/api/tree-shell-runtime/mnote-tree-shell-runtime_bg.wasm"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"jsGlueUrl":"/api/tree-shell-runtime/mnote-tree-shell-runtime.js"');
    expect(iframe?.getAttribute("srcdoc")).toContain("applyFileTreeSelectionAction");
    expect(iframe?.getAttribute("srcdoc")).toContain("loadTreeShellWasmRuntime");
    expect(iframe?.getAttribute("srcdoc")).toContain("reduceTreeShellRuntimeViaWasm");
    expect(iframe?.getAttribute("srcdoc")).toContain("reduceFileTreeSelectionActionWithRuntime");
    expect(iframe?.getAttribute("srcdoc")).toContain("reduceTreeShellRuntimeWithArtifact");
    expect(iframe?.getAttribute("srcdoc")).toContain('mode: "fileTree"');
    expect(iframe?.getAttribute("srcdoc")).toContain("runtimeArtifact.runtimeApi.reduceEndpoint");
    expect(iframe?.getAttribute("srcdoc")).toContain("computeFileTreeSelectionActionResult(action)");
    expect(iframe?.getAttribute("srcdoc")).toContain('kind: "update_drop_target"');
    expect(iframe?.getAttribute("srcdoc")).toContain("updateDropTarget");
    expect(iframe?.getAttribute("srcdoc")).toContain("dispatchInternalDrop");
    expect(iframe?.getAttribute("srcdoc")).toContain("dispatchExternalDrop");
    expect(iframe?.getAttribute("srcdoc")).toContain("dispatchFileTreeDropWithRuntime");
    expect(iframe?.getAttribute("srcdoc")).toContain("fileTreeInternalDrop");
    expect(iframe?.getAttribute("srcdoc")).toContain("fileTreeExternalDrop");
    expect(iframe?.getAttribute("srcdoc")).toContain("const fallbackRowId = firstItem?.rowId || firstDoc || \"\";");
    expect(iframe?.getAttribute("srcdoc")).toContain("replayFileTreeRuntimeHostEvents");
    expect(iframe?.getAttribute("srcdoc")).toContain("runtimeResult && fallbackHostEvent.runtimeRequired === true");
    expect(iframe?.getAttribute("srcdoc")).toContain("syncFileTreeDropTargetDom");
    expect(iframe?.getAttribute("srcdoc")).toContain("row.dataset.dropTarget");
    expect(iframe?.getAttribute("srcdoc")).toContain("commitFileTreeRuntimeState");
    expect(iframe?.getAttribute("srcdoc")).toContain("const rendererFiletreeSelection =");
    expect(iframe?.getAttribute("srcdoc")).toContain("rendererFiletreeSelection.selectedRowIds");
    expect(iframe?.getAttribute("srcdoc")).toContain("computeFileTreeSelectionActionResult");
    expect(iframe?.getAttribute("srcdoc")).toContain("const selectFileTreeContextRow = (rowId) =>");
    const fileTreeFallbackRenderBody =
      iframe?.getAttribute("srcdoc")?.match(/const renderFileTree = \(\) => \{(?<body>[\s\S]*?)const renderNode =/)?.groups
        ?.body ?? "";
    expect(fileTreeFallbackRenderBody).toContain(
      'toggleButton.addEventListener("click", (event) => {event.stopPropagation();toggleExpand(item.nodeId);});',
    );
    expect(fileTreeFallbackRenderBody).not.toContain(
      'applyPageKeyboardAction({ kind: "toggle", nodeId: item.nodeId }, item, event.currentTarget);',
    );
    expect(iframe?.getAttribute("srcdoc")).toContain("guide.pdf");
    const state = readTreeShellState(iframe?.getAttribute("srcdoc"));
    expect(state.items).toEqual([
      expect.objectContaining({ rowId: "doc:doc_a", nodeId: "doc_a" }),
      expect.objectContaining({ rowId: "asset:asset_pdf", nodeId: "asset:asset_pdf" }),
    ]);
    expect(state.rendererInput?.projectionItemIds).toEqual(["doc:doc_a", "asset:asset_pdf"]);
    expect(iframe?.getAttribute("srcdoc")).not.toContain("\n");
  });

  it("picker inline override 在高亮变化时应复用 bootstrap 文档，并通过 postMessage 同步状态", async () => {
    const fetchMock = vi.spyOn(globalThis, "fetch").mockResolvedValue(
      new Response(
        '<!doctype html><html><body><script id="tree-shell-state" type="application/json">{}</script></body></html>',
        { status: 200, headers: { "content-type": "text/html" } },
      ),
    );
    const pickerItems: TreeShellPickerItem[] = [
      { kind: "doc", id: "doc_1", title: "页面 1", depth: 0 },
      { kind: "doc", id: "doc_2", title: "页面 2", depth: 0 },
    ];

    await act(async () => {
      root.render(
        <TreeShellIframeHost
          mode="picker"
          surfaceTestId="tree-picker-surface"
          workspaceId="ws_1"
          activeDocumentId="doc_1"
          activePickerItemKey="doc_1"
          excludeIds={["doc_hidden"]}
          pickerItems={pickerItems}
        />,
      );
    });

    await act(async () => {
      await Promise.resolve();
    });

    const iframe = container.querySelector('[data-testid="tree-picker-surface-rust-iframe"]');
    expect(iframe?.getAttribute("srcdoc")).toContain('data-rust-picker-renderer="initial_v1"');
    expect(iframe?.getAttribute("srcdoc")).toContain('data-rust-rendered-row="picker"');
    expect(iframe?.getAttribute("srcdoc")).toContain("hydrateInitialPickerTree");
    expect(readTreeShellState(iframe?.getAttribute("srcdoc")).items).toEqual([
      expect.objectContaining({ nodeId: "doc_1" }),
      expect.objectContaining({ nodeId: "doc_2" }),
    ]);
    expect(readTreeShellState(iframe?.getAttribute("srcdoc")).rendererInput?.activePickerItem).toBe("doc_1");
    expect(iframe?.getAttribute("srcdoc")).toContain('"activePickerItem":"doc_1"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"excludedPickerIds":["doc_hidden"]');
    expect(iframe?.getAttribute("srcdoc")).toContain('"contractName":"rust_picker_state_reducer_v1"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"contractName":"rust_tree_shell_runtime_artifact_v1"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"family":"rust_family"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"executionStrategy":"browser_bridge"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"browserBridge":"iframe_srcdoc"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"wasmModuleUrl":"/api/tree-shell-runtime/mnote-tree-shell-runtime_bg.wasm"');
    expect(iframe?.getAttribute("srcdoc")).toContain('"jsGlueUrl":"/api/tree-shell-runtime/mnote-tree-shell-runtime.js"');
    expect(iframe?.getAttribute("srcdoc")).toContain("applyPickerStateAction");
    expect(iframe?.getAttribute("srcdoc")).toContain("loadTreeShellWasmRuntime");
    expect(iframe?.getAttribute("srcdoc")).toContain("reduceTreeShellRuntimeViaWasm");
    expect(iframe?.getAttribute("srcdoc")).toContain("reducePickerStateActionWithRuntime");
    expect(iframe?.getAttribute("srcdoc")).toContain("reduceTreeShellRuntimeWithArtifact");
    expect(iframe?.getAttribute("srcdoc")).toContain('mode: "picker"');
    expect(iframe?.getAttribute("srcdoc")).toContain("buildPickerRuntimeEnvironment");
    expect(iframe?.getAttribute("srcdoc")).toContain("runtimeArtifact.runtimeApi.reduceEndpoint");
    expect(iframe?.getAttribute("srcdoc")).toContain("pickerPickDocument");
    expect(iframe?.getAttribute("srcdoc")).toContain("patchPickerActiveDom");
    expect(iframe?.getAttribute("srcdoc")).toContain("getPickablePickerEntries");
    expect(iframe?.getAttribute("srcdoc")).toContain("postPickerPickResultToHost");
    expect(iframe?.getAttribute("srcdoc")).toContain('tabindex="0"');
    expect(iframe?.getAttribute("srcdoc")).toContain('tabindex="-1"');
    expect(iframe?.getAttribute("srcdoc")).toContain("dispatchPickerPickByItemKeyWithRuntime");
    expect(iframe?.getAttribute("srcdoc")).toContain('dispatchPickerPickByItemKeyWithRuntime("__root__", { focusDom: true })');
    expect(iframe?.getAttribute("srcdoc")).toContain("dispatchPickerPickByItemKeyWithRuntime(item.nodeId, { focusDom: true })");
    expect(iframe?.getAttribute("srcdoc")).not.toContain('applyPickerFocusByItemKey("__root__", { focusDom: true });applyPickerStateAction({ kind: "pick" })');
    expect(iframe?.getAttribute("srcdoc")).not.toContain('applyPickerFocusByItemKey(item.nodeId, { focusDom: true });applyPickerStateAction({ kind: "pick" })');
    expect(iframe?.getAttribute("srcdoc")).toContain("const shouldFocusDom = options.focusDom === true");
    expect(iframe?.getAttribute("srcdoc")).toContain("if (shouldFocusDom) focusPickerRowElement");
    expect(iframe?.getAttribute("srcdoc")).toContain("postPickerPickResultToHost(runtimeResult);");
    expect(iframe?.getAttribute("srcdoc")).toContain("if (runtimeResult) {reconcilePickerRuntimeResult(runtimeResult, fallbackResult);return;}commitPickerFocusResult(fallbackResult");
    expect(iframe?.getAttribute("srcdoc")).toContain("commitPickerFocusResult(focusResult, { focusDom: focusAction.focusDom });");
    const bindPickerRootEventsBody =
      iframe?.getAttribute("srcdoc")?.match(/const bindPickerRootEvents = \(row\) => \{(?<body>[\s\S]*?)\n        \};/)?.groups
        ?.body ?? "";
    expect(bindPickerRootEventsBody).not.toContain("postPickerPickResultToHost(");
    const bindPickerRowEventsBody =
      iframe?.getAttribute("srcdoc")?.match(/const bindPickerRowEvents = \(row, item\) => \{(?<body>[\s\S]*?)\n        \};/)?.groups
        ?.body ?? "";
    expect(bindPickerRowEventsBody).not.toContain("handleNavigate(item.nodeId)");
    expect(bindPickerRowEventsBody).not.toContain("postPickerPickResultToHost(");
    expect(iframe?.getAttribute("srcdoc")).not.toContain("postPickerPickResultToHost(applyPickerStateAction");
    expect(iframe?.getAttribute("srcdoc")).toContain("rendererInput.activePickerItem");
    expect(iframe?.getAttribute("srcdoc")).toContain("rendererInput.excludedPickerIds");
    const postMessage = vi.fn();
    Object.defineProperty(iframe, "contentWindow", {
      configurable: true,
      value: { postMessage },
    });

    await act(async () => {
      root.render(
        <TreeShellIframeHost
          mode="picker"
          surfaceTestId="tree-picker-surface"
          workspaceId="ws_1"
          activeDocumentId="doc_2"
          activePickerItemKey="doc_2"
          pickerItems={pickerItems}
        />,
      );
    });

    expect(fetchMock).not.toHaveBeenCalled();
    expect(postMessage).toHaveBeenCalledWith(
      expect.objectContaining({
        channel: "tree-picker-surface",
        type: "tree.shell.state.patch",
        activeDocumentId: "doc_2",
        activePickerItemKey: "doc_2",
      }),
      "*",
    );
  });

  it("picker 宿主应向 iframe 下发键盘命令，并接回焦点变化事件", async () => {
    const fetchMock = vi.spyOn(globalThis, "fetch").mockResolvedValue(
      new Response(
        '<!doctype html><html><body><script id="tree-shell-state" type="application/json">{}</script></body></html>',
        { status: 200, headers: { "content-type": "text/html" } },
      ),
    );
    const onPickerFocusChange = vi.fn();
    const pickerItems: TreeShellPickerItem[] = [
      { kind: "doc", id: "doc_1", title: "页面 1", depth: 0 },
      { kind: "doc", id: "doc_2", title: "页面 2", depth: 0 },
    ];

    await act(async () => {
      root.render(
        <TreeShellIframeHost
          mode="picker"
          surfaceTestId="tree-picker-surface"
          workspaceId="ws_1"
          activeDocumentId="doc_1"
          activePickerItemKey="doc_1"
          pickerItems={pickerItems}
          onPickerFocusChange={onPickerFocusChange}
        />,
      );
    });

    await act(async () => {
      await Promise.resolve();
    });

    const iframe = container.querySelector('[data-testid="tree-picker-surface-rust-iframe"]');
    const postMessage = vi.fn();
    Object.defineProperty(iframe, "contentWindow", {
      configurable: true,
      value: window,
    });
    vi.spyOn(window, "postMessage").mockImplementation(postMessage);

    await act(async () => {
      root.render(
        <TreeShellIframeHost
          mode="picker"
          surfaceTestId="tree-picker-surface"
          workspaceId="ws_1"
          activeDocumentId="doc_1"
          activePickerItemKey="doc_1"
          pickerItems={pickerItems}
          pickerCommand={{ kind: "next", seq: 1 }}
          onPickerFocusChange={onPickerFocusChange}
        />,
      );
    });

    expect(fetchMock).not.toHaveBeenCalled();
    expect(postMessage).toHaveBeenCalledWith(
      expect.objectContaining({
        channel: "tree-picker-surface",
        type: "tree.picker.command",
        command: "next",
      }),
      "*",
    );

    await act(async () => {
      window.dispatchEvent(
        new MessageEvent("message", {
          data: {
            channel: "tree-picker-surface",
            type: "tree.picker.focus.changed",
            documentId: "doc_2",
            itemKey: "doc_2",
          },
          source: window,
        }),
      );
    });

    expect(onPickerFocusChange).toHaveBeenCalledWith({
      documentId: "doc_2",
      itemKey: "doc_2",
    });
  });

  it("应把 iframe postMessage 桥接回宿主回调，并忽略错误 channel", async () => {
    const onNavigate = vi.fn();
    const onPageContextMenu = vi.fn();
    const onPageExpandChange = vi.fn();
    const onPageFocusChange = vi.fn();
    const onPick = vi.fn();
    const onFileTreeContextMenu = vi.fn();
    const onFileTreeSelectionChange = vi.fn();
    const onAssetOpen = vi.fn();
    const onTreeMutation = vi.fn();
    const onInternalDrop = vi.fn();
    const onDropFiles = vi.fn();
    const droppedFile = new File(["hello"], "hello.txt", { type: "text/plain" });

    await act(async () => {
      root.render(
        <TreeShellIframeHost
          mode="filetree"
          surfaceTestId="sidebar-file-tree-shell"
          workspaceId="ws_1"
          activeDocumentId="doc_1"
          channel="sidebar-file-tree-shell"
          host="sidebar-file-tree-shell"
          onNavigate={onNavigate}
          onPageContextMenu={onPageContextMenu}
          onPageExpandChange={onPageExpandChange}
          onPageFocusChange={onPageFocusChange}
          onPick={onPick}
          onFileTreeContextMenu={onFileTreeContextMenu}
          onFileTreeSelectionChange={onFileTreeSelectionChange}
          onAssetOpen={onAssetOpen}
          onTreeMutation={onTreeMutation}
          onInternalDrop={onInternalDrop}
          onDropFiles={onDropFiles}
        />,
      );
    });

    const iframe = container.querySelector("iframe");
    expect(iframe).not.toBeNull();
    Object.defineProperty(iframe, "contentWindow", {
      configurable: true,
      value: window,
    });

    await act(async () => {
      window.dispatchEvent(
        new MessageEvent("message", {
          data: {
            channel: "wrong-channel",
            type: "tree.navigate",
            documentId: "doc_wrong",
          },
          source: window,
        }),
      );
    });

    expect(onNavigate).not.toHaveBeenCalled();

    await act(async () => {
      window.dispatchEvent(
        new MessageEvent("message", {
          data: {
            channel: "sidebar-file-tree-shell",
            type: "tree.navigate",
            documentId: "doc_2",
          },
          source: window,
        }),
      );
      window.dispatchEvent(
        new MessageEvent("message", {
          data: {
            channel: "sidebar-file-tree-shell",
            type: "tree.page.context-menu",
            documentId: "doc_2",
            x: 12,
            y: 34,
          },
          source: window,
        }),
      );
      window.dispatchEvent(
        new MessageEvent("message", {
          data: {
            channel: "sidebar-file-tree-shell",
            type: "tree.page.expand.changed",
            documentId: "doc_2",
            expanded: true,
          },
          source: window,
        }),
      );
      window.dispatchEvent(
        new MessageEvent("message", {
          data: {
            channel: "sidebar-file-tree-shell",
            type: "tree.page.focus.changed",
            documentId: "doc_3",
          },
          source: window,
        }),
      );
      window.dispatchEvent(
        new MessageEvent("message", {
          data: {
            channel: "sidebar-file-tree-shell",
            type: "tree.pick.root",
            documentId: null,
          },
          source: window,
        }),
      );
      window.dispatchEvent(
        new MessageEvent("message", {
          data: {
            channel: "sidebar-file-tree-shell",
            type: "tree.filetree.context-menu",
            documentId: "doc_2",
            assetId: "asset_2",
            rowId: "asset:asset_2",
            rowKind: "asset",
            x: 56,
            y: 78,
          },
          source: window,
        }),
      );
      window.dispatchEvent(
        new MessageEvent("message", {
          data: {
            channel: "sidebar-file-tree-shell",
            type: "tree.filetree.selection.changed",
            selectedRowIds: ["doc:doc_2", "index:doc_2"],
            anchorRowId: "doc:doc_2",
            focusedRowId: "index:doc_2",
          },
          source: window,
        }),
      );
      window.dispatchEvent(
        new MessageEvent("message", {
          data: {
            channel: "sidebar-file-tree-shell",
            type: "tree.asset.open",
            documentId: "doc_2",
            assetId: "asset_2",
          },
          source: window,
        }),
      );
      window.dispatchEvent(
        new MessageEvent("message", {
          data: {
            channel: "sidebar-file-tree-shell",
            type: "tree.node.renamed",
            documentId: "doc_2",
          },
          source: window,
        }),
      );
    });

    expect(onNavigate).toHaveBeenCalledWith("doc_2");
    expect(onPageContextMenu).toHaveBeenCalledWith({
      documentId: "doc_2",
      x: 12,
      y: 34,
    });
    expect(onPageExpandChange).toHaveBeenCalledWith({
      documentId: "doc_2",
      expanded: true,
    });
    expect(onPageFocusChange).toHaveBeenCalledWith({
      documentId: "doc_3",
    });
    expect(onPick).toHaveBeenCalledWith(null);
    expect(onFileTreeContextMenu).toHaveBeenCalledWith({
      documentId: "doc_2",
      assetId: "asset_2",
      rowId: "asset:asset_2",
      rowKind: "asset",
      x: 56,
      y: 78,
    });
    expect(onFileTreeSelectionChange).toHaveBeenCalledWith({
      selectedRowIds: ["doc:doc_2", "index:doc_2"],
      anchorRowId: "doc:doc_2",
      focusedRowId: "index:doc_2",
    });
    expect(onAssetOpen).toHaveBeenCalledWith({
      documentId: "doc_2",
      assetId: "asset_2",
    });
    expect(onTreeMutation).toHaveBeenCalledWith({
      type: "tree.node.renamed",
      documentId: "doc_2",
    });

    await act(async () => {
      window.dispatchEvent(
        new MessageEvent("message", {
          data: {
            channel: "sidebar-file-tree-shell",
            type: "tree.filetree.internal-drop",
            rowIds: ["doc:doc_source", "asset:asset_source"],
            copy: true,
            rowId: "doc:doc_target",
            rowKind: "doc",
            documentId: "doc_target",
          },
          source: window,
        }),
      );
      window.dispatchEvent(
        new MessageEvent("message", {
          data: {
            channel: "sidebar-file-tree-shell",
            type: "tree.filetree.drop-files",
            documentId: "doc_target",
            rowId: "doc:doc_target",
            rowKind: "doc",
            files: [droppedFile],
          },
          source: window,
        }),
      );
    });

    expect(onInternalDrop).toHaveBeenCalledWith({
      targetDocumentId: "doc_target",
      targetRowId: "doc:doc_target",
      targetRowKind: "doc",
      targetAssetId: null,
      rowIds: ["doc:doc_source", "asset:asset_source"],
      copy: true,
    });
    expect(onDropFiles).toHaveBeenCalledWith({
      targetDocumentId: "doc_target",
      targetRowId: "doc:doc_target",
      targetRowKind: "doc",
      targetAssetId: null,
      files: [droppedFile],
    });
  });
});
