import { z } from 'zod';
import { Uuid, Code, DecimalString, NonNegativeDecimalString, PositiveDecimalString, AddressSchema, ContactSchema } from './common';
import {
  ACTIVE_STATUS, ZONE_TYPE, TEMPERATURE_CLASS, LOCATION_TYPE, LOCATION_USAGE, LOCATION_STATUS,
  ROTATION_RULE, STORAGE_CONDITION, PACK_LEVEL, PUTAWAY_STRATEGY, REASON_CODE_TYPE,
} from '../enums';

// ---------- Warehouse ----------
export const WarehouseSettingsSchema = z
  .object({
    receivingTolerancePct: z.number().min(0).max(100).default(0), // over-receipt tolerance
    requireLpnOnReceipt: z.boolean().default(true),
    autoCloseAsnWhenComplete: z.boolean().default(false),
    autoCreatePutawayTasks: z.boolean().default(true),
    defaultReceivingLocationId: z.string().uuid().optional(),
    defaultStagingLocationId: z.string().uuid().optional(),
    defaultPackingLocationId: z.string().uuid().optional(),
    lostAndFoundLocationId: z.string().uuid().optional(),
    pickConfirmationMode: z.enum(['SCAN_LOCATION_AND_SKU', 'SCAN_LPN', 'SCAN_LOCATION_ONLY']).default('SCAN_LOCATION_AND_SKU'),
    allowShortPick: z.boolean().default(true),
    requireGatePass: z.boolean().default(true),
    dayEndHour: z.number().int().min(0).max(23).default(23), // storage snapshot time (local)
  })
  .partial();
export type WarehouseSettings = z.infer<typeof WarehouseSettingsSchema>;

export const CreateWarehouseSchema = z.object({
  code: Code.max(20),
  name: z.string().min(1).max(120),
  address: AddressSchema.default({}),
  timezone: z.string().default('Asia/Kuwait'),
  settings: WarehouseSettingsSchema.default({}),
});
export type CreateWarehouseDto = z.infer<typeof CreateWarehouseSchema>;
export const UpdateWarehouseSchema = CreateWarehouseSchema.partial().extend({ status: z.enum(ACTIVE_STATUS).optional() });

// ---------- Zone ----------
export const CreateZoneSchema = z.object({
  warehouseId: Uuid,
  code: Code.max(20),
  name: z.string().min(1).max(120),
  type: z.enum(ZONE_TYPE).default('STORAGE'),
  temperatureClass: z.enum(TEMPERATURE_CLASS).default('AMBIENT'),
  pickSequence: z.number().int().default(100),
});
export type CreateZoneDto = z.infer<typeof CreateZoneSchema>;
export const UpdateZoneSchema = CreateZoneSchema.omit({ warehouseId: true }).partial().extend({ status: z.enum(ACTIVE_STATUS).optional() });

// ---------- Location ----------
export const CreateLocationSchema = z.object({
  warehouseId: Uuid,
  zoneId: Uuid,
  code: Code.max(40),
  barcode: z.string().max(60).optional(), // defaults to code
  checkDigit: z.string().max(6).optional(),
  aisle: z.string().max(10).optional(),
  bay: z.string().max(10).optional(),
  level: z.string().max(10).optional(),
  position: z.string().max(10).optional(),
  type: z.enum(LOCATION_TYPE).default('PALLET_RACK'),
  usage: z.enum(LOCATION_USAGE).default('RESERVE'),
  abcClass: z.enum(['A', 'B', 'C']).optional(),
  lengthCm: NonNegativeDecimalString.optional(),
  widthCm: NonNegativeDecimalString.optional(),
  heightCm: NonNegativeDecimalString.optional(),
  cubicCapacityM3: NonNegativeDecimalString.optional(),
  weightCapacityKg: NonNegativeDecimalString.optional(),
  maxLpns: z.number().int().min(0).optional(),
  stackLimit: z.number().int().min(1).optional(),
  allowMixedSku: z.boolean().default(true),
  allowMixedLots: z.boolean().default(true),
  pickSequence: z.number().int().default(0),
  putawaySequence: z.number().int().default(0),
  attributes: z.record(z.unknown()).default({}),
});
export type CreateLocationDto = z.infer<typeof CreateLocationSchema>;
export const UpdateLocationSchema = CreateLocationSchema.omit({ warehouseId: true }).partial().extend({ status: z.enum(LOCATION_STATUS).optional() });

