import { act } from "react";
import { createRoot, type Root } from "react-dom/client";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { SidebarTreeSurface, TreePickerSurface, type TreeRendererFamily } from "./tree-shell-surface";

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

vi.mock("@/components/sidebar/private-tree", () => ({
  PrivateTree: () => <div data-testid="private-tree-fallback" />,
}));

vi.mock("@/components/sidebar/file-tree", () => ({
  FileTree: () => <div data-testid="file-tree-fallback" />,
}));

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

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

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

  function renderPageSurface(rendererFamily: TreeRendererFamily) {
    act(() => {
      root.render(
        <SidebarTreeSurface
          mode="page"
          rendererFamily={rendererFamily}
          workspaceId="ws_1"
          treeShellEnabled
          rows={[]}
          expanded={new Set<string>()}
          activeId=""
          onToggleExpand={() => undefined}
          onMove={() => undefined}
          onCreateChild={() => undefined}
          onContextMenu={() => undefined}
        />,
      );
    });
  }

  it("page tree surface 在 rust_family 下可切到同源 iframe host", () => {
    renderPageSurface("rust_family");

    const surface = container.querySelector('[data-testid="sidebar-page-tree-shell"]');
    const rustHost = container.querySelector(
      '[data-testid="sidebar-page-tree-shell-rust-host"]',
    );
    const iframe = container.querySelector('[data-testid="sidebar-page-tree-shell-rust-iframe"]');

    expect(surface?.getAttribute("data-renderer-family")).toBe("rust_family");
    expect(surface?.getAttribute("data-tree-host-kind")).toBe("rust_family");
    expect(surface?.getAttribute("data-tree-host-implementation")).toBe("mnote_web_iframe_proxy");
    expect(rustHost).not.toBeNull();
    expect(iframe).not.toBeNull();
    expect(container.querySelector('[data-testid="private-tree-fallback"]')).toBeNull();
  });

  it("page tree 在禁用 tree shell 时应安全回退到 React fallback", () => {
    act(() => {
      root.render(
        <SidebarTreeSurface
          mode="page"
          rendererFamily="rust_family"
          workspaceId="ws_1"
          treeShellEnabled={false}
          rows={[]}
          expanded={new Set<string>()}
          activeId=""
          onToggleExpand={() => undefined}
          onMove={() => undefined}
          onCreateChild={() => undefined}
          onContextMenu={() => undefined}
        />,
      );
    });

    const surface = container.querySelector('[data-testid="sidebar-page-tree-shell"]');
    expect(surface?.getAttribute("data-tree-host-implementation")).toBe("react_fallback");
    expect(container.querySelector('[data-testid="private-tree-fallback"]')).not.toBeNull();
  });

  it("file tree surface 在 rust_family 下也应走同一 host 选择契约", () => {
    act(() => {
      root.render(
        <SidebarTreeSurface
          mode="filetree"
          rendererFamily="rust_family"
          workspaceId="ws_1"
          treeShellEnabled
          rows={[]}
          activeId=""
          selectedRowIds={new Set<string>()}
          onRowClick={() => undefined}
          onRowDoubleClick={() => undefined}
          onRowContextMenu={() => undefined}
          onToggleExpand={() => undefined}
          onCreateChild={() => undefined}
        />,
      );
    });

    const surface = container.querySelector('[data-testid="sidebar-file-tree-shell"]');
    const rustHost = container.querySelector(
      '[data-testid="sidebar-file-tree-shell-rust-host"]',
    );
    const iframe = container.querySelector('[data-testid="sidebar-file-tree-shell-rust-iframe"]');

    expect(surface?.getAttribute("data-renderer-family")).toBe("rust_family");
    expect(surface?.getAttribute("data-tree-host-kind")).toBe("rust_family");
    expect(surface?.getAttribute("data-tree-host-implementation")).toBe("mnote_web_iframe_proxy");
    expect(rustHost).not.toBeNull();
    expect(iframe).not.toBeNull();
    expect(container.querySelector('[data-testid="file-tree-fallback"]')).toBeNull();
  });

  it("file tree surface 应把 iframe 内部拖放与外部文件拖放桥接回宿主回调", async () => {
    const onInternalDrop = vi.fn();
    const onDropFiles = vi.fn();
    const targetRow = {
      kind: "doc",
      rowId: "doc:doc_target",
      depth: 0,
      docId: "doc_target",
      parentDocId: null,
      node: {
        _id: "doc_target",
        id: "doc_target",
        title: "目标页面",
      },
      hasChildren: false,
      isExpanded: false,
    };
    const droppedFile = new File(["bridge"], "bridge.txt", { type: "text/plain" });

    await act(async () => {
      root.render(
        <SidebarTreeSurface
          mode="filetree"
          rendererFamily="rust_family"
          workspaceId="ws_1"
          treeShellEnabled
          rows={[targetRow]}
          activeId=""
          selectedRowIds={new Set<string>()}
          onRowClick={() => undefined}
          onRowDoubleClick={() => undefined}
          onRowContextMenu={() => undefined}
          onToggleExpand={() => undefined}
          onCreateChild={() => undefined}
          onInternalDrop={onInternalDrop}
          onDropFiles={onDropFiles}
        />,
      );
    });

    const iframe = container.querySelector(
      '[data-testid="sidebar-file-tree-shell-rust-iframe"]',
    );
    expect(iframe).not.toBeNull();
    Object.defineProperty(iframe, "contentWindow", {
      configurable: true,
      value: window,
    });

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

    expect(onInternalDrop).toHaveBeenCalledWith({
      targetRow,
      rowIds: ["doc:doc_source"],
      copy: false,
    });
    expect(onDropFiles).toHaveBeenCalledWith("doc_target", [droppedFile], targetRow);
  });

  it("picker surface 在 rust_family 下也应挂到同一 host 边界", async () => {
    const onPick = vi.fn();

    await act(async () => {
      root.render(
        <TreePickerSurface
          rendererFamily="rust_family"
          workspaceId="ws_1"
          treeShellEnabled
          items={[{ kind: "doc", id: "doc_1", title: "页面 1", depth: 0 }]}
          highlighted={0}
          onHighlight={() => undefined}
          onPick={onPick}
        />,
      );
    });

    const surface = container.querySelector('[data-testid="tree-picker-surface"]');
    const rustHost = container.querySelector('[data-testid="tree-picker-surface-rust-host"]');
    const iframe = container.querySelector('[data-testid="tree-picker-surface-rust-iframe"]');

    expect(surface?.getAttribute("data-renderer-family")).toBe("rust_family");
    expect(surface?.getAttribute("data-tree-host-kind")).toBe("rust_family");
    expect(surface?.getAttribute("data-tree-host-implementation")).toBe("mnote_web_iframe_proxy");
    expect(rustHost).not.toBeNull();
    expect(iframe).not.toBeNull();
    expect(onPick).not.toHaveBeenCalled();
  });

  it("picker 在 rust_family 但 tree shell 不可用时仍应保留 React fallback", () => {
    act(() => {
      root.render(
        <TreePickerSurface
          rendererFamily="rust_family"
          workspaceId="ws_1"
          treeShellEnabled={false}
          items={[{ kind: "doc", id: "doc_1", title: "页面 1", depth: 0 }]}
          highlighted={0}
          emptyText="没有匹配结果"
          onHighlight={() => undefined}
          onPick={vi.fn()}
        />,
      );
    });

    const surface = container.querySelector('[data-testid="tree-picker-surface"]');
    const rustHost = container.querySelector('[data-testid="tree-picker-surface-rust-host"]');
    const row = container.querySelector('[data-testid="tree-picker-row"]');

    expect(surface?.getAttribute("data-tree-host-implementation")).toBe("react_fallback");
    expect(surface?.getAttribute("data-tree-host-kind")).toBe("rust_family");
    expect(rustHost).not.toBeNull();
    expect(row).not.toBeNull();
  });
});
