import { beforeEach, describe, expect, it, vi } from "vitest";
import { POST } from "@/app/api/quotes/route";

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

vi.mock("@/modules/quote/idempotency", () => ({
  checkIdempotency: vi.fn(),
  saveIdempotency: vi.fn(),
}));

vi.mock("@/modules/quote/quote-id", () => ({
  generateQuoteId: vi.fn(),
  handleQuoteIdConflict: vi.fn(),
  isQuoteIdUniqueConflict: vi.fn(),
}));

vi.mock("@/modules/quote/rpa-queue", () => ({
  enqueueQuoteJob: vi.fn(),
}));

vi.mock("@/modules/metrics/collector", () => ({
  safeRecord: vi.fn(),
  recordPostTotal: vi.fn().mockResolvedValue(undefined),
  recordCacheHit: vi.fn().mockResolvedValue(undefined),
  recordPostDone: vi.fn().mockResolvedValue(undefined),
  recordRpaTriggered: vi.fn().mockResolvedValue(undefined),
  markQuoteStarted: vi.fn().mockResolvedValue(undefined),
}));

vi.mock("@/lib/api/rate-limit", () => ({
  enforceRateLimits: vi.fn().mockResolvedValue(null),
}));

vi.mock("@/lib/prisma", () => ({
  prisma: {
    quoteRecord: { create: vi.fn() },
    markupConfig: { findFirst: vi.fn().mockResolvedValue(null) },
  },
}));

import { getL2 } from "@/modules/cache/redis-cache";
import { checkIdempotency } from "@/modules/quote/idempotency";
import { generateQuoteId } from "@/modules/quote/quote-id";
import { enqueueQuoteJob } from "@/modules/quote/rpa-queue";
import { prisma } from "@/lib/prisma";
import { resetServiceTokenCache } from "@/modules/auth/service-token";
import { buildCargoHash, serializeAddress } from "@/modules/quote/cargo-hash";
import {
  MOTHERSHIP_DELIVERY_CONFIRMED,
  MOTHERSHIP_PICKUP_CONFIRMED,
} from "@/__tests__/fixtures/mothership-address";

const SERVICE_HEADERS = {
  "x-auth-type": "service",
  "x-customer-id": "CUST_001",
  "x-permissions": "pricing:markup:write",
};

const VALID_BODY = {
  request_id: "550e8400-e29b-41d4-a716-446655440000",
  customer_id: "CUST_001",
  pickup_address: {
    street: "1234 Warehouse Blvd",
    city: "Los Angeles",
    state: "CA",
    zip: "90001",
    place_id: "ChIJ_pickup",
    formatted_address: "1234 Warehouse Blvd, Los Angeles, CA 90001, USA",
    selected_from_suggestions: true,
    ...MOTHERSHIP_PICKUP_CONFIRMED,
  },
  delivery_address: {
    street: "5678 Distribution Dr",
    city: "Dallas",
    state: "TX",
    zip: "75201",
    place_id: "ChIJ_delivery",
    formatted_address: "5678 Distribution Dr, Dallas, TX 75201, USA",
    selected_from_suggestions: true,
    ...MOTHERSHIP_DELIVERY_CONFIRMED,
  },
  weight: { value: 500, unit: "lb" },
  dimensions: { length: 48, width: 40, height: 48, unit: "in" },
  pallet_count: 2,
  cargo_type: "general_freight",
};

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",
  },
];

beforeEach(() => {
  vi.clearAllMocks();
  resetServiceTokenCache();
  process.env.HOST_SERVICE_TOKENS = JSON.stringify({
    "demo-host-token": {
      customerId: "CUST_001",
      permissions: ["pricing:markup:write"],
    },
  });
  vi.mocked(checkIdempotency).mockResolvedValue({ hit: false });
  vi.mocked(getL2).mockResolvedValue(null);
  vi.mocked(generateQuoteId).mockResolvedValue("QTE_INT_0001");
  vi.mocked(prisma.quoteRecord.create).mockResolvedValue({} as never);
  vi.mocked(enqueueQuoteJob).mockResolvedValue(undefined);
});

describe("POST /api/quotes 集成（task-085）", () => {
  it("TC-101：首次询价 → processing + 入队 RPA", async () => {
    const response = await POST(
      new Request("http://localhost/api/quotes", {
        method: "POST",
        headers: { "Content-Type": "application/json", ...SERVICE_HEADERS },
        body: JSON.stringify(VALID_BODY),
      }),
    );
    const json = await response.json();

    expect(response.status).toBe(200);
    expect(json.data.status).toBe("processing");
    expect(enqueueQuoteJob).toHaveBeenCalled();
  });

  it("TC-104：L2 命中 → done + source=cache", async () => {
    vi.mocked(getL2).mockResolvedValue({ quotes: FOUR_TIERS });

    const response = await POST(
      new Request("http://localhost/api/quotes", {
        method: "POST",
        headers: { "Content-Type": "application/json", ...SERVICE_HEADERS },
        body: JSON.stringify(VALID_BODY),
      }),
    );
    const json = await response.json();

    expect(json.data.status).toBe("done");
    expect(json.data.source_type).toBe("cache");
    expect(enqueueQuoteJob).not.toHaveBeenCalled();
  });

  it("TC-105：L1 幂等 → 返回原 quote_id", async () => {
    vi.mocked(checkIdempotency).mockResolvedValue({
      hit: true,
      quoteId: "QTE_EXISTING",
      response: {
        quote_id: "QTE_EXISTING",
        status: "done",
        source_type: "rpa",
      },
    });

    const response = await POST(
      new Request("http://localhost/api/quotes", {
        method: "POST",
        headers: { "Content-Type": "application/json", ...SERVICE_HEADERS },
        body: JSON.stringify(VALID_BODY),
      }),
    );
    const json = await response.json();

    expect(json.data.quote_id).toBe("QTE_EXISTING");
    expect(prisma.quoteRecord.create).not.toHaveBeenCalled();
  });

  it("TC-106/107：相同货物 hash 一致；service_level 不影响 hash", () => {
    const pickup = serializeAddress({
      street: "1234 Warehouse Blvd",
      city: "Los Angeles",
      state: "CA",
      zip: "90001",
      place_id: "ChIJ_pickup",
    });
    const delivery = serializeAddress({
      street: "5678 Distribution Dr",
      city: "Dallas",
      state: "TX",
      zip: "75201",
      place_id: "ChIJ_delivery",
    });
    const base = {
      pickupSerialized: pickup,
      deliverySerialized: delivery,
      weightLb: 500,
      dimLIn: 48,
      dimWIn: 40,
      dimHIn: 48,
      palletCount: 2,
      cargoType: "general_freight",
    };
    const h1 = buildCargoHash(base);
    const h2 = buildCargoHash({ ...base, cargoType: "general_freight" });
    expect(h1).toBe(h2);
    expect(h1).toHaveLength(32);
  });
});
