'use strict';

function commandError(code, message, details = {}) {
  const err = new Error(message || code);
  err.code = code;
  err.details = details;
  return err;
}

function handlerNotImplemented(commandType, reason) {
  throw commandError('handler_not_implemented', `${commandType} requiere un servicio local seguro antes de aplicarse`, { reason });
}

function requireObject(value, label = 'payload') {
  if (!value || typeof value !== 'object' || Array.isArray(value)) {
    throw commandError('invalid_payload', `${label} debe ser objeto`);
  }
  return value;
}

function pickString(payload, names, { required = false, label = names[0], max = 255 } = {}) {
  for (const name of names) {
    if (payload[name] !== undefined && payload[name] !== null && String(payload[name]).trim() !== '') {
      return String(payload[name]).trim().slice(0, max);
    }
  }
  if (required) throw commandError('invalid_payload', `${label} requerido`);
  return undefined;
}

function pickNumber(payload, names, { required = false, label = names[0], min = null } = {}) {
  for (const name of names) {
    if (payload[name] !== undefined && payload[name] !== null && payload[name] !== '') {
      const number = Number(payload[name]);
      if (!Number.isFinite(number)) throw commandError('invalid_payload', `${label} debe ser numerico`);
      if (min !== null && number < min) throw commandError('invalid_payload', `${label} debe ser mayor o igual a ${min}`);
      return number;
    }
  }
  if (required) throw commandError('invalid_payload', `${label} requerido`);
  return undefined;
}

function pickBoolean(payload, names) {
  for (const name of names) {
    if (payload[name] === undefined || payload[name] === null) continue;
    if (typeof payload[name] === 'boolean') return payload[name];
    if (payload[name] === 1 || payload[name] === '1') return true;
    if (payload[name] === 0 || payload[name] === '0') return false;
    if (String(payload[name]).toLowerCase() === 'true') return true;
    if (String(payload[name]).toLowerCase() === 'false') return false;
  }
  return undefined;
}

function productId(payload) {
  return pickString(payload, ['idproducto', 'product_id', 'external_id'], { required: true, label: 'idproducto', max: 20 });
}

function validatePriceUpdate(payload) {
  const data = requireObject(payload);
  return {
    idproducto: productId(data),
    precio1: pickNumber(data, ['precio1', 'price', 'precio'], { required: true, label: 'precio', min: 0 }),
  };
}

function validateProductCreate(payload) {
  const data = requireObject(payload);
  const out = {
    idproducto: productId(data),
    descripcion: pickString(data, ['descripcion', 'name', 'description'], { required: true, label: 'descripcion', max: 60 }),
  };
  const mappings = [
    ['idgrupo', ['idgrupo', 'group_id']],
    ['idsubgrupo', ['idsubgrupo', 'subgroup_id']],
    ['unidad', ['unidad', 'unit']],
    ['id_area', ['id_area', 'area_id']],
    ['suspendido', ['suspendido']],
    ['idempresa', ['idempresa', 'company_id']],
  ];
  for (const [target, names] of mappings) {
    const value = pickString(data, names, { max: 80 });
    if (value !== undefined) out[target] = value;
  }
  const numberMappings = [
    ['precio', ['precio', 'price', 'precio1'], 0],
    ['impuesto1', ['impuesto1', 'tax_rate'], 0],
    ['iva', ['iva'], 0],
  ];
  for (const [target, names, min] of numberMappings) {
    const value = pickNumber(data, names, { min });
    if (value !== undefined) out[target] = value;
  }
  for (const [target, names] of [
    ['usa_comedor', ['usa_comedor', 'dine_in']],
    ['usa_domicilio', ['usa_domicilio', 'delivery']],
    ['usa_rapido', ['usa_rapido', 'quick_service']],
    ['no_facturable', ['no_facturable', 'not_billable']],
  ]) {
    const value = pickBoolean(data, names);
    if (value !== undefined) out[target] = value;
  }
  return out;
}

