export type EcommerceDeliveryType = 'pickup' | 'delivery';
export type EcommerceWorkflowPreset = 'simple' | 'controlado' | 'completo' | 'personalizado';
export type EcommerceCancellationPolicy = 'before_preparation' | 'before_handoff' | 'supervisor_only';

export interface EcommerceWorkflowConfig {
  version: 1;
  preset: EcommerceWorkflowPreset;
  verificationEnabled: boolean;
  packingEnabled: boolean;
  transitEnabled: boolean;
  cancellationPolicy: EcommerceCancellationPolicy;
  labels: Record<string, string>;
}

export const DEFAULT_ECOMMERCE_WORKFLOW: EcommerceWorkflowConfig = {
  version: 1,
  preset: 'completo',
  verificationEnabled: true,
  packingEnabled: true,
  transitEnabled: true,
  cancellationPolicy: 'before_handoff',
  labels: {},
};

const PRESET_DEFAULTS: Record<
  Exclude<EcommerceWorkflowPreset, 'personalizado'>,
  Pick<EcommerceWorkflowConfig, 'verificationEnabled' | 'packingEnabled' | 'transitEnabled'>
> = {
  simple: { verificationEnabled: false, packingEnabled: false, transitEnabled: false },
  controlado: { verificationEnabled: true, packingEnabled: true, transitEnabled: false },
  completo: { verificationEnabled: true, packingEnabled: true, transitEnabled: true },
};

const CANCELLATION_POLICIES = new Set<EcommerceCancellationPolicy>([
  'before_preparation',
  'before_handoff',
  'supervisor_only',
]);

export function normalizeWorkflowConfig(value: unknown): EcommerceWorkflowConfig {
  const input = value && typeof value === 'object' ? (value as Record<string, unknown>) : {};
  const preset = isPreset(input.preset) ? input.preset : DEFAULT_ECOMMERCE_WORKFLOW.preset;
  const presetDefaults = preset === 'personalizado' ? DEFAULT_ECOMMERCE_WORKFLOW : PRESET_DEFAULTS[preset];

  return {
    version: 1,
    preset,
    verificationEnabled: boolOr(input.verificationEnabled, presetDefaults.verificationEnabled),
    packingEnabled: boolOr(input.packingEnabled, presetDefaults.packingEnabled),
    transitEnabled: boolOr(input.transitEnabled, presetDefaults.transitEnabled),
    cancellationPolicy: CANCELLATION_POLICIES.has(input.cancellationPolicy as EcommerceCancellationPolicy)
      ? (input.cancellationPolicy as EcommerceCancellationPolicy)
      : DEFAULT_ECOMMERCE_WORKFLOW.cancellationPolicy,
    labels: sanitizeLabels(input.labels),
  };
}

export function resolveDeliveryType(deliverySnapshot: unknown): EcommerceDeliveryType {
  const delivery =
    deliverySnapshot && typeof deliverySnapshot === 'object' ? (deliverySnapshot as Record<string, unknown>) : {};
  return delivery.type === 'pickup' ? 'pickup' : 'delivery';
}

export function buildOperationalFlow(deliveryType: EcommerceDeliveryType, configValue: unknown): string[] {
  const config = normalizeWorkflowConfig(configValue);
  const flow = ['pago_confirmado'];

  if (config.verificationEnabled) flow.push('en_verificacion', 'verificado');
  flow.push('preparando');
  if (config.packingEnabled) flow.push('empaquetado');

  if (deliveryType === 'pickup') {
    flow.push('listo_retiro', 'retirado');
  } else {
    flow.push('listo_despacho');
    if (config.transitEnabled) flow.push('en_camino');
    flow.push('entregado');
  }

  return flow;
}

export function effectiveWorkflowState(rawState: string, deliveryType: EcommerceDeliveryType): string {
  if (rawState === 'pedido_creado') return 'pago_confirmado';
  if (rawState === 'listo') return deliveryType === 'pickup' ? 'listo_retiro' : 'listo_despacho';
  return rawState;
}

