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

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,
  })),
}));

import {
  buildDocumentCommandEnvelope,
  type BridgeContext,
} from "@/lib/documents/bridge";
import { buildDocumentSavePayload } from "@/lib/documents/save-contract";
import { executePageWriteBridgeCommand } from "@/lib/documents/page-write-command-adapter";

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

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

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

const mockContext: BridgeContext = {
  deploymentId: null,
  projectId: null,
  workspaceId: "ws_1",
  requestId: "req_1",
  traceId: "trace_1",
  actor: {
    actorType: "user",
    actorId: "user_1",
    sessionId: "sess_1",
  },
  source: {
    channel: "next-route",
    client: "vitest",
  },
  tenantId: null,
  authToken: null,
  idempotencyKey: "idem_1",
  validateOnly: false,
  dryRun: false,
};

describe("page-write-command-adapter", () => {
  it("标题命令应走 rust bridge transport", async () => {
    const { getAuthedConvexClient } = await import("@/lib/convex/route");
    const {
      resolveRustBridgeCommandPlan,
      executeRustBridgeMutationTransport,
    } = await import("@/lib/documents/rust-runtime");

    vi.mocked(getAuthedConvexClient).mockResolvedValue({
      auth: { userId: "user_1" },
      client: { mutation: vi.fn() } as unknown as ConvexHttpClient,
    });
    vi.mocked(resolveRustBridgeCommandPlan).mockResolvedValue({
      kind: "command",
      commandName: "page.head.updateTitle",
      commandId: "cmd_title_1",
      functionName: "documents:updateTitle",
      workspaceId: "ws_1",
      requestId: "req_1",
      traceId: "trace_1",
      actorId: "user_1",
      idempotencyKey: "idem_1",
      payloadJson: "{\"kind\":\"command\"}",
      argsJson: {
        id: "doc_1",
        title: "新标题",
      },
    });
    vi.mocked(executeRustBridgeMutationTransport).mockResolvedValue({ ok: true });

    const result = await executePageWriteBridgeCommand({
      context: mockContext,
      envelope: buildDocumentCommandEnvelope({
        name: "page.head.updateTitle",
        payload: {
          documentId: "doc_1",
          workspaceId: "ws_1",
          title: "新标题",
        },
        context: mockContext,
        target: { workspaceId: "ws_1", pageId: "doc_1" },
      }),
    });

    expect(resolveRustBridgeCommandPlan).toHaveBeenCalledWith({
      context: mockContext,
      envelope: expect.objectContaining({
        name: "page.head.updateTitle",
      }),
    });
    expect(result.commandName).toBe("page.head.updateTitle");
    expect(result.revision).toBeNull();
    expect(result.conflictDetectionKey).toBeNull();
  });

  it("页面设置命令应走 bridge mutation request", async () => {
    const mutation = vi.fn().mockResolvedValue({ ok: true });
    const { getAuthedConvexClient } = await import("@/lib/convex/route");
    vi.mocked(getAuthedConvexClient).mockResolvedValue({
      auth: { userId: "user_1" },
      client: { mutation } as unknown as ConvexHttpClient,
    });

    const result = await executePageWriteBridgeCommand({
      context: mockContext,
      envelope: buildDocumentCommandEnvelope({
        name: "page.layout.updateOptions",
        payload: {
          documentId: "doc_1",
          workspaceId: "ws_1",
          options: {
            showToc: true,
            layoutDensity: "compact",
            embedDefaultBlockId: null,
          },
        },
        context: mockContext,
        target: { workspaceId: "ws_1", pageId: "doc_1" },
      }),
    });

    expect(mutation).toHaveBeenCalledTimes(1);
    expect(result.commandName).toBe("page.layout.updateOptions");
    expect(result.revision).toBeNull();
    expect(result.conflictDetectionKey).toBeNull();
  });

  it("正文保存命令应返回 revision 与 conflictDetectionKey", async () => {
    const { getAuthedConvexClient } = await import("@/lib/convex/route");
    const {
      resolveRustBridgeCommandPlan,
      executeRustBridgeMutationTransport,
    } = await import("@/lib/documents/rust-runtime");

    vi.mocked(getAuthedConvexClient).mockResolvedValue({
      auth: { userId: "user_1" },
      client: { mutation: vi.fn() } as unknown as ConvexHttpClient,
    });
    vi.mocked(resolveRustBridgeCommandPlan).mockResolvedValue({
      kind: "command",
      commandName: "page.body.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: [{ id: "block_1" }],
        expectedRevision: 7,
        conflictDetectionKey: "conflict_1",
      },
    });
    vi.mocked(executeRustBridgeMutationTransport).mockResolvedValue({
      revision: 8,
      conflict_detection_key: "conflict_2",
    });

    const payload = buildDocumentSavePayload({
      documentId: "doc_1",
      workspaceId: "ws_1",
      revision: 7,
      content: [{ id: "block_1" }],
      conflictDetectionKey: "conflict_1",
    });

    const result = await executePageWriteBridgeCommand({
      context: mockContext,
      envelope: buildDocumentCommandEnvelope({
        name: "page.body.save",
        payload,
        context: mockContext,
        target: { workspaceId: "ws_1", pageId: "doc_1" },
      }),
    });

    expect(result.commandName).toBe("page.body.save");
    expect(result.revision).toBe(8);
    expect(result.conflictDetectionKey).toBe("conflict_2");
  });
});
