import { describe, expect, it, vi } from "vitest";
import type { ConvexHttpClient } from "convex/browser";

vi.mock("server-only", () => ({}), { virtual: true });

vi.mock("@/lib/auth/authContext", () => ({
  HttpError: class HttpError extends Error {
    status: number;

    constructor(status: number, message: string) {
      super(message);
      this.status = status;
    }
  },
  requireAuthContext: vi.fn(async () => ({
    userId: "user_1",
  })),
}));

vi.mock("@/lib/api-utils", () => ({
  apiErrorResponse: vi.fn((message: string, status = 500, details?: unknown) => ({
    message,
    status,
    details,
  })),
}));

vi.mock("@/lib/convex/api", () => ({
  api: {
    documents: {
      getMeta: "documents:getMeta",
    },
  },
}));

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

vi.mock("@/lib/documents/bridge-log", () => ({
  recordRustBridgeCommandArtifacts: vi.fn(),
}));

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

vi.mock("@/lib/blocks", () => ({
  findBlockInTree: vi.fn(),
  getBlocksFromDocumentContent: vi.fn(),
  removeBlockSubtree: vi.fn(),
  replaceBlockInTree: vi.fn(),
  withBlocksWrittenBack: vi.fn(),
}));

describe("blocks/block-command-adapter", () => {
  it("executeBlockPatchCommand 应通过 Rust artifact writer 记录外层 blocks.patch", async () => {
    const { getAuthedConvexClient } = await import("@/lib/convex/route");
    const { recordRustBridgeCommandArtifacts } = await import("@/lib/documents/bridge-log");
    const {
      resolveRustBridgeCommandPlan,
      resolveRustBridgeQueryPlan,
      executeRustBridgeQueryTransport,
      executeRustBridgeMutationTransport,
    } = await import("@/lib/documents/rust-runtime");
    const {
      getBlocksFromDocumentContent,
      replaceBlockInTree,
      withBlocksWrittenBack,
    } = await import("@/lib/blocks");
    const { executeBlockPatchCommand } = await import("./block-command-adapter");

    const query = vi.fn(async (name: string) => {
      if (name === "documents:getMeta") {
        return {
          id: "doc_1",
          workspace_id: "ws_1",
          embed_default_block_id: null,
        };
      }
      return null;
    });

    vi.mocked(getAuthedConvexClient).mockResolvedValue({
      auth: { userId: "user_1" },
      client: {
        query,
        mutation: vi.fn(),
      } as unknown as ConvexHttpClient,
    });

    vi.mocked(resolveRustBridgeQueryPlan).mockResolvedValue({
      kind: "query",
      queryName: "documents.content.get",
      functionName: "documents:getContent",
      workspaceId: "ws_1",
      requestId: "req_1",
      traceId: "trace_1",
      actorId: "user_1",
      payloadJson: "{\"kind\":\"query\"}",
      argsJson: {
        id: "doc_1",
      },
    });
    vi.mocked(executeRustBridgeQueryTransport).mockResolvedValue({
      content: { type: "doc" },
      revision: 7,
      conflict_detection_key: "doc_1:7",
    });
    vi.mocked(getBlocksFromDocumentContent).mockReturnValue([{ id: "blk_1" }] as never);
    vi.mocked(replaceBlockInTree).mockReturnValue({
      ok: true,
      nextBlocks: [{ id: "blk_1", type: "paragraph" }],
    } as never);
    vi.mocked(withBlocksWrittenBack).mockReturnValue({
      type: "doc",
      content: [{ id: "blk_1", type: "paragraph" }],
    } as never);
    vi.mocked(resolveRustBridgeCommandPlan)
      .mockResolvedValueOnce({
        kind: "command",
        commandName: "blocks.patch",
        commandId: "cmd_block_patch_1",
        functionName: "documents:updateContent",
        workspaceId: "ws_1",
        requestId: "req_1",
        traceId: "trace_1",
        actorId: "user_1",
        idempotencyKey: "idem_1",
        payloadJson: "{\"kind\":\"command\"}",
        argsJson: {
          id: "doc_1",
          blockId: "blk_1",
          nextBlock: { id: "blk_1", type: "paragraph" },
        },
      })
      .mockResolvedValueOnce({
        kind: "command",
        commandName: "documents.save",
        commandId: "cmd_save_1",
        functionName: "documents:updateContent",
        workspaceId: "ws_1",
        requestId: "req_1",
        traceId: "trace_1",
        actorId: "user_1",
        idempotencyKey: "idem_1",
        payloadJson: "{\"kind\":\"command\"}",
        argsJson: {
          id: "doc_1",
          content: { type: "doc", content: [{ id: "blk_1", type: "paragraph" }] },
          expectedRevision: 7,
          conflictDetectionKey: "doc_1:7",
        },
      });
    vi.mocked(executeRustBridgeMutationTransport).mockResolvedValue({
      revision: 8,
      conflict_detection_key: "doc_1:8",
    });

    const result = await executeBlockPatchCommand({
      request: new Request("http://127.0.0.1:3000/api/blocks/patch", {
        method: "POST",
      }),
      sourceDocumentId: "doc_1",
      workspaceId: "ws_1",
      blockId: "blk_1",
      nextBlock: {
        id: "blk_1",
        type: "paragraph",
      },
    });

    expect(result.commandName).toBe("blocks.patch");
    expect(result.result).toEqual({
      revision: 8,
      conflict_detection_key: "doc_1:8",
    });
    expect(vi.mocked(resolveRustBridgeCommandPlan).mock.calls).toHaveLength(2);
    expect(recordRustBridgeCommandArtifacts).toHaveBeenCalledTimes(1);
    expect(recordRustBridgeCommandArtifacts).toHaveBeenCalledWith({
      client: expect.any(Object),
      context: expect.objectContaining({
        workspaceId: "ws_1",
      }),
      envelope: expect.objectContaining({
        name: "blocks.patch",
      }),
      plan: expect.objectContaining({
        commandName: "blocks.patch",
      }),
      result: {
        revision: 8,
        conflict_detection_key: "doc_1:8",
      },
    });
  });
});
