import { beforeEach, describe, expect, it, vi } from "vitest";
import {
  FILE_TREE_SEARCH_MAX_RESULTS,
  buildFileTreeProjectionSearchRequestMeta,
  fetchKernelFileTreeProjection,
} from "./projection-client";

describe("fetchKernelFileTreeProjection", () => {
  beforeEach(() => {
    vi.restoreAllMocks();
  });

  it("通过 3000 同源 file_tree projection endpoint 请求 Rust 搜索 projection", async () => {
    const fetchMock = vi.fn().mockResolvedValue(
      new Response(
        JSON.stringify({
          ok: true,
          result: {
            projectionId: "kernel_projection:file_tree:page_root",
            projection: "file_tree",
            rootNodeId: "page_root",
            items: [{ rowId: "asset:table_1" }],
            edges: [],
            meta: {
              search: {
                indexingVisibility: {
                  schema: "mnote.file_tree.indexing_visibility",
                  schemaVersion: 1,
                  source: "kernel.project_view",
                  status: "visible",
                  requestKey: "page_root:预算",
                  indexedResourceKinds: ["document", "index", "asset"],
                  visibleResourceKinds: ["document", "index", "asset"],
                  metrics: {
                    visibleRows: 1,
                    visibleEdges: 0,
                  },
                },
              },
            },
          },
        }),
        { status: 200 },
      ),
    );
    vi.stubGlobal("fetch", fetchMock);

    const result = await fetchKernelFileTreeProjection({
      workspaceId: "ws_1",
      rootNodeId: "page_root",
      depth: 3,
      query: "  预算  ",
      maxResults: 12,
    });

    expect(fetchMock).toHaveBeenCalledWith(
      "/api/tree/projections/file?workspaceId=ws_1&rootNodeId=page_root&depth=3&query=%E9%A2%84%E7%AE%97&maxResults=12",
      expect.objectContaining({
        method: "GET",
        credentials: "include",
      }),
    );
    expect(result.items.map((item) => item.rowId)).toEqual(["asset:table_1"]);
    expect(result.meta?.search?.indexingVisibility).toMatchObject({
      schema: "mnote.file_tree.indexing_visibility",
      status: "visible",
      requestKey: "page_root:预算",
    });
  });

  it("固定 file_tree 搜索语义边界：命中数先截断，祖先补全不计入 maxResults", () => {
    expect(FILE_TREE_SEARCH_MAX_RESULTS).toBe(80);
    expect(
      buildFileTreeProjectionSearchRequestMeta({
        workspaceId: "ws_1",
        query: "  rust  ",
        maxResults: 500,
      }),
    ).toEqual({
      query: "rust",
      maxResults: 80,
      maxResultsRule: "matches_only_before_ancestor_completion",
      ancestorCompletion: "include_all_ancestors_after_match_truncation",
      ordering: "kernel_file_tree_preorder",
      emptyStateText: "没有匹配结果",
      asyncVisibility: {
        source: "kernel.project_view",
        requestKey: "ws_1:rust",
      },
      coveredResourceKinds: ["index", "asset", "asset_folder", "mindmap", "book", "pdf"],
    });
  });

  it("搜索请求会把 maxResults 限制在 sidebar 使用的稳定上限内", async () => {
    const fetchMock = vi.fn().mockResolvedValue(
      new Response(
        JSON.stringify({
          ok: true,
          result: {
            projectionId: "kernel_projection:file_tree:root",
            projection: "file_tree",
            rootNodeId: null,
            items: [],
            edges: [],
          },
        }),
        { status: 200 },
      ),
    );
    vi.stubGlobal("fetch", fetchMock);

    await fetchKernelFileTreeProjection({
      workspaceId: "ws_1",
      query: "rust",
      maxResults: 500,
    });

    expect(fetchMock).toHaveBeenCalledWith(
      "/api/tree/projections/file?workspaceId=ws_1&query=rust&maxResults=80",
      expect.anything(),
    );
  });

  it("失败时透出服务端错误消息", async () => {
    vi.stubGlobal(
      "fetch",
      vi.fn().mockResolvedValue(
        new Response(JSON.stringify({ error: "获取 projection 失败" }), { status: 502 }),
      ),
    );

    await expect(fetchKernelFileTreeProjection({ workspaceId: "ws_1" })).rejects.toThrow(
      "获取 projection 失败",
    );
  });
});
