import { beforeEach, describe, expect, it, vi } from "vitest";

const mockGetAuthedConvexClient = vi.fn();
const mockBuildDocumentBridgeContext = vi.fn();
const mockBuildDocumentQueryEnvelope = vi.fn();
const mockExecuteRustBridgeQueryTransport = vi.fn();
const mockResolveRustBridgeQueryPlan = vi.fn();
const mockResolveKernelFileTreeProjection = vi.fn();
const mockAttachKernelFileTreeProjection = vi.fn((input: { dataset: unknown; projection: unknown }) => ({
  ...(input.dataset as Record<string, unknown>),
  kernel_file_tree_projection: input.projection,
}));
const mockDocumentBridgeErrorResponse = vi.fn((error: unknown) =>
  Response.json(
    {
      error: error instanceof Error ? error.message : String(error),
    },
    { status: 500 },
  ),
);

vi.mock("@/lib/convex/route", () => ({
  getAuthedConvexClient: mockGetAuthedConvexClient,
}));

vi.mock("@/lib/documents/bridge", () => ({
  buildDocumentBridgeContext: mockBuildDocumentBridgeContext,
  buildDocumentQueryEnvelope: mockBuildDocumentQueryEnvelope,
  documentBridgeErrorResponse: mockDocumentBridgeErrorResponse,
}));

vi.mock("@/lib/documents/rust-runtime", () => ({
  executeRustBridgeQueryTransport: mockExecuteRustBridgeQueryTransport,
  resolveRustBridgeQueryPlan: mockResolveRustBridgeQueryPlan,
}));

vi.mock("@/lib/server/kernel-file-tree", () => ({
  resolveKernelFileTreeProjection: (...args: unknown[]) => mockResolveKernelFileTreeProjection(...args),
  attachKernelFileTreeProjection: (...args: unknown[]) => mockAttachKernelFileTreeProjection(...args),
}));

