import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { HttpError } from "../../../shared/api/errors";
import { fileApi } from "../fileApi";
import {
  acquirePreview,
  clearPreviewRequestsForTests,
  createPreviewUrl,
  downloadFile,
  loadPreviewOnce,
  revokeObjectUrl,
} from "../fileState";

const blobResult = {
  blob: new Blob(["pdf"], { type: "application/pdf" }),
  contentType: "application/pdf",
  contentDisposition: "attachment; filename*=UTF-8''report.pdf",
  requestId: "request-id",
  httpStatus: 200,
  headers: new Headers(),
};

describe("preview/download lifecycle", () => {
  beforeEach(() => {
    clearPreviewRequestsForTests();
  });
  afterEach(() => {
    vi.useRealTimers();
    vi.restoreAllMocks();
    vi.unstubAllGlobals();
  });

  it("同一预览的并发请求合并为一次", async () => {
    const preview = vi.spyOn(fileApi, "preview").mockResolvedValue(blobResult);
    const [first, second] = await Promise.all([
      loadPreviewOnce("1"),
      loadPreviewOnce("1"),
    ]);
    expect(first).toEqual(blobResult);
    expect(second).toEqual(blobResult);
    expect(preview).toHaveBeenCalledTimes(1);
  });

  it("Object URL创建和释放均走明确生命周期", () => {
    const createObjectURL = vi.fn().mockReturnValue("blob:preview");
    const revokeObjectURL = vi.fn();
    vi.stubGlobal("URL", { createObjectURL, revokeObjectURL });
    expect(createPreviewUrl(blobResult.blob)).toBe("blob:preview");
    revokeObjectUrl("blob:preview");
    expect(createObjectURL).toHaveBeenCalledWith(blobResult.blob);
    expect(revokeObjectURL).toHaveBeenCalledWith("blob:preview");
  });

  it("关闭最后一个预览消费者会取消未完成请求", async () => {
    vi.useFakeTimers();
    let capturedSignal: AbortSignal | undefined;
    vi.spyOn(fileApi, "preview").mockImplementation((_id, signal) => {
      capturedSignal = signal;
      return new Promise(() => undefined);
    });
    const acquired = acquirePreview("1");
    acquired.release();
    await vi.runAllTimersAsync();
    expect(capturedSignal?.aborted).toBe(true);
    vi.useRealTimers();
  });

  it("下载成功创建临时链接并立即清理URL和DOM", async () => {
    vi.spyOn(fileApi, "download").mockResolvedValue(blobResult);
    const click = vi.fn();
    const remove = vi.fn();
    const anchor = { href: "", download: "", style: { display: "" }, click, remove };
    const appendChild = vi.fn();
    vi.stubGlobal("URL", {
      createObjectURL: vi.fn().mockReturnValue("blob:download"),
      revokeObjectURL: vi.fn(),
    });
    vi.stubGlobal("document", {
      createElement: vi.fn().mockReturnValue(anchor),
      body: { appendChild },
    });
    await expect(downloadFile("1", "fallback.pdf")).resolves.toEqual({
      filename: "report.pdf",
      requestId: "request-id",
    });
    expect(click).toHaveBeenCalledOnce();
    expect(remove).toHaveBeenCalledOnce();
    expect(URL.revokeObjectURL).toHaveBeenCalledWith("blob:download");
  });

  it("JSON/权限错误不会创建空下载文件", async () => {
    vi.spyOn(fileApi, "download").mockRejectedValue(new HttpError({
      httpStatus: 403,
      code: "DOCUMENT_DOWNLOAD_FORBIDDEN",
      message: "禁止下载",
      details: null,
      requestId: "request-id",
    }));
    const createObjectURL = vi.fn();
    vi.stubGlobal("URL", { createObjectURL, revokeObjectURL: vi.fn() });
    await expect(downloadFile("1", "fallback.pdf")).rejects.toMatchObject({
      code: "DOCUMENT_DOWNLOAD_FORBIDDEN",
      requestId: "request-id",
    });
    expect(createObjectURL).not.toHaveBeenCalled();
  });
});
