import { z } from 'zod';
import { Uuid, DateOnly, IsoDateTime, PositiveDecimalString, NonNegativeDecimalString, AddressSchema } from './common';
import { ORDER_TYPE, ORDER_STATUS, PACK_LEVEL, WAVE_STRATEGY, RETURN_DISPOSITION } from '../enums';

export const ShipToSchema = z.object({
  name: z.string().max(200).optional(),
  contact: z.string().max(120).optional(),
  phone: z.string().max(40).optional(),
  email: z.string().email().optional(),
  address: AddressSchema.optional(),
  deliveryLocationCode: z.string().max(40).optional(), // store / branch code
  instructions: z.string().max(1000).optional(),
});
export type ShipTo = z.infer<typeof ShipToSchema>;

export const OrderLineInputSchema = z.object({
  lineNo: z.number().int().min(1).optional(),
  skuId: Uuid.optional(),
  skuCode: z.string().max(50).optional(),
  orderedQty: PositiveDecimalString,
  packLevel: z.enum(PACK_LEVEL).default('EACH'),
  requestedLot: z.string().max(60).optional(),
  requestedSerials: z.array(z.string().max(80)).default([]),
  minExpiryDate: DateOnly.optional(),
  externalLineRef: z.string().max(60).optional(),
  unitPrice: NonNegativeDecimalString.optional(),
  notes: z.string().max(500).optional(),
}).refine((l) => l.skuId || l.skuCode, { message: 'skuId or skuCode is required' });
export type OrderLineInputDto = z.infer<typeof OrderLineInputSchema>;

export const CreateOrderSchema = z.object({
  warehouseId: Uuid,
  storerId: Uuid,
  externalRef: z.string().max(60).optional(),
  type: z.enum(ORDER_TYPE).default('SALES'),
  priority: z.number().int().min(1).max(9).default(5),
  customerId: Uuid.optional(),
  customerCode: z.string().max(30).optional(),
  shipTo: ShipToSchema.default({}),
  carrierId: Uuid.optional(),
  carrierName: z.string().max(120).optional(),
  serviceLevel: z.string().max(40).optional(),
  requestedShipDate: DateOnly.optional(),
  dueDate: IsoDateTime.optional(),
  notes: z.string().max(2000).optional(),
  attributes: z.record(z.unknown()).default({}),
  lines: z.array(OrderLineInputSchema).min(1),
});
export type CreateOrderDto = z.infer<typeof CreateOrderSchema>;

export const UpdateOrderSchema = CreateOrderSchema.omit({ warehouseId: true, storerId: true, lines: true }).partial().extend({
  lines: z.array(OrderLineInputSchema).optional(), // replacement allowed only while OPEN / ON_HOLD with no allocations
});

export const OrderListQuerySchema = z.object({
  warehouseId: Uuid.optional(),
  storerId: Uuid.optional(),
  status: z.enum(ORDER_STATUS).optional(),
  statuses: z.string().optional(),
  externalRef: z.string().optional(),
  waveId: Uuid.optional(),
  loadId: Uuid.optional(),
  shipFrom: DateOnly.optional(),
  shipTo: DateOnly.optional(),
});

export const HoldOrderSchema = z.object({ reason: z.string().min(1).max(500) });
export const CancelOrderSchema = z.object({ reason: z.string().min(1).max(500), reasonCodeId: Uuid.optional() });

export const AllocateOrdersSchema = z.object({
  orderIds: z.array(Uuid).min(1),
  allowPartial: z.boolean().default(true),
});

export const CreateWaveSchema = z.object({
  warehouseId: Uuid,
  strategy: z.enum(WAVE_STRATEGY).default('BY_ORDER'),
  orderIds: z.array(Uuid).optional(), // explicit list, or
  criteria: z
    .object({
      storerId: Uuid.optional(),
      carrierId: Uuid.optional(),
      shipDateFrom: DateOnly.optional(),
      shipDateTo: DateOnly.optional(),
      priorityMax: z.number().int().min(1).max(9).optional(),
      maxOrders: z.number().int().min(1).max(2000).optional(),
      deliveryLocationCode: z.string().optional(),
    })
    .optional(),
  allocateNow: z.boolean().default(true),
  releaseNow: z.boolean().default(false),
  allowPartial: z.boolean().default(true),
});
export type CreateWaveDto = z.infer<typeof CreateWaveSchema>;

export const WaveQuerySchema = z.object({ warehouseId: Uuid.optional(), status: z.string().optional() });