describe("/api/mnote-web/stream route", () => {
  beforeEach(() => {
    mockGetAuthedConvexClient.mockReset();
    mockBuildDocumentBridgeContext.mockReset();
    mockBuildDocumentQueryEnvelope.mockReset();
    mockExecuteRustBridgeQueryTransport.mockReset();
    mockResolveRustBridgeQueryPlan.mockReset();
    mockResolveKernelFileTreeProjection.mockReset();
    mockAttachKernelFileTreeProjection.mockClear();
    mockDocumentBridgeErrorResponse.mockClear();
  });

  it("直接在 3000 内生成 snapshot SSE，不再回源 mnote-web", async () => {
    mockGetAuthedConvexClient.mockResolvedValue({
      auth: {
        userId: "user_1",
        email: "dev@example.com",
        name: "开发用户",
      },
      client: { query: vi.fn() },
    });
    mockBuildDocumentBridgeContext.mockResolvedValue({
      requestId: "req_stream_1",
      traceId: "trace_stream_1",
      workspaceId: "ws_1",
    });
    mockBuildDocumentQueryEnvelope
      .mockReturnValueOnce({
        name: "sidebar.dataset.list",
        payload: { workspaceId: "ws_1" },
      })
      .mockReturnValueOnce({
        name: "bridge.workspace.overview",
        payload: {
          workspaceId: "ws_1",
          limit: 20,
          cursor: null,
          commandStatus: null,
          eventStatus: null,
          targetPageId: null,
          targetBlockId: null,
          aggregateType: null,
          aggregateId: null,
        },
      });
    mockResolveRustBridgeQueryPlan
      .mockResolvedValueOnce({ kind: "query", functionName: "sidebar:datasetList", argsJson: { workspaceId: "ws_1" } })
      .mockResolvedValueOnce({ kind: "query", functionName: "bridgeLogs:listWorkspaceOverview", argsJson: { workspaceId: "ws_1" } });
    mockExecuteRustBridgeQueryTransport
      .mockResolvedValueOnce({
        active_workspace_id: "ws_1",
        workspaces: [],
        documents: [
          {
            id: "page_root",
            workspace_id: "ws_1",
            title: "工作区首页",
            parent_id: null,
            sort_order: 0,
            is_starred: true,
            is_template: false,
            created_at: "2026-04-22T00:00:00Z",
            updated_at: "2026-04-22T00:00:00Z",
          },
        ],
        trashed_documents: [],
        media_assets: [],
        trashed_media_assets: [],
        mindmap_assets: [],
        trashed_mindmap_assets: [],
        table_assets: [],
        trashed_table_assets: [],
        mindmap_docs: [],
        mindmap_asset_children: {},
      })
      .mockResolvedValueOnce({
        workspace_id: "ws_1",
        command_logs: [],
        domain_events: [],
        counts: { command_logs: 0, domain_events: 0 },
        filters: null,
        next_cursor: null,
        has_more: false,
        generated_at: "2026-04-22T00:00:00Z",
      });
    mockResolveKernelFileTreeProjection.mockResolvedValue({
      projectionId: "kernel_projection:file_tree:root",
      projection: "file_tree",
      rootNodeId: null,
      items: [],
      edges: [],
    });

    const { GET } = await import("./route");
    const response = await GET(
      new Request("http://127.0.0.1:3000/api/mnote-web/stream?workspaceId=ws_1", {
        method: "GET",
        headers: { cookie: "a=1" },
      }),
    );

    expect(response.status).toBe(200);
    expect(response.headers.get("content-type")).toContain("text/event-stream");
    const text = await response.text();
    expect(text).toContain("event: snapshot");
    expect(text).toContain('"kind":"snapshot"');
    expect(text).toContain('"projection":"sidebar_tree"');
    expect(text).toContain('"workspaceId":"ws_1"');
    expect(text).toContain('"activeWorkspaceId":"ws_1"');
    expect(text).toContain('"kernelFileTreeProjection"');
    expect(mockResolveRustBridgeQueryPlan).toHaveBeenCalledTimes(2);
    expect(mockExecuteRustBridgeQueryTransport).toHaveBeenCalledTimes(2);
    expect(mockResolveKernelFileTreeProjection).toHaveBeenCalledTimes(1);
  });

  it("应把请求 cursor 继续透传到 overview query 和 snapshot envelope", async () => {
    mockGetAuthedConvexClient.mockResolvedValue({
      auth: {
        userId: "user_1",
        email: "dev@example.com",
        name: "开发用户",
      },
      client: { query: vi.fn() },
    });
    mockBuildDocumentBridgeContext.mockResolvedValue({
      requestId: "req_stream_2",
      traceId: "trace_stream_2",
      workspaceId: "ws_1",
    });
    mockBuildDocumentQueryEnvelope
      .mockReturnValueOnce({
        name: "sidebar.dataset.list",
        payload: { workspaceId: "ws_1" },
      })
      .mockReturnValueOnce({
        name: "bridge.workspace.overview",
        payload: {
          workspaceId: "ws_1",
          limit: 20,
          cursor: "evt_9",
          commandStatus: null,
          eventStatus: null,
          targetPageId: null,
          targetBlockId: null,
          aggregateType: null,
          aggregateId: null,
        },
      });
    mockResolveRustBridgeQueryPlan
      .mockResolvedValueOnce({ kind: "query", functionName: "sidebar:datasetList", argsJson: { workspaceId: "ws_1" } })
      .mockResolvedValueOnce({
        kind: "query",
        functionName: "bridgeLogs:listWorkspaceOverview",
        argsJson: { workspaceId: "ws_1", cursor: "evt_9" },
      });
    mockExecuteRustBridgeQueryTransport
      .mockResolvedValueOnce({
        active_workspace_id: "ws_1",
        workspaces: [],
        documents: [],
        trashed_documents: [],
        media_assets: [],
        trashed_media_assets: [],
        mindmap_assets: [],
        trashed_mindmap_assets: [],
        table_assets: [],
        trashed_table_assets: [],
        mindmap_docs: [],
        mindmap_asset_children: {},
      })
      .mockResolvedValueOnce({
        workspace_id: "ws_1",
        command_logs: [],
        domain_events: [],
        counts: { command_logs: 0, domain_events: 0 },
        filters: null,
        next_cursor: "evt_10",
        has_more: true,
        generated_at: "2026-04-22T00:00:00Z",
      });
    mockResolveKernelFileTreeProjection.mockResolvedValue({
      projectionId: "kernel_projection:file_tree:root",
      projection: "file_tree",
      rootNodeId: null,
      items: [],
      edges: [],
    });

    const { GET } = await import("./route");
    const response = await GET(
      new Request("http://127.0.0.1:3000/api/mnote-web/stream?workspaceId=ws_1&cursor=evt_9", {
        method: "GET",
      }),
    );

    expect(response.status).toBe(200);
    const text = await response.text();
    expect(text).toContain('"cursor":"evt_9"');
    expect(mockBuildDocumentQueryEnvelope).toHaveBeenNthCalledWith(
      2,
      expect.objectContaining({
        payload: expect.objectContaining({
          workspaceId: "ws_1",
          cursor: "evt_9",
        }),
      }),
    );
  });
});