function validateProductUpdate(payload) {
  const data = requireObject(payload);
  const out = { idproducto: productId(data) };
  const description = pickString(data, ['descripcion', 'name', 'description'], { max: 60 });
  if (description !== undefined) out.descripcion = description;
  const groupId = pickString(data, ['idgrupo', 'group_id'], { max: 20 });
  if (groupId !== undefined) out.idgrupo = groupId;
  const subgroupId = pickString(data, ['idsubgrupo', 'subgroup_id'], { max: 20 });
  if (subgroupId !== undefined) out.idsubgrupo = subgroupId;
  const unidad = pickString(data, ['unidad', 'unit'], { max: 50 });
  if (unidad !== undefined) out.unidad = unidad;
  const price = pickNumber(data, ['precio1', 'price', 'precio'], { min: 0 });
  if (price !== undefined) out.precio1 = price;
  const iva = pickNumber(data, ['iva', 'impuesto1', 'tax_rate'], { min: 0 });
  if (iva !== undefined) out.iva = iva;
  const area = pickString(data, ['id_area', 'area_id'], { max: 20 });
  if (area !== undefined) out.id_area = area;
  const suspendido = pickString(data, ['suspendido'], { max: 2 });
  if (suspendido !== undefined) out.suspendido = suspendido.toUpperCase() === 'SI' ? 'SI' : 'NO';
  for (const [target, names] of [
    ['usa_comedor', ['usa_comedor', 'dine_in']],
    ['usa_domicilio', ['usa_domicilio', 'delivery']],
    ['usa_rapido', ['usa_rapido', 'quick_service']],
    ['no_facturable', ['no_facturable', 'not_billable']],
  ]) {
    const value = pickBoolean(data, names);
    if (value !== undefined) out[target] = value;
  }
  if (Object.keys(out).length === 1) throw commandError('invalid_payload', 'config.product.update no trae campos editables');
  return out;
}

function validateRecipeUpdate(payload) {
  const data = requireObject(payload);
  const lines = data.lines || data.lineas || data.ingredients || [];
  if (!Array.isArray(lines)) throw commandError('invalid_payload', 'lines debe ser array');
  return {
    idproducto: productId(data),
    lines: lines.map((line) => {
      const item = requireObject(line, 'line');
      return {
        idinsumo: pickString(item, ['idinsumo', 'ingredient_id'], { required: true, label: 'idinsumo', max: 20 }),
        cantidad: pickNumber(item, ['cantidad', 'quantity'], { required: true, label: 'cantidad', min: 0 }),
        idinsumospresentaciones: pickString(item, ['idinsumospresentaciones', 'presentation_id'], { max: 20 }),
      };
    }),
  };
}

function validateIngredientCreate(payload) {
  const data = requireObject(payload);
  return {
    idinsumo: pickString(data, ['idinsumo', 'ingredient_id'], { required: true, label: 'idinsumo', max: 20 }),
    descripcion: pickString(data, ['descripcion', 'name'], { required: true, label: 'descripcion', max: 60 }),
    unidad: pickString(data, ['unidad', 'unit'], { max: 10 }),
    idgrupo: pickString(data, ['idgrupo', 'group_id'], { max: 10 }),
    ultimocosto: pickNumber(data, ['ultimocosto', 'cost'], { min: 0 }),
    porcentajemerma: pickNumber(data, ['porcentajemerma', 'waste_pct'], { min: 0 }),
    inventariable: pickBoolean(data, ['inventariable', 'inventory_enabled']),
  };
}

function validateIngredientUpdate(payload) {
  const data = requireObject(payload);
  const out = {
    idinsumo: pickString(data, ['idinsumo', 'ingredient_id'], { required: true, label: 'idinsumo', max: 20 }),
  };
  const descripcion = pickString(data, ['descripcion', 'name'], { max: 60 });
  if (descripcion !== undefined) out.descripcion = descripcion;
  const unidad = pickString(data, ['unidad', 'unit'], { max: 10 });
  if (unidad !== undefined) out.unidad = unidad;
  const idgrupo = pickString(data, ['idgruposi', 'idgrupo', 'group_id'], { max: 10 });
  if (idgrupo !== undefined) out.idgruposi = idgrupo;
  const elaborado = pickBoolean(data, ['elaborado', 'prepared']);
  if (elaborado !== undefined) out.elaborado = elaborado;
  if (Object.keys(out).length === 1) throw commandError('invalid_payload', 'config.ingredient.update no trae campos editables');
  return out;
}

