import { CARGO_LIMITS } from "@/lib/constants/cargo-limits";
import { z } from "zod";
import {
  buildCargoHash,
  serializeAddress,
} from "@/modules/quote/cargo-hash";
import type {
  NormalizedCargo,
  QuoteRequestBody,
  ValidationError,
} from "@/modules/quote/types";
import {
  SecurityValidationError,
  ValidationError as ValidationErrorClass,
} from "@/modules/quote/types";
import { cmToIn, kgToLb } from "@/modules/quote/unit-converter";

/** 查价货物硬限（PRD §4.2.8） */
export const MOTHERSHIP_LIMITS = CARGO_LIMITS;

export const CARGO_TYPES = [
  "general_freight",
  "machinery",
  "furniture",
  "electronics",
  "building_materials",
  "auto_parts",
  "food_nonperishable",
  "apparel",
  "other",
] as const;

const UUID_V4_REGEX =
  /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;

const US_ZIP_REGEX = /^\d{5}(-\d{4})?$/;

const SQL_INJECTION_PATTERNS = [
  /(\bunion\b.*\bselect\b)/i,
  /(\bdrop\b.*\btable\b)/i,
  /(\binsert\b.*\binto\b)/i,
  /(\bdelete\b.*\bfrom\b)/i,
  /(--|\/\*|\*\/)/,
  /(\bor\b\s+\d+\s*=\s*\d+)/i,
  /(<script|javascript:)/i,
  /(\bexec\b|\bxp_)/i,
];

const addressSchema = z.object({
  street: z.string().min(1, "请填写街道地址"),
  city: z.string().min(1, "请填写城市"),
  state: z.string().min(2, "请填写州/省").max(2, "请填写州/省"),
  zip: z
    .string()
    .refine((v) => v === "" || US_ZIP_REGEX.test(v), "邮编格式无效")
    .default(""),
  place_id: z.string().min(1, "请从地址列表中选择有效地址"),
  formatted_address: z.string().min(1, "请填写完整地址"),
  selected_from_suggestions: z.literal(true, {
    errorMap: () => ({ message: "请从地址列表中选择有效地址" }),
  }),
  mothership_option_id: z.string().min(1, "请确认 MotherShip 精确地址"),
  mothership_display_label: z.string().min(1, "请确认 MotherShip 精确地址"),
  selected_from_mothership: z.literal(true, {
    errorMap: () => ({
      message: "提货/派送地址须在 MotherShip 候选列表中确认后再询价",
    }),
  }),
});

const weightSchema = z.object({
  value: z.number({ invalid_type_error: "请填写重量" }),
  unit: z.enum(["lb", "kg"], { errorMap: () => ({ message: "请选择有效的单位" }) }),
});

const dimensionsSchema = z.object({
  length: z.number({ invalid_type_error: "请填写长度" }),
  width: z.number({ invalid_type_error: "请填写宽度" }),
  height: z.number({ invalid_type_error: "请填写高度" }),
  unit: z.enum(["in", "cm"], {
    errorMap: () => ({ message: "请选择有效的单位" }),
  }),
});

const quoteBodySchema = z.object({
  request_id: z.string().min(1, "请填写请求标识"),
  customer_id: z.string().min(1, "请填写客户标识"),
  pickup_address: addressSchema,
  delivery_address: addressSchema,
  weight: weightSchema,
  dimensions: dimensionsSchema,
  pallet_count: z.number({ invalid_type_error: "请填写托盘数" }),
  cargo_type: z.enum(CARGO_TYPES, {
    errorMap: () => ({ message: "请选择有效的货物类型" }),
  }),
  service_level: z.enum(["standard", "guaranteed"]).optional(),
  rate_option: z.enum(["lowest", "fastest"]).optional(),
  display_unit: z.enum(["imperial", "metric"]).optional(),
  quote_session_id: z.string().uuid("会话标识无效").optional(),
});

function containsSqlInjection(value: string): boolean {
  return SQL_INJECTION_PATTERNS.some((pattern) => pattern.test(value));
}

function checkAddressInjection(
  addr: {
    street: string;
    city: string;
    state: string;
    zip: string;
    place_id: string;
    formatted_address: string;
  },
  label: string,
): void {
  const fields = [
    addr.street,
    addr.city,
    addr.state,
    addr.zip,
    addr.formatted_address,
    addr.place_id,
  ];
  if (fields.some(containsSqlInjection)) {
    throw new SecurityValidationError(`${label}无效，请检查`);
  }
}

