import { describe, expect, it } from "vitest";
import type { SidebarInitialData } from "@/components/sidebar/types";
import {
  applyTreeStreamDelta,
  applyTreeStreamDeltaToProjectionState,
} from "./tree-delta";

const baseSidebarData: SidebarInitialData = {
  activeWorkspaceId: "ws_1",
  workspaces: [],
  documents: [
    {
      id: "root",
      workspace_id: "ws_1",
      title: "Root",
      parent_id: null,
      sort_order: 0,
      is_starred: false,
      access_scope: "private",
      is_template: false,
      created_at: "2026-04-18T00:00:00Z",
      updated_at: null,
    },
    {
      id: "child",
      workspace_id: "ws_1",
      title: "Child",
      parent_id: "root",
      sort_order: 1,
      is_starred: false,
      access_scope: "private",
      is_template: false,
      created_at: "2026-04-18T00:00:00Z",
      updated_at: null,
    },
  ],
  kernelFileTreeProjection: {
    projectionId: "kernel_projection:file_tree:root",
    projection: "file_tree",
    rootNodeId: null,
    items: [
      {
        rowId: "doc:root",
        rowKind: "document",
        nodeId: "root",
        parentNodeId: null,
        nodeType: "page",
        projectionKind: "file_tree",
        title: "Root",
        depth: 0,
        position: 0,
        childCount: 2,
        expandable: true,
        expandedByDefault: true,
        capabilities: ["expand", "open", "select"],
        resourceMeta: {
          resourceKind: "document",
          documentId: "root",
          workspaceId: "ws_1",
          iconHint: "page",
        },
        iconHint: "page",
      },
      {
        rowId: "index:root",
        rowKind: "index",
        nodeId: "index:root",
        parentNodeId: "root",
        nodeType: "index",
        projectionKind: "file_tree",
        title: "index.md",
        depth: 1,
        position: 0,
        childCount: 0,
        expandable: false,
        expandedByDefault: false,
        capabilities: ["open", "select"],
        resourceMeta: {
          resourceKind: "index",
          documentId: "root",
          workspaceId: "ws_1",
          iconHint: "index",
        },
        iconHint: "index",
      },
    ],
    edges: [],
  },
  kernelSidebarProjection: {
    projectionId: "kernel_projection:sidebar_tree:workspace_root",
    projection: "sidebar_tree",
    rootNodeId: null,
    items: [
      {
        nodeId: "root",
        parentNodeId: null,
        nodeType: "page",
        projectionKind: "sidebar_tree",
        title: "Root",
        depth: 0,
        position: 0,
        childCount: 1,
        expandable: true,
        expandedByDefault: true,
        capabilities: [
          "expand",
          "open",
          "drag",
          "drop",
          "select",
          "create-child",
          "rename",
          "archive",
          "restore",
          "context-menu",
          "reorder",
        ],
        resourceMeta: {
          resourceKind: "document",
          documentId: "root",
          workspaceId: "ws_1",
          iconHint: "page",
        },
        iconHint: "page",
      },
      {
        nodeId: "child",
        parentNodeId: "root",
        nodeType: "page",
        projectionKind: "sidebar_tree",
        title: "Child",
        depth: 1,
        position: 1,
        childCount: 0,
        expandable: false,
        expandedByDefault: true,
        capabilities: [
          "open",
          "drag",
          "drop",
          "select",
          "create-child",
          "rename",
          "archive",
          "restore",
          "context-menu",
          "reorder",
        ],
        resourceMeta: {
          resourceKind: "document",
          documentId: "child",
          workspaceId: "ws_1",
          iconHint: "page",
        },
        iconHint: "page",
      },
    ],
    edges: [
      {
        id: "edge:root:child:parent_of",
        edgeType: "parent_of",
        workspaceId: "ws_1",
        fromNodeId: "root",
        toNodeId: "child",
      },
    ],
  },
  kernelSidebarTree: [],
  trashedDocuments: [],
  trashedMediaAssets: [],
  trashedMindmapAssets: [],
  trashedTableAssets: [],
  tableAssets: [],
  mindmapDocs: [],
  mindmapAssets: [],
  mindmapAssetChildren: {},
  mediaAssets: [],
};

