import { CARGO_LIMITS } from "@/lib/constants/cargo-limits";
import { snapMothershipReadyDateToWeekday } from "@/lib/mothership/logged-in-constraints";
import { z } from "zod";
import {
  buildCargoHash,
  buildLoggedInCargoHashExtras,
  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 {
  dimValueToIn,
  normalizeDimUnit,
  normalizeWeightUnit,
  toAxelWholeInches,
  toAxelWholePounds,
  weightUnitAmbiguousMessage,
  weightValueToLb,
} 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 weightUnitSchema = z
  .string({ errorMap: () => ({ message: "请选择有效的重量单位" }) })
  .superRefine((s, ctx) => {
    const ambiguous = weightUnitAmbiguousMessage(s);
    if (ambiguous) {
      ctx.addIssue({ code: z.ZodIssueCode.custom, message: ambiguous });
      return;
    }
    if (normalizeWeightUnit(s) === null) {
      ctx.addIssue({
        code: z.ZodIssueCode.custom,
        message: "请选择有效的重量单位（lb / kg / t）",
      });
    }
  })
  .transform((s) => normalizeWeightUnit(s)!);

const dimUnitSchema = z
  .string({ errorMap: () => ({ message: "请选择有效的尺寸单位" }) })
  .refine((s) => normalizeDimUnit(s) !== null, "请选择有效的尺寸单位（in / cm / m）")
  .transform((s) => normalizeDimUnit(s)!);

const positiveFinite = (label: string) =>
  z
    .number({ invalid_type_error: `请填写${label}` })
    .finite(`请填写有效的${label}`)
    .positive(`请填写有效的${label}`);

const weightSchema = z.object({
  value: positiveFinite("重量"),
  unit: weightUnitSchema,
});

const dimensionsSchema = z.object({
  length: positiveFinite("长度"),
  width: positiveFinite("宽度"),
  height: positiveFinite("高度"),
  unit: dimUnitSchema,
});

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(),
  pickup_accessorials: z.array(z.string().min(1).max(64)).max(20).optional(),
  delivery_accessorials: z.array(z.string().min(1).max(64)).max(20).optional(),
  ready_date: z
    .string()
    .regex(/^\d{4}-\d{2}-\d{2}$/, "可提货日期格式无效")
    .transform((v) => snapMothershipReadyDateToWeekday(v))
    .optional(),
  ready_time: z.string().min(1).max(32).optional(),
  /** 多行货物字段已为英制（weight_lb / *_in），不做公制换算 */
  cargo_lines: z
    .array(
      z.object({
        cargo_type: z.string().min(1).max(64),
        quantity: z.number().finite().positive().max(999),
        weight_lb: z.number().finite().positive().max(100_000),
        length_in: z.number().finite().positive().max(1_000),
        width_in: z.number().finite().positive().max(1_000),
        height_in: z.number().finite().positive().max(1_000),
      }),
    )
    .min(1)
    .max(20)
    .optional(),
  mothership_details: z
    .object({
      pickup: z
        .object({
          company_name: z.string().max(128).optional(),
          suite: z.string().max(64).optional(),
          contact_first: z.string().max(64).optional(),
          contact_last: z.string().max(64).optional(),
          contact_email: z.string().max(128).optional(),
          contact_phone: z.string().max(64).optional(),
          reference: z.string().max(128).optional(),
          notes: z.string().max(1000).optional(),
          opens_at: z.string().max(32).optional(),
          closes_at: z.string().max(32).optional(),
        })
        .optional(),
      delivery: z
        .object({
          company_name: z.string().max(128).optional(),
          suite: z.string().max(64).optional(),
          contact_first: z.string().max(64).optional(),
          contact_last: z.string().max(64).optional(),
          contact_email: z.string().max(128).optional(),
          contact_phone: z.string().max(64).optional(),
          reference: z.string().max(128).optional(),
          notes: z.string().max(1000).optional(),
          opens_at: z.string().max(32).optional(),
          closes_at: z.string().max(32).optional(),
        })
        .optional(),
      request_delivery_appointment: z.boolean().optional(),
      fba_number: z.string().max(128).optional(),
      fba_po_number: z.string().max(128).optional(),
      cargo: z
        .array(
          z.object({
            piece_count_type: z.string().max(32).optional(),
            piece_count_qty: z.number().min(0).max(999999).optional(),
            description: z.string().max(500).optional(),
            nmfc: z.string().max(64).optional(),
            hazmat: z.boolean().optional(),
            alcohol: z.boolean().optional(),
            tobacco: z.boolean().optional(),
          }),
        )
        .max(20)
        .optional(),
    })
    .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 weightLbRaw = weightValueToLb(data.weight.value, data.weight.unit);
  const dimLInRaw = dimValueToIn(data.dimensions.length, data.dimensions.unit);
  const dimWInRaw = dimValueToIn(data.dimensions.width, data.dimensions.unit);
  const dimHInRaw = dimValueToIn(data.dimensions.height, data.dimensions.unit);

  validateMothershipLimits(
    weightLbRaw,
    dimLInRaw,
    dimWInRaw,
    dimHInRaw,
    data.pallet_count,
  );

  const weightLb = toAxelWholePounds(weightLbRaw);
  const dimLIn = toAxelWholeInches(dimLInRaw);
  const dimWIn = toAxelWholeInches(dimWInRaw);
  const dimHIn = toAxelWholeInches(dimHInRaw);

  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,
    loggedInExtras: buildLoggedInCargoHashExtras({
      pickupAccessorials: data.pickup_accessorials,
      deliveryAccessorials: data.delivery_accessorials,
      readyDate: data.ready_date,
      readyTime: data.ready_time,
      cargoLines: data.cargo_lines?.map((row) => ({
        cargoType: row.cargo_type,
        quantity: row.quantity,
        weightLb: row.weight_lb,
        lengthIn: row.length_in,
        widthIn: row.width_in,
        heightIn: row.height_in,
      })),
    }),
  });

  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,
    pickupAccessorials: data.pickup_accessorials,
    deliveryAccessorials: data.delivery_accessorials,
    readyDate: data.ready_date,
    readyTime: data.ready_time,
    cargoLines: data.cargo_lines?.map((row) => ({
      cargoType: row.cargo_type,
      quantity: row.quantity,
      weightLb: row.weight_lb,
      lengthIn: row.length_in,
      widthIn: row.width_in,
      heightIn: row.height_in,
    })),
    mothershipDetails: data.mothership_details,
  };
}

export type { ValidationError };