function validateZip(zip: string, label: string): void {
  if (zip.trim() === "") {
    return;
  }
  if (!US_ZIP_REGEX.test(zip)) {
    throw new ValidationErrorClass(`${label}邮编无效`);
  }
}

function validateMothershipLimits(
  weightLb: number,
  dimLIn: number,
  dimWIn: number,
  dimHIn: number,
  palletCount: number,
): void {
  const { palletCount: pc, dimIn, weightLbPerPallet, totalWeightLbMax } =
    MOTHERSHIP_LIMITS;

  if (
    !Number.isInteger(palletCount) ||
    palletCount < pc.min ||
    palletCount > pc.max
  ) {
    throw new ValidationErrorClass(
      `托盘数超出 Mothership 允许范围（${pc.min}–${pc.max}）`,
    );
  }

  if (weightLb < weightLbPerPallet.min || weightLb > weightLbPerPallet.max) {
    throw new ValidationErrorClass("重量超出 Mothership 允许范围");
  }

  if (
    dimLIn < dimIn.min ||
    dimLIn > dimIn.lengthMax ||
    dimWIn < dimIn.min ||
    dimWIn > dimIn.widthMax ||
    dimHIn < dimIn.min ||
    dimHIn > dimIn.heightMax
  ) {
    throw new ValidationErrorClass("尺寸超出 Mothership 允许范围");
  }

  const totalWeight = weightLb * palletCount;
  if (totalWeight > totalWeightLbMax) {
    throw new ValidationErrorClass(
      `整票总重超出 Mothership 允许范围（≤${totalWeightLbMax} lb）`,
    );
  }
}

/** E4.7：拒绝 quantity 件数字段 */
export function rejectQuantityField(body: Record<string, unknown>): void {
  if ("quantity" in body) {
    throw new ValidationErrorClass("请使用托盘数 pallet_count");
  }
}

/**
 * 校验 + 单位换算 + cargo_hash 生成
 * @throws ValidationError
 */
export function validateQuoteInput(
  body: unknown,
): NormalizedCargo {
  if (typeof body !== "object" || body === null) {
    throw new ValidationErrorClass("请求体无效");
  }

  rejectQuantityField(body as Record<string, unknown>);

  const parsed = quoteBodySchema.safeParse(body);
  if (!parsed.success) {
    const first = parsed.error.errors[0];
    throw new ValidationErrorClass(first?.message ?? "请求参数无效");
  }

  const data = parsed.data as QuoteRequestBody;

  if (!UUID_V4_REGEX.test(data.request_id)) {
    throw new ValidationErrorClass("请求标识无效");
  }

  checkAddressInjection(data.pickup_address, "发货地址");
  checkAddressInjection(data.delivery_address, "收货地址");
  validateZip(data.pickup_address.zip, "发货地址");
  validateZip(data.delivery_address.zip, "收货地址");

  const weightLb =
    data.weight.unit === "kg" ? kgToLb(data.weight.value) : data.weight.value;
  const dimLIn =
    data.dimensions.unit === "cm"
      ? cmToIn(data.dimensions.length)
      : data.dimensions.length;
  const dimWIn =
    data.dimensions.unit === "cm"
      ? cmToIn(data.dimensions.width)
      : data.dimensions.width;
  const dimHIn =
    data.dimensions.unit === "cm"
      ? cmToIn(data.dimensions.height)
      : data.dimensions.height;

  validateMothershipLimits(
    weightLb,
    dimLIn,
    dimWIn,
    dimHIn,
    data.pallet_count,
  );

  const pickupSerialized = serializeAddress(data.pickup_address);
  const deliverySerialized = serializeAddress(data.delivery_address);

  const cargoHash = buildCargoHash({
    pickupSerialized,
    deliverySerialized,
    weightLb,
    dimLIn,
    dimWIn,
    dimHIn,
    palletCount: data.pallet_count,
    cargoType: data.cargo_type,
  });

  return {
    requestId: data.request_id,
    customerId: data.customer_id,
    pickupAddress: data.pickup_address,
    deliveryAddress: data.delivery_address,
    weightLb,
    dimLIn,
    dimWIn,
    dimHIn,
    palletCount: data.pallet_count,
    cargoType: data.cargo_type,
    serviceLevel: data.service_level ?? "standard",
    rateOption: data.rate_option ?? "lowest",
    cargoHash,
    pickupSerialized,
    deliverySerialized,
    quoteSessionId: data.quote_session_id,
  };
}

export type { ValidationError };