export function nextWorkflowStates(
  rawState: string,
  deliveryType: EcommerceDeliveryType,
  configValue: unknown,
): string[] {
  const config = normalizeWorkflowConfig(configValue);
  const state = effectiveWorkflowState(rawState, deliveryType);

  if (state === 'pendiente_pago') return ['pago_confirmado', 'cancelado'];
  // Una incidencia congela el pedido: sólo un supervisor puede reencauzarlo a
  // preparación (vuelve al flujo normal) o cancelarlo. No avanza por sí solo.
  if (state === 'incidencia') return ['preparando', 'cancelado'];
  if (['cancelado', 'expirado', 'retirado', 'entregado'].includes(state)) return [];

  const flow = buildOperationalFlow(deliveryType, config);
  const index = flow.indexOf(state);
  const next = index >= 0 && index < flow.length - 1 ? flow[index + 1] : legacyNextState(state, deliveryType);
  const states = next ? [next] : [];
  if (canCancelAt(state, flow, config.cancellationPolicy)) states.push('cancelado');
  return states;
}

/**
 * Estados desde los que un pedido puede marcarse "con incidencia": cualquier
 * punto operativo activo. Se excluyen los finales, el pendiente de pago (que
 * tiene su propia salida por cancelación/expiración) y la propia incidencia.
 */
export function canReportIncidencia(rawState: string, deliveryType: EcommerceDeliveryType): boolean {
  const state = effectiveWorkflowState(rawState, deliveryType);
  const noReport = new Set([
    'pendiente_pago',
    'incidencia',
    'cancelado',
    'expirado',
    'entregado',
    'retirado',
  ]);
  return !noReport.has(state);
}

export function assertDeliveryState(state: string, deliveryType: EcommerceDeliveryType): void {
  const pickupOnly = new Set(['listo_retiro', 'retirado']);
  const deliveryOnly = new Set(['listo_despacho', 'en_camino', 'entregado']);

  if (deliveryType === 'pickup' && deliveryOnly.has(state)) {
    throw new Error(`El estado "${state}" no corresponde a un retiro local.`);
  }
  if (deliveryType === 'delivery' && pickupOnly.has(state)) {
    throw new Error(`El estado "${state}" no corresponde a un delivery.`);
  }
}

function canCancelAt(state: string, flow: string[], policy: EcommerceCancellationPolicy): boolean {
  if (state === 'pendiente_pago') return true;
  const index = flow.indexOf(state);
  if (index < 0) return false;

  if (policy === 'before_preparation') {
    return index < flow.indexOf('preparando');
  }
  if (policy === 'before_handoff') {
    return !['en_camino', 'retirado', 'entregado'].includes(state);
  }
  return !['retirado', 'entregado'].includes(state);
}

function legacyNextState(state: string, deliveryType: EcommerceDeliveryType): string | undefined {
  const fallback: Record<string, string> = {
    en_verificacion: 'verificado',
    verificado: 'preparando',
    preparando: deliveryType === 'pickup' ? 'listo_retiro' : 'listo_despacho',
    empaquetado: deliveryType === 'pickup' ? 'listo_retiro' : 'listo_despacho',
    listo_retiro: 'retirado',
    listo_despacho: 'entregado',
    en_camino: 'entregado',
  };
  return fallback[state];
}

function boolOr(value: unknown, fallback: boolean): boolean {
  return typeof value === 'boolean' ? value : fallback;
}

function isPreset(value: unknown): value is EcommerceWorkflowPreset {
  return typeof value === 'string' && ['simple', 'controlado', 'completo', 'personalizado'].includes(value);
}

function sanitizeLabels(value: unknown): Record<string, string> {
  if (!value || typeof value !== 'object' || Array.isArray(value)) return {};
  return Object.fromEntries(
    Object.entries(value as Record<string, unknown>).flatMap(([state, label]) =>
      typeof label === 'string' && label.trim().length > 0 ? [[state, label.trim().slice(0, 80)]] : [],
    ),
  );
}
