import { describe, expect, it, vi } from "vitest";
import type { JsonResult } from "../../../shared/api/types";
import { detail } from "../../documents/__tests__/fixtures";
import {
  attachmentBatchImportForm,
  attachmentUploadForm,
  createAttachmentMutationApi,
} from "../attachmentMutationApi";
import {
  batchImportForm,
  createDocumentMutationApi,
  documentUploadForm,
} from "../documentMutationApi";
import type {
  AttachmentCreateMetadata,
  DocumentCreateMetadata,
} from "../mutationTypes";

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

const mainMetadata: DocumentCreateMetadata = {
  documentName: "主案",
  documentType: "MAIN",
  summary: null,
  categoryId: "1",
  securityLevel: "INTERNAL",
  tags: ["标签"],
};
const subMetadata: DocumentCreateMetadata = {
  documentName: mainMetadata.documentName,
  summary: mainMetadata.summary,
  securityLevel: mainMetadata.securityLevel,
  tags: mainMetadata.tags,
  documentType: "SUB_PLAN",
  parentDocumentId: "9",
};
const attachmentMetadata: AttachmentCreateMetadata = {
  documentName: "附件",
  attachmentType: "POLICY",
  summary: null,
  tags: ["制度"],
};
const file = new File(["test"], "test.pdf", { type: "application/pdf" });
const successBatch = {
  total: 1,
  successCount: 1,
  failureCount: 0,
  items: [{ index: 0, originalFileName: "test.pdf", success: true, document: detail }],
};

function client(data: unknown) {
  return {
    post: vi.fn().mockResolvedValue(result(data)),
    put: vi.fn().mockResolvedValue(result(data)),
    delete: vi.fn().mockResolvedValue(result(data)),
  };
}

