import { beforeEach, describe, expect, it, vi } from "vitest";
import {
  checkIdempotency,
  saveIdempotency,
} from "@/modules/quote/idempotency";

vi.mock("@/modules/cache/redis-cache", () => ({
  getL1: vi.fn(),
  setL1: vi.fn(),
}));

vi.mock("@/lib/prisma", () => ({
  prisma: {
    idempotencyRecord: {
      findUnique: vi.fn(),
      create: vi.fn(),
    },
    quoteRecord: {
      findUnique: vi.fn(),
    },
  },
}));

import { getL1, setL1 } from "@/modules/cache/redis-cache";
import { prisma } from "@/lib/prisma";

const mockedGetL1 = vi.mocked(getL1);
const mockedSetL1 = vi.mocked(setL1);
const mockedFindUnique = vi.mocked(prisma.idempotencyRecord.findUnique);
const mockedCreate = vi.mocked(prisma.idempotencyRecord.create);
const mockedQuoteFind = vi.mocked(prisma.quoteRecord.findUnique);

describe("checkIdempotency", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("L1 命中 → 返回 quote_id", async () => {
    mockedGetL1.mockResolvedValue({
      quoteId: "QTE_20260616_0001",
      response: { quote_id: "QTE_20260616_0001", status: "done" },
    });

    const result = await checkIdempotency("550e8400-e29b-41d4-a716-446655440000");
    expect(result.hit).toBe(true);
    if (result.hit) {
      expect(result.quoteId).toBe("QTE_20260616_0001");
    }
  });

  it("L1 未命中、DB 命中 → 回填 L1", async () => {
    mockedGetL1.mockResolvedValue(null);
    mockedFindUnique.mockResolvedValue({
      quoteId: "QTE_20260616_0002",
    } as never);
    mockedQuoteFind.mockResolvedValue({
      quoteId: "QTE_20260616_0002",
      status: "done",
    } as never);

    const result = await checkIdempotency("550e8400-e29b-41d4-a716-446655440000");
    expect(result.hit).toBe(true);
    expect(mockedSetL1).toHaveBeenCalled();
  });

  it("均未命中 → hit=false", async () => {
    mockedGetL1.mockResolvedValue(null);
    mockedFindUnique.mockResolvedValue(null);

    const result = await checkIdempotency("550e8400-e29b-41d4-a716-446655440000");
    expect(result.hit).toBe(false);
  });
});

describe("saveIdempotency", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockedCreate.mockResolvedValue({} as never);
    mockedSetL1.mockResolvedValue(true);
  });

  it("写入 DB + Redis", async () => {
    await saveIdempotency(
      "550e8400-e29b-41d4-a716-446655440000",
      "CUST_001",
      "QTE_20260616_0001",
      { quote_id: "QTE_20260616_0001", status: "done" },
    );
    expect(mockedCreate).toHaveBeenCalled();
    expect(mockedSetL1).toHaveBeenCalled();
  });

  it("P2002 冲突 → 静默返回", async () => {
    mockedCreate.mockRejectedValue({ code: "P2002" });
    mockedFindUnique.mockResolvedValue({
      quoteId: "QTE_20260616_0001",
    } as never);

    await expect(
      saveIdempotency(
        "550e8400-e29b-41d4-a716-446655440000",
        "CUST_001",
        "QTE_20260616_0001",
        {},
      ),
    ).resolves.toBeUndefined();
  });
});