function validateSupplierUpsert(payload) {
  const data = requireObject(payload);
  const source = data.supplier !== undefined ? requireObject(data.supplier, 'supplier') : data;
  const out = {
    idproveedor: pickString(source, ['idproveedor', 'supplier_id', 'external_id'], { required: true, label: 'idproveedor', max: 15 }),
    nombre: pickString(source, ['nombre', 'name'], { required: true, label: 'nombre', max: 50 }),
  };
  for (const [target, names, max] of [
    ['razonsocial', ['razonsocial', 'legal_name'], 100],
    ['direccion', ['direccion', 'address'], 150],
    ['codigopostal', ['codigopostal', 'postal_code'], 15],
    ['telefono', ['telefono', 'phone'], 20],
    ['fax', ['fax'], 20],
    ['email', ['email'], 150],
    ['rfc', ['rfc', 'tax_id'], 15],
    ['idtipoproveedor', ['idtipoproveedor', 'supplier_type_id'], 5],
    ['idcuentacontable', ['idcuentacontable', 'account_id'], 5],
    ['nombrebanco', ['nombrebanco', 'bank_name'], 100],
    ['nocuenta', ['nocuenta', 'bank_account'], 100],
    ['cuentaclave', ['cuentaclave', 'clabe'], 100],
  ]) {
    const value = pickString(source, names, { max });
    if (value !== undefined) out[target] = value;
  }
  for (const [target, names, min] of [
    ['credito', ['credito', 'credit_days'], 0],
    ['estatus', ['estatus', 'status'], 0],
  ]) {
    const value = pickNumber(source, names, { min });
    if (value !== undefined) out[target] = value;
  }
  for (const [target, names] of [
    ['usarcostosasignados', ['usarcostosasignados', 'use_assigned_costs']],
    ['usarenpoliza', ['usarenpoliza', 'use_accounting_policy']],
  ]) {
    const value = pickBoolean(source, names);
    if (value !== undefined) out[target] = value;
  }
  return out;
}

function validateCreatePurchase(payload) {
  const data = requireObject(payload);
  const lines = data.lineas || data.lines || data.items || [];
  if (!Array.isArray(lines) || !lines.length) throw commandError('invalid_payload', 'lineas requerido');
  return {
    idproveedor: pickString(data, ['idproveedor', 'supplier_id'], { required: true, label: 'idproveedor', max: 15 }),
    fechaaplicacion: pickString(data, ['fechaaplicacion', 'applied_at'], { max: 40 }),
    fechafactura: pickString(data, ['fechafactura', 'invoice_date'], { max: 40 }),
    foliofactura: pickString(data, ['foliofactura', 'invoice_number'], { max: 15 }) || '',
    referencia: pickString(data, ['referencia', 'reference'], { max: 50 }) || '',
    lineas: lines.map((line) => {
      const item = requireObject(line, 'linea');
      return {
        idinsumospresentaciones: pickString(item, ['idinsumospresentaciones', 'presentation_id'], { required: true, label: 'idinsumospresentaciones', max: 15 }),
        cantidad: pickNumber(item, ['cantidad', 'quantity'], { required: true, label: 'cantidad', min: 0 }),
        costoUnitario: pickNumber(item, ['costoUnitario', 'costo_unitario', 'unit_cost', 'cost'], { required: true, label: 'costoUnitario', min: 0 }),
        iva: pickNumber(item, ['iva', 'tax_rate'], { min: 0 }) ?? 0,
        idalmacen: pickString(item, ['idalmacen', 'warehouse_id'], { required: true, label: 'idalmacen', max: 10 }),
      };
    }),
  };
}

function validateCancelPurchase(payload) {
  const data = requireObject(payload);
  return {
    idcompra: pickString(data, ['idcompra', 'purchase_id', 'external_id'], { required: true, label: 'idcompra', max: 15 }),
  };
}

function validateInventoryAdjust(payload) {
  const data = requireObject(payload);
  return {
    idinsumospresentaciones: pickString(data, ['idinsumospresentaciones', 'presentation_id'], { required: true, label: 'idinsumospresentaciones', max: 20 }),
    idalmacen: pickString(data, ['idalmacen', 'warehouse_id'], { required: true, label: 'idalmacen', max: 10 }),
    conteo_fisico: pickNumber(data, ['conteo_fisico', 'physical_count', 'quantity'], { required: true, label: 'conteo_fisico', min: 0 }),
    reason: pickString(data, ['reason', 'motivo'], { max: 160 }),
  };
}

