import { beforeEach, describe, expect, it, vi } from "vitest";
import type { FlockRunQuoteResult } from "@/workers/rpa/flock/types";

const directMock = vi.fn<() => Promise<FlockRunQuoteResult>>();
const visualMock = vi.fn<() => Promise<FlockRunQuoteResult>>();
const customerLoginMock = vi.fn();
const hasCredMock = vi.fn();

vi.mock("@/lib/flock/direct-quote", () => ({
  fetchFlockQuotesViaDirect: () => directMock(),
}));

vi.mock("@/workers/rpa/flock/visual-chain", () => ({
  runFlockVisualChain: () => visualMock(),
}));

vi.mock("@/workers/rpa/flock/worker-session", () => ({
  runFlockQuoteWithWorkerSession: () => visualMock(),
  shouldRotateFlockSession: () => false,
}));

vi.mock("@/lib/flock/exclusive-lock", () => ({
  withFlockExclusiveLock: async (
    _owner: string,
    fn: () => Promise<FlockRunQuoteResult>,
  ) => fn(),
}));

vi.mock("@/modules/customer/provider-credentials", () => ({
  getCustomerProviderLogin: (...args: unknown[]) => customerLoginMock(...args),
  hasCustomerProviderCredential: (...args: unknown[]) => hasCredMock(...args),
}));

describe("runFlockQuoteRpa customer login forces DOM", () => {
  beforeEach(() => {
    directMock.mockReset();
    visualMock.mockReset();
    customerLoginMock.mockReset();
    hasCredMock.mockReset();
    hasCredMock.mockResolvedValue(false);
    process.env.RPA_MOCK_MODE = "false";
    process.env.FLOCK_QUOTE_MODE = "direct_then_dom";
    delete process.env.FLOCK_LOGIN_EMAIL;
    delete process.env.FLOCK_LOGIN_PASSWORD;
    process.env.FLOCK_WORKER_REUSE_SESSION = "false";
  });

  const input = {
    pickupZip: "94102",
    deliveryZip: "90212",
    pickupDate: "07/20/2026",
    palletCount: 4,
    totalWeightLb: 2800,
    lengthIn: 48,
    widthIn: 40,
    heightIn: 48,
  };

  it("有客户账密时跳过 Direct，走 DOM", async () => {
    customerLoginMock.mockResolvedValue({
      email: "cust@example.com",
      password: "secret",
    });
    visualMock.mockResolvedValue({
      ok: true,
      quotes: [{ tier: "standard", totalUsd: 100 } as FlockRunQuoteResult["quotes"][0]],
    });

    const { runFlockQuoteRpa } = await import("@/workers/rpa/flock/run-quote");
    const result = await runFlockQuoteRpa(input, { customerId: "CUST_001" });

    expect(result.ok).toBe(true);
    expect(directMock).not.toHaveBeenCalled();
    expect(visualMock).toHaveBeenCalled();
    expect(customerLoginMock).toHaveBeenCalledWith("CUST_001", "flock");
  });

  it("库中已配账密但解密失败时不回落 Direct", async () => {
    customerLoginMock.mockResolvedValue(null);
    hasCredMock.mockResolvedValue(true);

    const { runFlockQuoteRpa } = await import("@/workers/rpa/flock/run-quote");
    const result = await runFlockQuoteRpa(input, { customerId: "CUST_001" });

    expect(result.ok).toBe(false);
    expect(result.errorMessage).toMatch(/解密失败|禁止改走 Direct/i);
    expect(directMock).not.toHaveBeenCalled();
    expect(visualMock).not.toHaveBeenCalled();
  });

  it("无客户账密时走 Direct", async () => {
    customerLoginMock.mockResolvedValue(null);
    hasCredMock.mockResolvedValue(false);
    directMock.mockResolvedValue({
      ok: true,
      quotes: [{ tier: "standard", totalUsd: 200 } as FlockRunQuoteResult["quotes"][0]],
    });

    const { runFlockQuoteRpa } = await import("@/workers/rpa/flock/run-quote");
    const result = await runFlockQuoteRpa(input, { customerId: "CUST_001" });

    expect(result.ok).toBe(true);
    expect(directMock).toHaveBeenCalled();
  });
});