/** Bulk generator: creates aisle/bay/level/position grid in one call. */
export const GenerateLocationsSchema = z.object({
  warehouseId: Uuid,
  zoneId: Uuid,
  pattern: z.string().default('{aisle}-{bay}-{level}-{position}'), // tokens: {aisle} {bay} {level} {position}
  aisles: z.array(z.string().max(10)).min(1),
  baysFrom: z.number().int().min(1),
  baysTo: z.number().int().min(1),
  levels: z.array(z.string().max(10)).min(1),
  positions: z.array(z.string().max(10)).default(['']),
  padBays: z.number().int().min(0).max(4).default(2),
  type: z.enum(LOCATION_TYPE).default('PALLET_RACK'),
  usage: z.enum(LOCATION_USAGE).default('RESERVE'),
  groundLevelIsPick: z.boolean().default(false),
  cubicCapacityM3: NonNegativeDecimalString.optional(),
  weightCapacityKg: NonNegativeDecimalString.optional(),
  maxLpns: z.number().int().min(1).default(1),
});
export type GenerateLocationsDto = z.infer<typeof GenerateLocationsSchema>;

// ---------- Storer ----------
export const StorerSettingsSchema = z
  .object({
    receivingTolerancePct: z.number().min(0).max(100).optional(),
    defaultRotation: z.enum(ROTATION_RULE).optional(),
    minShelfLifeOutboundDays: z.number().int().min(0).optional(),
    rcrEmails: z.array(z.string().email()).default([]),
    orderConfirmationEmails: z.array(z.string().email()).default([]),
    requireSerialOnOutbound: z.boolean().default(false),
    autoSendRcr: z.boolean().default(false),
    allowedWarehouseIds: z.array(z.string().uuid()).optional(),
  })
  .partial();
export type StorerSettings = z.infer<typeof StorerSettingsSchema>;

export const CreateStorerSchema = z.object({
  code: Code.max(20),
  name: z.string().min(1).max(200),
  legalName: z.string().max(200).optional(),
  taxId: z.string().max(60).optional(),
  address: AddressSchema.default({}),
  contacts: z.array(ContactSchema).default([]),
  settings: StorerSettingsSchema.default({}),
  warehouseIds: z.array(Uuid).default([]),
});
export type CreateStorerDto = z.infer<typeof CreateStorerSchema>;
export const UpdateStorerSchema = CreateStorerSchema.partial().extend({ status: z.enum(ACTIVE_STATUS).optional() });

// ---------- Supplier / Customer / Carrier ----------
export const CreateSupplierSchema = z.object({
  storerId: Uuid.nullable().optional(),
  code: Code.max(30),
  name: z.string().min(1).max(200),
  address: AddressSchema.default({}),
  contacts: z.array(ContactSchema).default([]),
});
export const UpdateSupplierSchema = CreateSupplierSchema.partial().extend({ status: z.enum(ACTIVE_STATUS).optional() });

export const CreateCustomerSchema = z.object({
  storerId: Uuid,
  code: Code.max(30),
  name: z.string().min(1).max(200),
  address: AddressSchema.default({}),
  contacts: z.array(ContactSchema).default([]),
});
export const UpdateCustomerSchema = CreateCustomerSchema.partial().extend({ status: z.enum(ACTIVE_STATUS).optional() });

export const CreateCarrierSchema = z.object({
  code: Code.max(30),
  name: z.string().min(1).max(200),
  serviceLevels: z.array(z.string().max(40)).default([]),
  contacts: z.array(ContactSchema).default([]),
});
export const UpdateCarrierSchema = CreateCarrierSchema.partial().extend({ status: z.enum(ACTIVE_STATUS).optional() });

// ---------- SKU ----------
export const SkuPackSchema = z.object({
  level: z.enum(PACK_LEVEL),
  uomCode: z.string().min(1).max(10),
  qtyInBase: PositiveDecimalString,
  lengthCm: NonNegativeDecimalString.optional(),
  widthCm: NonNegativeDecimalString.optional(),
  heightCm: NonNegativeDecimalString.optional(),
  weightKg: NonNegativeDecimalString.optional(),
  barcode: z.string().max(60).optional(),
  palletTi: z.number().int().min(1).optional(),
  palletHi: z.number().int().min(1).optional(),
  isDefaultReceiving: z.boolean().default(false),
  isDefaultShipping: z.boolean().default(false),
});
export type SkuPackDto = z.infer<typeof SkuPackSchema>;

