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

const prismaMock = vi.hoisted(() => ({
  quoteCacheMeta: {
    findUnique: vi.fn(),
    create: vi.fn(),
    update: vi.fn(),
  },
  alertLog: {
    create: vi.fn(),
  },
}));

vi.mock("@/lib/prisma", () => ({ prisma: prismaMock }));

import { detectDeviation } from "@/modules/alert/deviation-detector";
import type { RawQuoteTier } from "@/modules/pricing/engine";

const fourTiers = (): RawQuoteTier[] => [
  {
    service_level: "standard",
    rate_option: "lowest",
    carrier: "A",
    transit_days: "3",
    transit_description: "3d",
    raw_freight: 320,
    surcharges: 0,
    raw_total: 320,
  },
  {
    service_level: "standard",
    rate_option: "fastest",
    carrier: "A",
    transit_days: "2",
    transit_description: "2d",
    raw_freight: 380,
    surcharges: 0,
    raw_total: 380,
  },
  {
    service_level: "guaranteed",
    rate_option: "lowest",
    carrier: "B",
    transit_days: "3",
    transit_description: "3d",
    raw_freight: 420,
    surcharges: 0,
    raw_total: 420,
  },
  {
    service_level: "guaranteed",
    rate_option: "fastest",
    carrier: "B",
    transit_days: "2",
    transit_description: "2d",
    raw_freight: 480,
    surcharges: 0,
    raw_total: 480,
  },
];

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

  it("TC-212 首次询价不触发偏差预警", async () => {
    prismaMock.quoteCacheMeta.findUnique.mockResolvedValue(null);
    prismaMock.quoteCacheMeta.create.mockResolvedValue({});

    await detectDeviation("hash1", fourTiers(), "QTE001");

    expect(prismaMock.alertLog.create).not.toHaveBeenCalled();
    expect(prismaMock.quoteCacheMeta.create).toHaveBeenCalled();
  });

  it("TC-210 偏差≥5% 写 PRICE_DEVIATION", async () => {
    prismaMock.quoteCacheMeta.findUnique.mockResolvedValue({
      cargoHash: "hash1",
      rawTotalStandard: 352,
    });
    prismaMock.quoteCacheMeta.update.mockResolvedValue({});
    prismaMock.alertLog.create.mockResolvedValue({});

    const quotes = fourTiers();
    quotes[0].raw_total = 370;
    quotes[0].raw_freight = 370;

    await detectDeviation("hash1", quotes, "QTE002");

    expect(prismaMock.alertLog.create).toHaveBeenCalledWith(
      expect.objectContaining({
        data: expect.objectContaining({ alertType: "PRICE_DEVIATION" }),
      }),
    );
  });

  it("TC-211 偏差4.9% 不触发", async () => {
    prismaMock.quoteCacheMeta.findUnique.mockResolvedValue({
      cargoHash: "hash1",
      rawTotalStandard: 352,
    });
    prismaMock.quoteCacheMeta.update.mockResolvedValue({});

    const quotes = fourTiers();
    quotes[0].raw_total = 369.25;
    quotes[0].raw_freight = 369.25;

    await detectDeviation("hash1", quotes, "QTE003");

    expect(prismaMock.alertLog.create).not.toHaveBeenCalled();
    expect(prismaMock.quoteCacheMeta.update).toHaveBeenCalled();
  });
});
