import { describe, expect, it, vi } from "vitest";
import { buildParentById } from "@/lib/file-tree/dnd";
import { getAuthedConvexClient } from "@/lib/convex/route";
import {
  buildDocumentBridgeContextWithActor,
  buildDocumentCommandEnvelope,
  documentBridgeErrorResponse,
} from "@/lib/documents/bridge";
import {
  recordBridgeCommandFailureArtifacts,
} from "@/lib/documents/bridge-log";
import {
  executeRustBridgeMutationTransport,
  resolveRustBridgeCommandPlan,
} from "@/lib/documents/rust-runtime";

vi.mock("next/server", () => ({
  NextResponse: {
    json: (body: unknown, init?: { status?: number }) => ({
      body,
      status: init?.status ?? 200,
    }),
  },
}), { virtual: true });

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

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

vi.mock("@/lib/documents/bridge", () => ({
  buildDocumentBridgeContextWithActor: vi.fn(),
  buildDocumentCommandEnvelope: vi.fn(),
  documentBridgeErrorResponse: vi.fn(),
}));

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

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

vi.mock("@/lib/server/local-paths", () => ({
  getDocumentsBaseDir: () => "/tmp/mnote-vitest-documents",
}));

const {
  handleDocumentMoveRequest,
  normalizeDocumentCopyTreePayload,
  normalizeDocumentMovePayload,
  resolveSubtreeMoveLegality,
} = await import("./page-lifecycle-command-adapter");

