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

const mockIsConvexEnabled = vi.fn(() => true);
const mockGetAuthedConvexClient = vi.fn();
const mockBuildDocumentBridgeContext = vi.fn();
const mockBuildDocumentCommandEnvelope = vi.fn();
const mockResolveRustBridgeCommandPlan = vi.fn();
const mockExecuteRustBridgeMutationTransport = vi.fn();
const mockDocumentBridgeErrorResponse = vi.fn((error: unknown) =>
  Response.json(
    {
      error: error instanceof Error ? error.message : String(error),
    },
    { status: 500 },
  ),
);
const mockEnsureDocumentScaffold = vi.fn();

vi.mock("@/lib/convex/enabled", () => ({
  isConvexEnabled: () => mockIsConvexEnabled(),
}));

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

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

vi.mock("@/lib/documents/bridge", () => ({
  assertDocumentId: (value: string | null | undefined) => {
    const normalized = typeof value === "string" ? value.trim() : "";
    if (!normalized) {
      throw new Error("缺少 documentId");
    }
    return normalized;
  },
  assertTitle: (value: string | null | undefined) => {
    const normalized = typeof value === "string" ? value.trim() : "";
    if (!normalized) {
      throw new Error("缺少标题");
    }
    return normalized;
  },
  buildDocumentBridgeContext: mockBuildDocumentBridgeContext,
  buildDocumentCommandEnvelope: mockBuildDocumentCommandEnvelope,
  documentBridgeErrorResponse: (...args: unknown[]) => mockDocumentBridgeErrorResponse(...args),
}));

vi.mock("@/lib/documents/rust-runtime", () => ({
  resolveRustBridgeCommandPlan: (...args: unknown[]) => mockResolveRustBridgeCommandPlan(...args),
  executeRustBridgeMutationTransport: (...args: unknown[]) => mockExecuteRustBridgeMutationTransport(...args),
}));

vi.mock("@/lib/documents/page-lifecycle-side-effects", () => ({
  ensureDocumentScaffold: (...args: unknown[]) => mockEnsureDocumentScaffold(...args),
}));