const fileTreeProjectionBase: SidebarInitialData = {
  ...baseSidebarData,
  mediaAssets: [
    {
      id: "asset_pdf",
      workspace_id: "ws_1",
      document_id: "root",
      asset_type: "file",
      file_url: "/manual.pdf",
      thumbnail_url: null,
      bucket: null,
      storage_path: "documents/root/manual.pdf",
      file_name: "manual.pdf",
      file_size: 1024,
      mime_type: "application/pdf",
      ocr_payload: undefined,
      ocr_strategy: null,
      ocr_text: null,
      ocr_status: null,
      signed_url: null,
      created_at: "2026-04-18T00:00:00Z",
      updated_at: "2026-04-18T00:00:00Z",
    },
    {
      id: "asset_book",
      workspace_id: "ws_1",
      document_id: "root",
      asset_type: "file",
      file_url: "/novel.epub",
      thumbnail_url: null,
      bucket: null,
      storage_path: "documents/root/novel.epub",
      file_name: "novel.epub",
      file_size: 2048,
      mime_type: "application/epub+zip",
      ocr_payload: undefined,
      ocr_strategy: null,
      ocr_text: null,
      ocr_status: null,
      signed_url: null,
      created_at: "2026-04-18T00:00:00Z",
      updated_at: "2026-04-18T00:00:00Z",
    },
    {
      id: "asset_mindmap_child",
      workspace_id: "ws_1",
      document_id: "root",
      asset_type: "image",
      file_url: "/mindmap/concept.png",
      thumbnail_url: null,
      bucket: null,
      storage_path: "documents/root/mindmaps/asset_mindmap/concept.png",
      file_name: "concept.png",
      file_size: 512,
      mime_type: "image/png",
      ocr_payload: undefined,
      ocr_strategy: null,
      ocr_text: null,
      ocr_status: null,
      signed_url: null,
      created_at: "2026-04-18T00:00:00Z",
      updated_at: "2026-04-18T00:00:00Z",
    },
  ],
  tableAssets: [
    {
      id: "asset_table",
      workspace_id: "ws_1",
      document_id: "root",
      asset_type: "luckysheet",
      file_url: null,
      thumbnail_url: null,
      bucket: null,
      storage_path: null,
      file_name: "budget.luckysheet",
      file_size: null,
      mime_type: "application/json",
      ocr_payload: undefined,
      ocr_strategy: null,
      ocr_text: null,
      ocr_status: null,
      signed_url: null,
      created_at: "2026-04-18T00:00:00Z",
      updated_at: "2026-04-18T00:00:00Z",
    },
  ],
  mindmapAssets: [
    {
      id: "asset_mindmap",
      workspace_id: "ws_1",
      document_id: "root",
      asset_type: "mindmap",
      file_url: null,
      thumbnail_url: null,
      bucket: null,
      storage_path: null,
      file_name: "mindmap.json",
      file_size: null,
      mime_type: "application/json",
      ocr_payload: undefined,
      ocr_strategy: null,
      ocr_text: null,
      ocr_status: null,
      signed_url: null,
      created_at: "2026-04-18T00:00:00Z",
      updated_at: "2026-04-18T00:00:00Z",
    },
  ],
  mindmapAssetChildren: {
    asset_mindmap: ["asset_mindmap_child"],
  },
};