describe("page-lifecycle-command-adapter", () => {
  it("归一化 move payload 的 targetParentId 与 sortOrder", () => {
    expect(
      normalizeDocumentMovePayload({
        documentId: "  doc_1 ",
        parentId: " parent_1 ",
        position: 2.8,
      }),
    ).toEqual({
      documentId: "doc_1",
      parentId: "parent_1",
      sortOrder: 2,
    });

    expect(
      normalizeDocumentMovePayload({
        documentId: "doc_1",
        parentId: " ",
        position: Number.NaN,
      }),
    ).toEqual({
      documentId: "doc_1",
      parentId: null,
      sortOrder: 0,
    });
  });

  it("归一化 copy_tree payload 并去重 source ids", () => {
    expect(
      normalizeDocumentCopyTreePayload({
        targetParentId: " parent_1 ",
        items: [
          { documentId: " doc_a ", recursive: true },
          { documentId: "doc_b", recursive: false },
          { documentId: "doc_a", recursive: false },
          { documentId: " ", recursive: true },
        ],
      }),
    ).toEqual({
      targetParentId: "parent_1",
      items: [
        { documentId: "doc_a", recursive: true },
        { documentId: "doc_b", recursive: false },
        { documentId: "doc_a", recursive: false },
      ],
      sourceIds: ["doc_a", "doc_b"],
    });
  });

  it("在 subtree move legality 中先收敛顶层 source，再拦截自拖拽/拖入后代", () => {
    const parentById = buildParentById([
      { id: "a", parentId: null },
      { id: "b", parentId: "a" },
      { id: "c", parentId: "b" },
      { id: "d", parentId: null },
    ]);

    expect(
      resolveSubtreeMoveLegality({
        sourceDocIds: ["a", "b", "c", "d"],
        targetParentId: "b",
        parentById,
      }),
    ).toEqual({
      sourceDocIds: ["a", "d"],
      targetParentId: "b",
      isInvalid: true,
    });

    expect(
      resolveSubtreeMoveLegality({
        sourceDocIds: ["b", "c"],
        targetParentId: "a",
        parentById,
      }),
    ).toEqual({
      sourceDocIds: ["b"],
      targetParentId: "a",
      isInvalid: false,
    });
  });

  it("documents.move 应把 movePreflight 透传给 Rust plan", async () => {
    const client = {
      query: vi.fn(async (name: string, args: { id: string }) => {
        if (name !== "documents:getMeta") {
          return null;
        }
        if (args.id === "doc_1") {
          return {
            id: "doc_1",
            workspace_id: "ws_1",
            parent_id: null,
          };
        }
        if (args.id === "child_1") {
          return {
            id: "child_1",
            workspace_id: "ws_1",
            parent_id: "doc_1",
          };
        }
        return null;
      }),
      mutation: vi.fn(),
    };
    vi.mocked(getAuthedConvexClient).mockResolvedValue({
      auth: {
        userId: "user_1",
        email: "dev@example.com",
        name: "开发用户",
      },
      client: client as never,
    });
    vi.mocked(buildDocumentBridgeContextWithActor).mockReturnValue({
      deploymentId: null,
      projectId: null,
      workspaceId: "ws_1",
      requestId: "req_move_1",
      traceId: "trace_move_1",
      actor: {
        actorType: "user",
        actorId: "user_1",
        sessionId: null,
      },
      source: {
        channel: "next-route",
        client: "wolai-frontend",
      },
      tenantId: null,
      authToken: null,
      idempotencyKey: null,
      validateOnly: false,
      dryRun: false,
    });
    vi.mocked(buildDocumentCommandEnvelope).mockImplementation((input: unknown) => input as never);
    vi.mocked(resolveRustBridgeCommandPlan).mockResolvedValue({
      kind: "command",
      commandName: "documents.move",
      commandId: "cmd_move_1",
      functionName: "documents:move",
      workspaceId: "ws_1",
      requestId: "req_move_1",
      traceId: "trace_move_1",
      actorId: "user_1",
      idempotencyKey: null,
      payloadJson: "{\"kind\":\"command\"}",
      argsJson: {
        id: "doc_1",
        parentId: "child_1",
        sortOrder: 0,
      },
    });
    vi.mocked(executeRustBridgeMutationTransport).mockResolvedValue({ ok: true } as never);

    const response = await handleDocumentMoveRequest(
      new Request("http://127.0.0.1:3000/api/documents/move", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          documentId: "doc_1",
          parentId: "child_1",
          position: 0,
        }),
      }),
    );

    expect(response.status).toBe(200);
    expect(resolveRustBridgeCommandPlan).toHaveBeenCalledWith({
      context: expect.objectContaining({
        requestId: "req_move_1",
      }),
      envelope: expect.objectContaining({
        name: "documents.move",
        preflightData: {
          sourceDocument: {
            id: "doc_1",
            workspaceId: "ws_1",
            parentId: null,
          },
          targetParentDocument: {
            id: "child_1",
            workspaceId: "ws_1",
            parentId: "doc_1",
          },
          targetAncestorIds: ["doc_1"],
        },
        payload: {
          documentId: "doc_1",
          parentId: "child_1",
          sortOrder: 0,
          movePreflight: {
            sourceDocument: {
              id: "doc_1",
              workspaceId: "ws_1",
              parentId: null,
            },
            targetParentDocument: {
              id: "child_1",
              workspaceId: "ws_1",
              parentId: "doc_1",
            },
            targetAncestorIds: ["doc_1"],
          },
        },
      }),
    });
  });

  it("documents.move 失败时记录的 failure artifact 仍应保持 move envelope", async () => {
    const client = {
      query: vi.fn(async (name: string, args: { id: string }) => {
        if (name !== "documents:getMeta") {
          return null;
        }
        if (args.id === "doc_1") {
          return {
            id: "doc_1",
            workspace_id: "ws_1",
            parent_id: null,
          };
        }
        if (args.id === "child_1") {
          return {
            id: "child_1",
            workspace_id: "ws_1",
            parent_id: "doc_1",
          };
        }
        return null;
      }),
      mutation: vi.fn(),
    };
    vi.mocked(getAuthedConvexClient).mockResolvedValue({
      auth: {
        userId: "user_1",
        email: "dev@example.com",
        name: "开发用户",
      },
      client: client as never,
    });
    vi.mocked(buildDocumentBridgeContextWithActor).mockReturnValue({
      deploymentId: null,
      projectId: null,
      workspaceId: "ws_1",
      requestId: "req_move_2",
      traceId: "trace_move_2",
      actor: {
        actorType: "user",
        actorId: "user_1",
        sessionId: null,
      },
      source: {
        channel: "next-route",
        client: "wolai-frontend",
      },
      tenantId: null,
      authToken: null,
      idempotencyKey: null,
      validateOnly: false,
      dryRun: false,
    });
    vi.mocked(buildDocumentCommandEnvelope).mockImplementation((input: unknown) => input as never);
    vi.mocked(resolveRustBridgeCommandPlan).mockRejectedValue(new Error("move failed"));
    vi.mocked(documentBridgeErrorResponse).mockImplementation((error: unknown) => ({
      body: { error: error instanceof Error ? error.message : String(error) },
      status: 500,
    }) as never);

    const response = await handleDocumentMoveRequest(
      new Request("http://127.0.0.1:3000/api/documents/move", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          documentId: "doc_1",
          parentId: "child_1",
          position: 0,
        }),
      }),
    );

    expect(response.status).toBe(500);
    expect(recordBridgeCommandFailureArtifacts).toHaveBeenCalledWith(
      expect.objectContaining({
        envelope: expect.objectContaining({
          name: "documents.move",
          preflightData: {
            sourceDocument: {
              id: "doc_1",
              workspaceId: "ws_1",
              parentId: null,
            },
            targetParentDocument: {
              id: "child_1",
              workspaceId: "ws_1",
              parentId: "doc_1",
            },
            targetAncestorIds: ["doc_1"],
          },
          payload: {
            documentId: "doc_1",
            parentId: "child_1",
            sortOrder: 0,
            movePreflight: {
              sourceDocument: {
                id: "doc_1",
                workspaceId: "ws_1",
                parentId: null,
              },
              targetParentDocument: {
                id: "child_1",
                workspaceId: "ws_1",
                parentId: "doc_1",
              },
              targetAncestorIds: ["doc_1"],
            },
          },
        }),
      }),
    );
  });
});