describe("/api/tree/commands route", () => {
  beforeEach(() => {
    mockIsConvexEnabled.mockReturnValue(true);
    mockGetAuthedConvexClient.mockReset();
    mockBuildDocumentBridgeContext.mockReset();
    mockBuildDocumentCommandEnvelope.mockReset();
    mockResolveRustBridgeCommandPlan.mockReset();
    mockExecuteRustBridgeMutationTransport.mockReset();
    mockEnsureDocumentScaffold.mockReset();
    mockDocumentBridgeErrorResponse.mockClear();
  });

  it("create action 走 tree.node.create，并保留本地 scaffold 副作用", async () => {
    const client = {
      mutation: vi.fn(async () => ({ activeWorkspaceId: "ws_root" })),
      query: vi.fn(),
    };
    mockGetAuthedConvexClient.mockResolvedValue({
      auth: {
        userId: "user_1",
        email: "dev@example.com",
        name: "开发用户",
      },
      client,
    });
    mockBuildDocumentBridgeContext.mockResolvedValue({
      requestId: "req_tree_1",
      traceId: "trace_tree_1",
      workspaceId: "ws_root",
      actor: {
        actorType: "user",
        actorId: "user_1",
        sessionId: null,
      },
      source: {
        channel: "next-route",
        client: "wolai-frontend",
      },
      tenantId: null,
      deploymentId: null,
      projectId: null,
      authToken: null,
      idempotencyKey: null,
      validateOnly: false,
      dryRun: false,
    });
    mockBuildDocumentCommandEnvelope.mockImplementation((input: unknown) => input);
    mockResolveRustBridgeCommandPlan.mockResolvedValue({
      kind: "command",
      commandName: "tree.node.create",
      commandId: "cmd_tree_create_1",
      functionName: "documents:createWithParentReference",
      workspaceId: "ws_root",
      requestId: "req_tree_1",
      traceId: "trace_tree_1",
      actorId: "user_1",
      idempotencyKey: null,
      payloadJson: "{}",
      argsJson: {},
    });
    mockExecuteRustBridgeMutationTransport.mockResolvedValue({
      id: "doc_new",
      title: "无标题",
      parent_id: null,
      sort_order: 0,
      workspace_id: "ws_root",
      access_scope: "private",
      is_template: false,
      created_at: "2026-04-23T00:00:00Z",
      updated_at: "2026-04-23T00:00:00Z",
    });

    const { POST } = await import("./route");
    const response = await POST(
      new Request("http://127.0.0.1:3000/api/tree/commands", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          action: "create",
          parentId: null,
        }),
      }),
    );

    expect(response.status).toBe(200);
    const payload = await response.json() as {
      result: {
        action: string;
        documentId: string;
        workspaceId: string;
        title: string;
      };
    };
    expect(payload.result.action).toBe("create");
    expect(payload.result.documentId).toBe("doc_new");
    expect(payload.result.workspaceId).toBe("ws_root");
    expect(mockBuildDocumentCommandEnvelope).toHaveBeenCalledWith(
      expect.objectContaining({
        name: "tree.node.create",
        payload: expect.objectContaining({
          workspaceId: "ws_root",
          parentId: null,
          title: "无标题",
          accessScope: "private",
        }),
      }),
    );
    expect(mockEnsureDocumentScaffold).toHaveBeenCalledWith("doc_new", "无标题");
  });

  it("move action 走 tree.subtree.move，并把 position 归一化为 sortOrder", async () => {
    const client = {
      mutation: vi.fn(),
      query: vi.fn(async () => ({
        id: "doc_1",
        workspace_id: "ws_1",
      })),
    };
    mockGetAuthedConvexClient.mockResolvedValue({
      auth: {
        userId: "user_1",
        email: "dev@example.com",
        name: "开发用户",
      },
      client,
    });
    mockBuildDocumentBridgeContext.mockResolvedValue({
      requestId: "req_tree_2",
      traceId: "trace_tree_2",
      workspaceId: "ws_1",
      actor: {
        actorType: "user",
        actorId: "user_1",
        sessionId: null,
      },
      source: {
        channel: "next-route",
        client: "wolai-frontend",
      },
      tenantId: null,
      deploymentId: null,
      projectId: null,
      authToken: null,
      idempotencyKey: null,
      validateOnly: false,
      dryRun: false,
    });
    mockBuildDocumentCommandEnvelope.mockImplementation((input: unknown) => input);
    mockResolveRustBridgeCommandPlan.mockResolvedValue({
      kind: "command",
      commandName: "tree.subtree.move",
      commandId: "cmd_tree_move_1",
      functionName: "documents:move",
      workspaceId: "ws_1",
      requestId: "req_tree_2",
      traceId: "trace_tree_2",
      actorId: "user_1",
      idempotencyKey: null,
      payloadJson: "{}",
      argsJson: {},
    });
    mockExecuteRustBridgeMutationTransport.mockResolvedValue({
      ok: true,
      updated_at: "2026-04-23T00:00:00Z",
    });

    const { POST } = await import("./route");
    const response = await POST(
      new Request("http://127.0.0.1:3000/api/tree/commands", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          action: "move",
          documentId: "doc_1",
          parentId: "parent_1",
          sortOrder: 2.9,
          workspaceId: "ws_1",
        }),
      }),
    );

    expect(response.status).toBe(200);
    const payload = await response.json() as {
      result: {
        action: string;
        documentId: string;
        parentId: string | null;
        sortOrder: number | null;
      };
    };
    expect(payload.result).toMatchObject({
      action: "move",
      documentId: "doc_1",
      parentId: "parent_1",
      sortOrder: 2,
      workspaceId: "ws_1",
    });
    expect(mockBuildDocumentCommandEnvelope).toHaveBeenCalledWith(
      expect.objectContaining({
        name: "tree.subtree.move",
        payload: {
          documentId: "doc_1",
          parentId: "parent_1",
          sortOrder: 2,
        },
      }),
    );
    expect(mockEnsureDocumentScaffold).not.toHaveBeenCalled();
  });

  it("rename action 走 tree.node.rename，并保留标题 payload", async () => {
    const client = {
      mutation: vi.fn(),
      query: vi.fn(async () => ({
        id: "doc_1",
        workspace_id: "ws_1",
      })),
    };
    mockGetAuthedConvexClient.mockResolvedValue({
      auth: {
        userId: "user_1",
        email: "dev@example.com",
        name: "开发用户",
      },
      client,
    });
    mockBuildDocumentBridgeContext.mockResolvedValue({
      requestId: "req_tree_3",
      traceId: "trace_tree_3",
      workspaceId: "ws_1",
      actor: {
        actorType: "user",
        actorId: "user_1",
        sessionId: null,
      },
      source: {
        channel: "next-route",
        client: "wolai-frontend",
      },
      tenantId: null,
      deploymentId: null,
      projectId: null,
      authToken: null,
      idempotencyKey: null,
      validateOnly: false,
      dryRun: false,
    });
    mockBuildDocumentCommandEnvelope.mockImplementation((input: unknown) => input);
    mockResolveRustBridgeCommandPlan.mockResolvedValue({
      kind: "command",
      commandName: "tree.node.rename",
      commandId: "cmd_tree_rename_1",
      functionName: "documents:updateTitle",
      workspaceId: "ws_1",
      requestId: "req_tree_3",
      traceId: "trace_tree_3",
      actorId: "user_1",
      idempotencyKey: null,
      payloadJson: "{}",
      argsJson: {},
    });
    mockExecuteRustBridgeMutationTransport.mockResolvedValue({
      ok: true,
      updated_at: "2026-04-23T00:00:00Z",
    });

    const { POST } = await import("./route");
    const response = await POST(
      new Request("http://127.0.0.1:3000/api/tree/commands", {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({
          action: "rename",
          documentId: "doc_1",
          workspaceId: "ws_1",
          title: "新标题",
        }),
      }),
    );

    expect(response.status).toBe(200);
    const payload = await response.json() as {
      result: {
        action: string;
        documentId: string;
        workspaceId: string;
        title: string;
      };
    };
    expect(payload.result).toMatchObject({
      action: "rename",
      documentId: "doc_1",
      workspaceId: "ws_1",
      title: "新标题",
    });
    expect(mockBuildDocumentCommandEnvelope).toHaveBeenCalledWith(
      expect.objectContaining({
        name: "tree.node.rename",
        payload: {
          documentId: "doc_1",
          workspaceId: "ws_1",
          title: "新标题",
        },
      }),
    );
    expect(mockEnsureDocumentScaffold).not.toHaveBeenCalled();
  });
});
