import { z } from 'zod';
import { Uuid, DateOnly, PositiveDecimalString, DecimalString, NonNegativeDecimalString } from './common';
import { INVENTORY_STATUS, HOLD_SCOPE, COUNT_TYPE, PACK_LEVEL, LPN_TYPE } from '../enums';

export const InventoryQuerySchema = z.object({
  warehouseId: Uuid.optional(),
  storerId: Uuid.optional(),
  skuId: Uuid.optional(),
  skuCode: z.string().optional(),
  locationId: Uuid.optional(),
  locationCode: z.string().optional(),
  zoneId: Uuid.optional(),
  lpnNumber: z.string().optional(),
  lotNumber: z.string().optional(),
  status: z.enum(INVENTORY_STATUS).optional(),
  expiryBefore: DateOnly.optional(),
  nonZeroOnly: z.coerce.boolean().default(true),
  groupBy: z.enum(['BALANCE', 'SKU', 'LOCATION', 'LPN', 'LOT']).default('BALANCE'),
});
export type InventoryQueryDto = z.infer<typeof InventoryQuerySchema>;

export const MovementQuerySchema = z.object({
  warehouseId: Uuid.optional(),
  storerId: Uuid.optional(),
  skuId: Uuid.optional(),
  locationId: Uuid.optional(),
  lpnId: Uuid.optional(),
  type: z.string().optional(),
  refType: z.string().optional(),
  refId: Uuid.optional(),
  from: DateOnly.optional(),
  to: DateOnly.optional(),
});

/** Move a quantity (or whole LPN) from one location to another. */
export const MoveStockSchema = z.object({
  warehouseId: Uuid,
  mode: z.enum(['LPN', 'QTY']).default('LPN'),
  lpnNumber: z.string().max(40).optional(), // LPN mode: move entire LPN
  // QTY mode fields
  skuId: Uuid.optional(),
  barcode: z.string().optional(),
  fromLocationId: Uuid.optional(),
  fromLocationCode: z.string().optional(),
  fromLpnNumber: z.string().optional(),
  lotNumber: z.string().optional(),
  expiryDate: DateOnly.optional(),
  qty: PositiveDecimalString.optional(),
  packLevel: z.enum(PACK_LEVEL).optional(),
  inventoryStatus: z.enum(INVENTORY_STATUS).optional(),
  // destination
  toLocationId: Uuid.optional(),
  toLocationCode: z.string().optional(),
  toLpnNumber: z.string().optional(), // merge into / create LPN at destination (QTY mode)
  reasonCodeId: Uuid.optional(),
  notes: z.string().max(500).optional(),
  idempotencyKey: z.string().max(100).optional(),
});
export type MoveStockDto = z.infer<typeof MoveStockSchema>;

export const CreateLpnSchema = z.object({
  warehouseId: Uuid,
  type: z.enum(LPN_TYPE).default('PALLET'),
  count: z.number().int().min(1).max(500).default(1),
  locationId: Uuid.optional(),
  storerId: Uuid.optional(),
});

export const PlaceHoldSchema = z.object({
  warehouseId: Uuid,
  scope: z.enum(HOLD_SCOPE),
  targetId: z.string().min(1).max(80), // uuid for BALANCE/LPN/SKU/LOCATION, lot number for LOT
  storerId: Uuid.optional(), // required for LOT scope
  skuId: Uuid.optional(), // narrows LOT scope
  reasonCodeId: Uuid.optional(),
  toStatus: z.enum(['HOLD', 'QUARANTINE', 'DAMAGED', 'QC_PENDING']).default('HOLD'),
  notes: z.string().max(1000).optional(),
});
export type PlaceHoldDto = z.infer<typeof PlaceHoldSchema>;

export const ReleaseHoldSchema = z.object({
  toStatus: z.enum(['AVAILABLE', 'DAMAGED', 'QUARANTINE']).default('AVAILABLE'),
  notes: z.string().max(1000).optional(),
});

export const AdjustmentLineInputSchema = z.object({
  skuId: Uuid,
  locationId: Uuid,
  lpnNumber: z.string().max(40).optional(),
  lpnId: Uuid.optional(), // internal callers (counts) pass the id directly
  lotNumber: z.string().max(60).optional(),
  expiryDate: DateOnly.optional(),
  fromStatus: z.enum(INVENTORY_STATUS).default('AVAILABLE'),
  toStatus: z.enum(INVENTORY_STATUS).optional(),
  qtyDelta: DecimalString, // +/- in base units; 0 for status change
  serials: z.array(z.string().max(80)).default([]),
  notes: z.string().max(500).optional(),
});
export type AdjustmentLineInputDto = z.infer<typeof AdjustmentLineInputSchema>;

export const CreateAdjustmentSchema = z.object({
  warehouseId: Uuid,
  storerId: Uuid,
  reasonCodeId: Uuid,
  notes: z.string().max(2000).optional(),
  lines: z.array(AdjustmentLineInputSchema).min(1),
  submit: z.boolean().default(true), // submit for approval immediately
});
export type CreateAdjustmentDto = z.infer<typeof CreateAdjustmentSchema>;

export const DecideAdjustmentSchema = z.object({
  decision: z.enum(['APPROVE', 'REJECT']),
  notes: z.string().max(1000).optional(),
});

export const CreateCycleCountSchema = z.object({
  warehouseId: Uuid,
  type: z.enum(COUNT_TYPE).default('CYCLE'),
  storerId: Uuid.optional(),
  scope: z
    .object({
      zoneIds: z.array(Uuid).optional(),
      locationIds: z.array(Uuid).optional(),
      skuIds: z.array(Uuid).optional(),
      abcClasses: z.array(z.string()).optional(),
      includeEmptyLocations: z.boolean().default(false),
      maxLocations: z.number().int().min(1).max(5000).optional(),
      notCountedSinceDays: z.number().int().min(0).optional(),
    })
    .default({}),
  blind: z.boolean().default(true),
  recountTolerancePct: z.number().min(0).max(100).default(0),
  scheduledFor: z.string().datetime({ offset: true }).optional(),
  notes: z.string().max(2000).optional(),
  releaseNow: z.boolean().default(false),
});
export type CreateCycleCountDto = z.infer<typeof CreateCycleCountSchema>;

/** RF count entry for one location. Each entry is a (sku, lpn, lot, expiry) observation. */
export const SubmitCountSchema = z.object({
  locationId: Uuid.optional(),
  locationCode: z.string().optional(),
  entries: z
    .array(
      z.object({
        skuId: Uuid.optional(),
        barcode: z.string().optional(),
        lpnNumber: z.string().optional(),
        lotNumber: z.string().optional(),
        expiryDate: DateOnly.optional(),
        inventoryStatus: z.enum(INVENTORY_STATUS).default('AVAILABLE'),
        qty: NonNegativeDecimalString,
        packLevel: z.enum(PACK_LEVEL).optional(),
      }),
    )
    .default([]), // empty = location counted as empty
  isRecount: z.boolean().default(false),
});
export type SubmitCountDto = z.infer<typeof SubmitCountSchema>;

export const ReviewCountLinesSchema = z.object({
  lineIds: z.array(Uuid).min(1),
  action: z.enum(['APPROVE', 'RECOUNT', 'SKIP']),
  reasonCodeId: Uuid.optional(), // for approve with variance
  notes: z.string().max(1000).optional(),
});

export const SerialQuerySchema = z.object({
  storerId: Uuid.optional(),
  skuId: Uuid.optional(),
  serialNumber: z.string().optional(),
  status: z.string().optional(),
  warehouseId: Uuid.optional(),
});
