import { describe, expect, it, vi } from "vitest";
import type { JsonResult } from "../../../shared/api/types";
import { createCategoryApi } from "../categoryApi";
import type { CategoryNode } from "../categoryTypes";

const node: CategoryNode = {
  id: "1",
  categoryCode: "SCENE_A",
  categoryName: "XXXX场景A",
  categoryType: "SCENE",
  parentId: null,
  categoryPath: "/XXXX场景A",
  sortNo: 10,
  documentCount: 0,
  rowVersion: 0,
  children: [],
};

function result<T>(data: T, httpStatus = 200): JsonResult<T> {
  return {
    data,
    message: "success",
    requestId: "request-id",
    httpStatus,
    headers: new Headers(),
  };
}

function client() {
  return {
    get: vi.fn().mockResolvedValue(result([node])),
    post: vi.fn().mockResolvedValue(result(node, 201)),
    put: vi.fn().mockResolvedValue(result({ ...node, rowVersion: 1 })),
    delete: vi.fn().mockResolvedValue(result({ id: "1", deleted: true })),
  };
}

describe("categoryApi", () => {
  it("查询分类树使用keyword参数和AbortSignal", async () => {
    const mock = client();
    const api = createCategoryApi(mock);
    const signal = new AbortController().signal;
    await expect(api.getTree({ keyword: "场景A" }, signal)).resolves.toEqual([node]);
    expect(mock.get).toHaveBeenCalledWith("categories/tree", {
      query: { keyword: "场景A", status: undefined },
      signal,
    });
  });

  it("新增顶级分类仅提交契约字段", async () => {
    const mock = client();
    const api = createCategoryApi(mock);
    const body = {
      categoryCode: "F3_TEST_ROOT",
      categoryName: "F3测试根分类",
      categoryType: "SCENE" as const,
      parentId: null,
      sortNo: 10,
    };
    await api.create(body);
    expect(mock.post).toHaveBeenCalledWith("categories", body);
  });

  it("新增子分类携带字符串parentId", async () => {
    const mock = client();
    const api = createCategoryApi(mock);
    const body = {
      categoryCode: "F3_TEST_CHILD",
      categoryName: "F3测试子分类",
      categoryType: "STYLE" as const,
      parentId: "1",
      sortNo: 10,
    };
    await api.create(body);
    expect(mock.post).toHaveBeenCalledWith("categories", body);
  });

  it("编辑分类携带rowVersion且不提交categoryCode", async () => {
    const mock = client();
    const api = createCategoryApi(mock);
    const body = {
      categoryName: "新名称",
      categoryType: "STYLE" as const,
      parentId: null,
      sortNo: 20,
      rowVersion: 3,
    };
    await api.update("1", body);
    expect(mock.put).toHaveBeenCalledWith("categories/1", body);
    expect(body).not.toHaveProperty("categoryCode");
  });

  it("删除分类通过查询参数携带rowVersion", async () => {
    const mock = client();
    const api = createCategoryApi(mock);
    await api.remove("1", 4);
    expect(mock.delete).toHaveBeenCalledWith("categories/1", {
      query: { rowVersion: 4 },
    });
  });

  it("拒绝number ID响应并保留requestId", async () => {
    const mock = client();
    mock.get.mockResolvedValue(result([{ ...node, id: 1 }]));
    const api = createCategoryApi(mock);
    await expect(api.getTree()).rejects.toMatchObject({
      code: "INTERNAL_ERROR",
      requestId: "request-id",
    });
  });
});

