import { beforeEach, describe, expect, it, vi } from "vitest";
import { handleRpaFailure } from "@/modules/quote/fallback-orchestrator";

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

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

vi.mock("@/modules/pricing/engine", () => ({
  getMarkupPercent: vi.fn().mockResolvedValue(10),
  getMarkupRule: vi.fn().mockResolvedValue({ type: "percent", percent: 10 }),
  applyMarkupToQuotes: vi.fn((quotes: unknown[]) =>
    quotes.map((q) => ({ ...(q as object), markup_percent: 10 })),
  ),
}));

import { getL3 } from "@/modules/cache/redis-cache";
import { prisma } from "@/lib/prisma";

const mockedGetL3 = vi.mocked(getL3);
const mockedUpdate = vi.mocked(prisma.quoteRecord.update);
const mockedAlertCreate = vi.mocked(prisma.alertLog.create);

const FOUR_TIERS = [
  { service_level: "standard", rate_option: "lowest", raw_freight: 320, surcharges: 0, raw_total: 320, carrier: "M", transit_days: "5", transit_description: "x" },
  { service_level: "standard", rate_option: "fastest", raw_freight: 365, surcharges: 0, raw_total: 365, carrier: "M", transit_days: "3", transit_description: "x" },
  { service_level: "guaranteed", rate_option: "lowest", raw_freight: 380, surcharges: 0, raw_total: 380, carrier: "M", transit_days: "4", transit_description: "x" },
  { service_level: "guaranteed", rate_option: "fastest", raw_freight: 410, surcharges: 0, raw_total: 410, carrier: "M", transit_days: "3", transit_description: "x" },
];

describe("handleRpaFailure", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockedUpdate.mockResolvedValue({} as never);
    mockedAlertCreate.mockResolvedValue({} as never);
  });

  it("TC-205：L3 命中 → done stale + STALE_FALLBACK", async () => {
    mockedGetL3.mockResolvedValue({ quotes: FOUR_TIERS });

    const result = await handleRpaFailure(
      "QTE_20260616_0001",
      "abc123",
      "CUST_001",
      new Error("captcha"),
    );

    expect(result.status).toBe("done");
    if (result.status === "done") {
      expect(result.sourceType).toBe("stale");
      expect(result.isRealtime).toBe(false);
    }
    expect(mockedAlertCreate).toHaveBeenCalledWith(
      expect.objectContaining({
        data: expect.objectContaining({ alertType: "STALE_FALLBACK" }),
      }),
    );
  });

  it("TC-206：无 L3 → failed QUOTE_UNAVAILABLE + RPA_FAILED", async () => {
    mockedGetL3.mockResolvedValue(null);

    const result = await handleRpaFailure(
      "QTE_20260616_0001",
      "abc123",
      "CUST_001",
      new Error("timeout"),
    );

    expect(result).toEqual({
      status: "failed",
      errorCode: "QUOTE_UNAVAILABLE",
      errorMessage: "timeout",
    });
    expect(mockedAlertCreate).toHaveBeenCalledWith(
      expect.objectContaining({
        data: expect.objectContaining({ alertType: "RPA_FAILED" }),
      }),
    );
  });

  it("skipStaleFallback 时不使用 L3 缓存", async () => {
    mockedGetL3.mockResolvedValue({ quotes: FOUR_TIERS });

    const result = await handleRpaFailure(
      "QTE_20260616_0001",
      "abc123",
      "CUST_001",
      new Error("priority1 timeout"),
      { skipStaleFallback: true },
    );

    expect(result.status).toBe("failed");
    expect(mockedGetL3).not.toHaveBeenCalled();
  });
});