describe("F5 mutation APIs", () => {
  it("单文件方案上传使用 POST documents，FormData包含file和metadata", async () => {
    const mock = client(detail);
    await createDocumentMutationApi(mock).create(file, mainMetadata);
    expect(mock.post).toHaveBeenCalledWith("documents", expect.any(FormData), { signal: undefined });
    const form = mock.post.mock.calls[0][1] as FormData;
    expect(form.get("file")).toBe(file);
    expect(JSON.parse(String(form.get("metadata")))).toEqual(mainMetadata);
    expect([...form.keys()]).toEqual(["file", "metadata"]);
  });

  it("子方案metadata保持字符串parentDocumentId，主案不提交该字段", () => {
    expect(JSON.parse(String(documentUploadForm(file, subMetadata).get("metadata"))).parentDocumentId).toBe("9");
    expect(JSON.parse(String(documentUploadForm(file, mainMetadata).get("metadata")))).not.toHaveProperty("parentDocumentId");
  });

  it("确认覆盖时同时提交字符串文档ID和版本", () => {
    const metadata = { ...mainMetadata, overwriteDocumentId: "12", rowVersion: 4 };
    expect(JSON.parse(String(documentUploadForm(file, metadata).get("metadata")))).toEqual(metadata);
    expect(() => documentUploadForm(file, { ...mainMetadata, overwriteDocumentId: "12" })).toThrow();
  });

  it("方案批量使用重复files和稳定items顺序", async () => {
    const second = new File(["two"], "two.docx");
    const form = batchImportForm([file, second], [mainMetadata, subMetadata]);
    expect(form.getAll("files")).toEqual([file, second]);
    expect(JSON.parse(String(form.get("items")))).toEqual([mainMetadata, subMetadata]);
    const mock = client(successBatch);
    await createDocumentMutationApi(mock).batchImport([file], [mainMetadata]);
    expect(mock.post).toHaveBeenCalledWith("documents/batch-import", expect.any(FormData), { signal: undefined });
  });

  it("方案编辑只发送契约字段，删除通过查询参数携带rowVersion", async () => {
    const mock = client(detail);
    const request = {
      documentName: "新名称",
      summary: null,
      categoryId: "1",
      securityLevel: "SECRET" as const,
      tags: [],
      rowVersion: 3,
    };
    await createDocumentMutationApi(mock).update("10", request);
    expect(mock.put).toHaveBeenCalledWith("documents/10", request, { signal: undefined });
    mock.delete.mockResolvedValue(result({ id: "10", deleted: true }));
    await createDocumentMutationApi(mock).remove("10", 3);
    expect(mock.delete).toHaveBeenCalledWith("documents/10", { query: { rowVersion: 3 }, signal: undefined });
  });

  it("1.4主案和子方案请求严格排除废止字段", () => {
    const main = JSON.parse(String(documentUploadForm(file, mainMetadata).get("metadata")));
    const sub = JSON.parse(String(documentUploadForm(file, subMetadata).get("metadata")));
    for (const value of [main, sub]) {
      expect(value).not.toHaveProperty("status");
      expect(value).not.toHaveProperty("visibilityType");
    }
    expect(sub).not.toHaveProperty("categoryId");
    expect(main).toHaveProperty("categoryId", "1");
  });

  it("单附件上传只包含附件metadata，不提交固定后端字段", async () => {
    const mock = client({ ...detail, id: "7", documentType: "ATTACHMENT", attachmentType: "POLICY", categoryId: null, categoryName: null, categoryPath: null, parentDocumentId: null, rootDocumentId: null });
    await createAttachmentMutationApi(mock).create(file, attachmentMetadata);
    expect(mock.post).toHaveBeenCalledWith("attachments", expect.any(FormData), { signal: undefined });
    const metadata = JSON.parse(String((mock.post.mock.calls[0][1] as FormData).get("metadata")));
    expect(metadata).toEqual(attachmentMetadata);
    for (const forbidden of ["documentType", "securityLevel", "visibilityType", "categoryId", "parentDocumentId"]) {
      expect(metadata).not.toHaveProperty(forbidden);
    }
  });

  it("附件批量使用重复files字段并调用attachments/batch-import", async () => {
    const form = attachmentBatchImportForm([file, file], [attachmentMetadata, attachmentMetadata]);
    expect(form.getAll("files")).toHaveLength(2);
    const mock = client(successBatch);
    await createAttachmentMutationApi(mock).batchImport([file], [attachmentMetadata]);
    expect(mock.post).toHaveBeenCalledWith("attachments/batch-import", expect.any(FormData), { signal: undefined });
  });

  it("附件编辑和删除路径、方法正确", async () => {
    const attachmentDetail = { ...detail, id: "7", documentType: "ATTACHMENT", attachmentType: "POLICY", categoryId: null, categoryName: null, categoryPath: null, parentDocumentId: null, rootDocumentId: null };
    const mock = client(attachmentDetail);
    const request = { documentName: "新附件", attachmentType: "OTHER" as const, summary: null, tags: [], rowVersion: 2 };
    await createAttachmentMutationApi(mock).update("7", request);
    expect(mock.put).toHaveBeenCalledWith("attachments/7", request, { signal: undefined });
    mock.delete.mockResolvedValue(result({ id: "7", deleted: true }));
    await createAttachmentMutationApi(mock).remove("7", 2);
    expect(mock.delete).toHaveBeenCalledWith("attachments/7", { query: { rowVersion: 2 }, signal: undefined });
  });

  it("拒绝number ID、未知枚举、files/items不一致及额外文件身份字段", () => {
    expect(() => documentUploadForm(file, { ...subMetadata, parentDocumentId: 9 } as never)).toThrow();
    expect(() => documentUploadForm(file, { ...mainMetadata, securityLevel: "UNKNOWN" } as never)).toThrow();
    expect(() => batchImportForm([file], [])).toThrow();
    expect(() => documentUploadForm(file, { ...mainMetadata, fileHash: "x" } as never)).toThrow();
  });
});