describe("tree-stream/tree-delta", () => {
  it("支持 upsert_document 重建 sidebar projection", () => {
    const next = applyTreeStreamDelta(baseSidebarData, {
      op: "upsert_document",
      document: {
        id: "leaf",
        workspace_id: "ws_1",
        title: "Leaf",
        parent_id: "child",
        sort_order: 0,
        is_starred: false,
        access_scope: "private",
        is_template: false,
        created_at: "2026-04-18T00:00:00Z",
        updated_at: null,
      },
    });

    expect(next.documents.map((item) => item.id)).toEqual(["root", "child", "leaf"]);
    expect(next.kernelSidebarProjection.items.map((item) => item.nodeId)).toEqual([
      "root",
      "child",
      "leaf",
    ]);
  });

  it("支持 remove_document 级联移除子树", () => {
    const next = applyTreeStreamDelta(baseSidebarData, {
      op: "remove_document",
      documentId: "root",
    });

    expect(next.documents).toEqual([]);
    expect(next.kernelSidebarProjection.items).toEqual([]);
  });

  it("支持对已存在文档做局部 upsert patch，而不丢失原有排序字段", () => {
    const next = applyTreeStreamDelta(baseSidebarData, {
      op: "upsert_document",
      document: {
        id: "child",
        title: "Child Renamed",
        updated_at: "2026-04-18T00:05:00Z",
      },
    });

    expect(next.documents).toHaveLength(2);
    expect(next.documents.find((item) => item.id === "child")).toMatchObject({
      id: "child",
      title: "Child Renamed",
      parent_id: "root",
      sort_order: 1,
      workspace_id: "ws_1",
    });
  });

  it("支持 upsert_documents 批量新增复制出的子树", () => {
    const next = applyTreeStreamDelta(baseSidebarData, {
      op: "upsert_documents",
      upsertDocuments: [
        {
          id: "copy_root",
          workspace_id: "ws_1",
          title: "Copy Root",
          parent_id: null,
          sort_order: 2,
          is_starred: false,
          access_scope: "private",
          is_template: false,
          created_at: "2026-04-18T00:10:00Z",
          updated_at: "2026-04-18T00:10:00Z",
        },
        {
          id: "copy_child",
          workspace_id: "ws_1",
          title: "Copy Child",
          parent_id: "copy_root",
          sort_order: 0,
          is_starred: false,
          access_scope: "private",
          is_template: false,
          created_at: "2026-04-18T00:10:00Z",
          updated_at: "2026-04-18T00:10:00Z",
        },
      ],
    });

    expect(next.documents.map((item) => item.id)).toEqual([
      "root",
      "child",
      "copy_root",
      "copy_child",
    ]);
    expect(next.kernelSidebarProjection.items.map((item) => item.nodeId)).toEqual([
      "root",
      "child",
      "copy_root",
      "copy_child",
    ]);
    expect(
      next.kernelSidebarProjection.items.find((item) => item.nodeId === "copy_child"),
    ).toMatchObject({
      parentNodeId: "copy_root",
      depth: 1,
    });
  });

  it("支持 move_document 细粒度更新父节点与排序字段", () => {
    const next = applyTreeStreamDelta(baseSidebarData, {
      op: "move_document",
      documentId: "child",
      parentId: null,
      sortOrder: 0,
      updatedAt: "2026-04-18T00:10:00Z",
    });

    expect(next.documents.find((item) => item.id === "child")).toMatchObject({
      id: "child",
      parent_id: null,
      sort_order: 0,
      updated_at: "2026-04-18T00:10:00Z",
    });
    expect(next.kernelSidebarProjection.items.map((item) => item.nodeId)).toEqual([
      "root",
      "child",
    ]);
    expect(
      next.kernelSidebarProjection.items.find((item) => item.nodeId === "child"),
    ).toMatchObject({
      parentNodeId: null,
      depth: 0,
    });
  });

  it("支持 replace_sidebar 直接切换到 resync snapshot", () => {
    const next = applyTreeStreamDelta(baseSidebarData, {
      op: "replace_sidebar",
      sidebar: {
        activeWorkspaceId: "ws_1",
        workspaces: [],
        documents: [],
        kernelSidebarProjection: {
          projectionId: "kernel_projection:sidebar_tree:workspace_root",
          projection: "sidebar_tree",
          rootNodeId: null,
          items: [],
          edges: [],
        },
        kernelFileTreeProjection: {
          projectionId: "kernel_projection:file_tree:root",
          projection: "file_tree",
          rootNodeId: null,
          items: [],
          edges: [],
        },
        kernelSidebarTree: [],
        trashedDocuments: [],
        trashedMediaAssets: [],
        trashedMindmapAssets: [],
        trashedTableAssets: [],
        tableAssets: [],
        mindmapDocs: [],
        mindmapAssets: [],
        mindmapAssetChildren: {},
        mediaAssets: [],
      },
    });

    expect(next.documents).toEqual([]);
    expect(next.kernelSidebarProjection.items).toEqual([]);
  });

  it("支持 noop delta 仅推进 cursor，不修改当前 sidebar snapshot", () => {
    const next = applyTreeStreamDelta(baseSidebarData, {
      op: "noop",
    });

    expect(next).toEqual(baseSidebarData);
  });

  it("支持 resync_required delta 仅推进 cursor，等待后续 snapshot/resync", () => {
    const next = applyTreeStreamDelta(baseSidebarData, {
      op: "resync_required",
    });

    expect(next).toEqual(baseSidebarData);
  });

  it("为 page_tree 定义统一 delta 应用边界，并可稳定派生页面行", () => {
    const next = applyTreeStreamDeltaToProjectionState({
      projection: "page_tree",
      base: baseSidebarData,
      event: {
        op: "upsert_document",
        document: {
          id: "leaf",
          workspace_id: "ws_1",
          title: "Leaf",
          parent_id: "child",
          sort_order: 0,
          is_starred: false,
          access_scope: "private",
          is_template: false,
          created_at: "2026-04-18T00:00:00Z",
          updated_at: null,
        },
      },
    });

    expect(next.projection).toBe("page_tree");
    expect(next.documents.map((item) => item.id)).toEqual(["root", "child", "leaf"]);
    expect(next.pageTreeItems.map((item) => item.nodeId)).toEqual(["root", "child", "leaf"]);
    expect(next.pageTreeItems.find((item) => item.nodeId === "leaf")).toMatchObject({
      parentNodeId: "child",
      depth: 2,
      title: "Leaf",
    });
  });

  it("为 file_tree 定义统一 delta 应用边界，并保留 doc/index/asset-folder/asset 行语义", () => {
    const next = applyTreeStreamDeltaToProjectionState({
      projection: "file_tree",
      base: fileTreeProjectionBase,
      event: {
        op: "replace_documents",
        documents: [...fileTreeProjectionBase.documents],
      },
    });

    expect(next.projection).toBe("file_tree");
    expect(next.fileTreeItems.map((item) => item.rowKind)).toEqual(
      expect.arrayContaining(["document", "index", "asset_folder", "asset"]),
    );
    expect(
      next.fileTreeItems.find((item) => item.resourceMeta.assetId === "asset_mindmap"),
    ).toMatchObject({
      rowKind: "asset_folder",
      iconHint: "mindmap",
      resourceMeta: expect.objectContaining({
        resourceKind: "mindmap",
        assetKind: "mindmap",
      }),
    });
    expect(
      next.fileTreeItems.find((item) => item.resourceMeta.assetId === "asset_pdf"),
    ).toMatchObject({
      rowKind: "asset",
      iconHint: "pdf",
      resourceMeta: expect.objectContaining({
        resourceKind: "pdf",
        assetKind: "pdf",
      }),
    });
    expect(
      next.fileTreeItems.find((item) => item.resourceMeta.assetId === "asset_book"),
    ).toMatchObject({
      rowKind: "asset",
      iconHint: "book",
      resourceMeta: expect.objectContaining({
        resourceKind: "book",
        assetKind: "book",
      }),
    });
    expect(
      next.fileTreeItems.find((item) => item.resourceMeta.assetId === "asset_table"),
    ).toMatchObject({
      rowKind: "asset",
      iconHint: "table",
      resourceMeta: expect.objectContaining({
        resourceKind: "table",
        assetKind: "table",
      }),
    });
  });

  it("file_tree fixture 覆盖搜索 hardening 需要的 index、asset-folder、mindmap child、book 与 pdf 资源类型", () => {
    const next = applyTreeStreamDeltaToProjectionState({
      projection: "file_tree",
      base: fileTreeProjectionBase,
      event: {
        op: "replace_documents",
        documents: [...fileTreeProjectionBase.documents],
      },
    });
    const rowById = new Map(next.fileTreeItems.map((item) => [item.rowId, item]));

    expect(rowById.get("index:root")).toMatchObject({
      rowKind: "index",
      title: "index.md",
      resourceMeta: expect.objectContaining({
        resourceKind: "index",
        documentId: "root",
      }),
    });
    expect(rowById.get("asset-folder:asset_mindmap")).toMatchObject({
      rowKind: "asset_folder",
      title: "mindmap",
      resourceMeta: expect.objectContaining({
        resourceKind: "mindmap",
        assetKind: "mindmap",
      }),
    });
    expect(rowById.get("asset:asset_mindmap_child")).toMatchObject({
      rowKind: "asset",
      parentNodeId: "asset-folder:asset_mindmap",
      resourceMeta: expect.objectContaining({
        resourceKind: "asset",
        assetKind: "image",
      }),
    });
    expect(rowById.get("asset:asset_book")).toMatchObject({
      rowKind: "asset",
      iconHint: "book",
      resourceMeta: expect.objectContaining({
        resourceKind: "book",
        assetKind: "book",
      }),
    });
    expect(rowById.get("asset:asset_pdf")).toMatchObject({
      rowKind: "asset",
      iconHint: "pdf",
      resourceMeta: expect.objectContaining({
        resourceKind: "pdf",
        assetKind: "pdf",
      }),
    });
  });

  it("支持 upsert_assets 更新资源归属并重建 file_tree projection", () => {
    const next = applyTreeStreamDeltaToProjectionState({
      projection: "file_tree",
      base: fileTreeProjectionBase,
      event: {
        op: "upsert_assets",
        upsertAssets: [
          {
            id: "asset_pdf",
            workspace_id: "ws_1",
            document_id: "child",
            asset_type: "file",
            file_url: "/manual.pdf",
            thumbnail_url: "/manual.pdf",
            bucket: null,
            storage_path: "documents/child/manual.pdf",
            file_name: "manual.pdf",
            file_size: 1024,
            mime_type: "application/pdf",
            ocr_text: null,
            ocr_status: null,
            created_at: "2026-04-18T00:00:00Z",
            updated_at: "2026-04-26T00:00:00Z",
          },
        ],
      },
    });

    expect(next.sidebar.mediaAssets?.find((asset) => asset.id === "asset_pdf")).toMatchObject({
      document_id: "child",
      updated_at: "2026-04-26T00:00:00Z",
    });
    expect(
      next.fileTreeItems.find((item) => item.resourceMeta.assetId === "asset_pdf"),
    ).toMatchObject({
      parentNodeId: "child",
      resourceMeta: expect.objectContaining({
        documentId: "child",
        resourceKind: "pdf",
      }),
    });
  });
});