function validateCreatePurchaseOrder(payload) {
  const data = requireObject(payload);
  const lines = data.lineas || data.lines || data.items || [];
  if (!Array.isArray(lines) || !lines.length) throw commandError('invalid_payload', 'lineas requerido');
  return {
    purchase_order_id: pickString(data, ['purchase_order_id'], { required: true, label: 'purchase_order_id', max: 80 }),
    idproveedor: pickString(data, ['idproveedor', 'supplier_id'], { required: true, label: 'idproveedor', max: 15 }),
    fechacaptura: pickString(data, ['fechacaptura', 'created_at'], { max: 40 }),
    fecharecepcion: pickString(data, ['fecharecepcion', 'expected_at'], { max: 40 }),
    referencia: pickString(data, ['referencia', 'reference'], { max: 200 }) || '',
    notes: pickString(data, ['notes', 'notas'], { max: 1000 }) || '',
    lineas: lines.map((line) => {
      const item = requireObject(line, 'linea');
      return {
        idinsumo: pickString(item, ['idinsumo', 'supply_id'], { required: true, label: 'idinsumo', max: 15 }),
        cantidad: pickNumber(item, ['cantidad', 'quantity'], { required: true, label: 'cantidad', min: 0 }),
        costoUnitario: pickNumber(item, ['costoUnitario', 'costo_unitario', 'unit_cost', 'cost'], { required: true, label: 'costoUnitario', min: 0 }),
        idalmacen: pickString(item, ['idalmacen', 'warehouse_id'], { required: true, label: 'idalmacen', max: 10 }),
      };
    }),
  };
}

function validateInventoryCountApply(payload) {
  const data = requireObject(payload);
  const items = data.items;
  if (!Array.isArray(items) || !items.length) throw commandError('invalid_payload', 'items requerido');
  if (items.length > 10000) throw commandError('invalid_payload', 'items excede el máximo permitido');
  const capturePolicy = pickString(data, ['capture_policy'], { max: 40 }) || 'counted_only';
  if (!['counted_only', 'full_catalog_zero'].includes(capturePolicy)) throw commandError('invalid_payload', 'capture_policy inválido');
  return {
    count_id: pickString(data, ['count_id'], { required: true, label: 'count_id', max: 80 }),
    warehouse_id: pickString(data, ['warehouse_id', 'idalmacen'], { required: true, label: 'warehouse_id', max: 40 }),
    name: pickString(data, ['name'], { required: true, label: 'name', max: 200 }),
    reason: pickString(data, ['reason'], { required: true, label: 'reason', max: 500 }),
    capture_policy: capturePolicy,
    effective_at: pickString(data, ['effective_at'], { max: 40 }) || new Date().toISOString(),
    items: items.map((line) => {
      const item = requireObject(line, 'item');
      return {
        idinsumospresentaciones: pickString(item, ['idinsumospresentaciones', 'item_key'], { required: true, label: 'idinsumospresentaciones', max: 80 }),
        counted_qty: pickNumber(item, ['counted_qty', 'conteo_fisico'], { required: true, label: 'counted_qty', min: 0 }),
        expected_qty: pickNumber(item, ['expected_qty', 'existencia_teorica'], { min: 0 }) ?? 0,
        unit_cost: pickNumber(item, ['unit_cost', 'costo'], { min: 0 }) ?? 0,
      };
    }),
  };
}

function validateFoodcostRecalculate(payload) {
  const data = requireObject(payload || {});
  return {
    idproducto: pickString(data, ['idproducto', 'product_id', 'external_id'], { max: 20 }),
  };
}

function validateBillingProviderConfig(payload) {
  const data = requireObject(payload);
  const baseUrl = pickString(data, ['base_url', 'baseUrl'], { max: 500 });
  if (baseUrl !== undefined && baseUrl && !/^https?:\/\//i.test(baseUrl)) {
    throw commandError('invalid_payload', 'base_url debe iniciar con http:// o https://');
  }
  return { ...data, base_url: baseUrl !== undefined ? baseUrl : data.base_url };
}

module.exports = {
  commandError,
  handlerNotImplemented,
  validatePriceUpdate,
  validateProductCreate,
  validateProductUpdate,
  validateIngredientCreate,
  validateIngredientUpdate,
  validateSupplierUpsert,
  validateCreatePurchase,
  validateCreatePurchaseOrder,
  validateCancelPurchase,
  validateInventoryAdjust,
  validateInventoryCountApply,
  validateRecipeUpdate,
  validateFoodcostRecalculate,
  validateBillingProviderConfig,
};