export const CreateSkuSchema = z.object({
  storerId: Uuid,
  code: Code.max(50),
  description: z.string().min(1).max(200),
  longDescription: z.string().max(2000).optional(),
  category: z.string().max(60).optional(),
  brand: z.string().max(60).optional(),
  groupCode: z.string().max(30).optional(),
  materialType: z.string().max(30).optional(),
  storageCondition: z.enum(STORAGE_CONDITION).default('AMBIENT'),
  rotationRule: z.enum(ROTATION_RULE).default('FIFO'),
  lotControlled: z.boolean().default(false),
  expiryControlled: z.boolean().default(false),
  serialControlled: z.boolean().default(false),
  captureSerialOnReceipt: z.boolean().default(true),
  shelfLifeDays: z.number().int().min(0).optional(),
  minShelfLifeInboundDays: z.number().int().min(0).optional(),
  minShelfLifeOutboundDays: z.number().int().min(0).optional(),
  baseUom: z.string().min(1).max(10).default('EA'),
  weightKg: NonNegativeDecimalString.optional(),
  cbm: NonNegativeDecimalString.optional(),
  lengthCm: NonNegativeDecimalString.optional(),
  widthCm: NonNegativeDecimalString.optional(),
  heightCm: NonNegativeDecimalString.optional(),
  hsCode: z.string().max(20).optional(),
  countryOfOrigin: z.string().length(2).optional(),
  hazmat: z.boolean().default(false),
  hazmatClass: z.string().max(20).optional(),
  countFrequencyDays: z.number().int().min(1).optional(),
  unitValue: NonNegativeDecimalString.optional(),
  barcodes: z.array(z.object({ barcode: z.string().min(1).max(60), packLevel: z.enum(PACK_LEVEL).default('EACH'), isPrimary: z.boolean().default(false) })).default([]),
  packs: z.array(SkuPackSchema).default([]),
  attributes: z.record(z.unknown()).default({}),
});
export type CreateSkuDto = z.infer<typeof CreateSkuSchema>;
export const UpdateSkuSchema = CreateSkuSchema.omit({ storerId: true }).partial().extend({ status: z.enum(ACTIVE_STATUS).optional() });

export const UpsertSlottingSchema = z.object({
  skuId: Uuid,
  warehouseId: Uuid,
  pickLocationId: Uuid,
  minQty: NonNegativeDecimalString,
  maxQty: PositiveDecimalString,
  replenishPackLevel: z.enum(PACK_LEVEL).default('CASE'),
  active: z.boolean().default(true),
});
export type UpsertSlottingDto = z.infer<typeof UpsertSlottingSchema>;

// ---------- Rules ----------
export const PutawayCriteriaSchema = z
  .object({
    storerIds: z.array(Uuid).optional(),
    storageConditions: z.array(z.enum(STORAGE_CONDITION)).optional(),
    categories: z.array(z.string()).optional(),
    hazmat: z.boolean().optional(),
    packLevels: z.array(z.enum(PACK_LEVEL)).optional(),
    abcClasses: z.array(z.string()).optional(),
    skuIds: z.array(Uuid).optional(),
    asnTypes: z.array(z.string()).optional(),
  })
  .partial();
export type PutawayCriteria = z.infer<typeof PutawayCriteriaSchema>;

export const CreatePutawayRuleSchema = z.object({
  warehouseId: Uuid,
  name: z.string().min(1).max(120),
  priority: z.number().int().default(100),
  criteria: PutawayCriteriaSchema.default({}),
  strategy: z.enum(PUTAWAY_STRATEGY).default('EMPTY_LOCATION'),
  targetZoneIds: z.array(Uuid).default([]),
  allowMixedSku: z.boolean().default(true),
  allowMixedLots: z.boolean().default(false),
  respectCapacity: z.boolean().default(true),
  active: z.boolean().default(true),
});
export type CreatePutawayRuleDto = z.infer<typeof CreatePutawayRuleSchema>;
export const UpdatePutawayRuleSchema = CreatePutawayRuleSchema.omit({ warehouseId: true }).partial();

export const CreateAllocationRuleSchema = z.object({
  storerId: Uuid.nullable().optional(),
  skuId: Uuid.nullable().optional(),
  name: z.string().min(1).max(120),
  priority: z.number().int().default(100),
  rotation: z.enum(ROTATION_RULE).default('FIFO'),
  pickFaceFirst: z.boolean().default(true),
  fullLpnFirst: z.boolean().default(true),
  allowPartialLpn: z.boolean().default(true),
  minShelfLifeDays: z.number().int().min(0).optional(),
  allowedStatuses: z.array(z.string()).default(['AVAILABLE']),
  zoneIds: z.array(Uuid).default([]),
  active: z.boolean().default(true),
});
export type CreateAllocationRuleDto = z.infer<typeof CreateAllocationRuleSchema>;
export const UpdateAllocationRuleSchema = CreateAllocationRuleSchema.partial();

export const CreateReasonCodeSchema = z.object({
  type: z.enum(REASON_CODE_TYPE),
  code: Code.max(20),
  description: z.string().min(1).max(200),
  requiresApproval: z.boolean().default(false),
  requiresPhoto: z.boolean().default(false),
});
export const UpdateReasonCodeSchema = CreateReasonCodeSchema.partial().extend({ active: z.boolean().optional() });

export const DecimalQty = DecimalString;