// ---------- Packing ----------
export const StartPackingSchema = z.object({
  orderId: Uuid,
  packingLocationId: Uuid.optional(),
});

export const PackCartonSchema = z.object({
  shipmentId: Uuid,
  cartonNumber: z.string().max(40).optional(), // auto when omitted
  type: z.enum(['CARTON', 'PALLET']).default('CARTON'),
  lpnNumber: z.string().max(40).optional(), // pallet LPN when type PALLET
  lines: z
    .array(
      z.object({
        orderLineId: Uuid,
        qty: PositiveDecimalString,
        packLevel: z.enum(PACK_LEVEL).optional(),
        lotNumber: z.string().optional(),
        expiryDate: DateOnly.optional(),
        serials: z.array(z.string()).default([]),
      }),
    )
    .min(1),
  weightKg: NonNegativeDecimalString.optional(),
  lengthCm: NonNegativeDecimalString.optional(),
  widthCm: NonNegativeDecimalString.optional(),
  heightCm: NonNegativeDecimalString.optional(),
  trackingNo: z.string().max(60).optional(),
});
export type PackCartonDto = z.infer<typeof PackCartonSchema>;

export const CompletePackingSchema = z.object({
  shipmentId: Uuid,
  stagingLocationId: Uuid.optional(),
  stagingLocationCode: z.string().optional(),
});

// ---------- Loads / shipping ----------
export const CreateLoadSchema = z.object({
  warehouseId: Uuid,
  carrierId: Uuid.optional(),
  carrierName: z.string().max(120).optional(),
  vehicleNo: z.string().max(40).optional(),
  vehicleType: z.string().max(20).optional(),
  driverName: z.string().max(120).optional(),
  driverIdNo: z.string().max(60).optional(),
  driverPhone: z.string().max(40).optional(),
  dockLocationId: Uuid.optional(),
  scheduledAt: IsoDateTime.optional(),
  shipmentIds: z.array(Uuid).default([]),
  notes: z.string().max(1000).optional(),
});
export type CreateLoadDto = z.infer<typeof CreateLoadSchema>;
export const UpdateLoadSchema = CreateLoadSchema.omit({ warehouseId: true, shipmentIds: true }).partial();

export const LoadShipmentsSchema = z.object({ shipmentIds: z.array(Uuid).min(1) });

/** Confirm load departed = Post Goods Issue for all shipments on it. */
export const DispatchLoadSchema = z.object({
  sealNo: z.string().max(40).optional(),
  temperatureAtDispatch: z.number().optional(),
  departedAt: IsoDateTime.optional(),
  generateGatePass: z.boolean().default(true),
  generateDeliveryNotes: z.boolean().default(true),
});

export const RecordPodSchema = z.object({
  receivedBy: z.string().max(120),
  signedAt: IsoDateTime.optional(),
  remarks: z.string().max(1000).optional(),
  rejections: z
    .array(z.object({ shipmentId: Uuid, orderLineId: Uuid.optional(), skuId: Uuid.optional(), qty: PositiveDecimalString, reason: z.string().max(300) }))
    .default([]),
  attachmentIds: z.array(Uuid).default([]),
});

/** Ship a single order directly (no load), e.g. courier pickup. Posts goods issue. */
export const ShipOrderSchema = z.object({
  carrierName: z.string().max(120).optional(),
  trackingNo: z.string().max(60).optional(),
  shippedAt: IsoDateTime.optional(),
});

// ---------- Returns ----------
export const CreateReturnSchema = z.object({
  warehouseId: Uuid,
  storerId: Uuid,
  originalOrderId: Uuid.optional(),
  customerRef: z.string().max(60).optional(),
  reasonCodeId: Uuid.optional(),
  notes: z.string().max(2000).optional(),
  lines: z
    .array(
      z.object({
        skuId: Uuid.optional(),
        skuCode: z.string().optional(),
        expectedQty: PositiveDecimalString,
        serials: z.array(z.string()).default([]),
        reasonCodeId: Uuid.optional(),
        notes: z.string().max(500).optional(),
      }),
    )
    .min(1),
  authoriseNow: z.boolean().default(true), // creates the RETURN ASN immediately
});
export type CreateReturnDto = z.infer<typeof CreateReturnSchema>;

export const DispositionReturnSchema = z.object({
  lines: z.array(z.object({ lineId: Uuid, disposition: z.enum(RETURN_DISPOSITION), notes: z.string().max(500).optional() })).min(1),
});
