import {
  buildObservacionAnticipoHistorico,
  construirFilaEstructurada,
  detectarFilaNoGasto,
  esAnticipoViaticoHistorico,
  normalizarCondicionOperacion,
  normalizarRuc,
  normalizarTextoAlias,
  parseFechaSET,
  resolverMapeoColumnas,
  splitNumeroComprobante,
} from './libro-compras-set.normalizador';

const HEADERS_REALES_CLIENTE = [
  'RUC / Nº de Identificacion del Informado',
  'Tipo de Identificación del Informado',
  'Nombre o Razón Social del Informado',
  'Tipo de Registro',
  'Tipo de Comprobante',
  'Fecha de Emisión',
  'Periodo de Emisión',
  'Condicion de la Operacion',
  'Timbrado del Comprobante',
  'Numero de Comprobante',
  'CDC',
  'Monto Gravado 10%',
  'IVA 10%',
  'Monto Gravado 5%',
  'IVA 5%',
  'Monto No Gravado / Exento ',
  'Total Comprobante',
  'TIPO CUENTA CONTABLE- GASTO',
  'TIPO CUENTA CONTABLE- CONTRAPARTIDA PASIVA',
];

describe('normalizarTextoAlias', () => {
  it('quita tildes, pasa a mayúsculas y colapsa espacios', () => {
    expect(normalizarTextoAlias('Gastos de Escribanía')).toBe('GASTOS DE ESCRIBANIA');
  });

  it('limpia caracteres sueltos como "}"', () => {
    expect(normalizarTextoAlias('GASTOS DE PAPELERIA Y UTILES}')).toBe('GASTOS DE PAPELERIA Y UTILES');
  });

  it('unifica variantes con espacios extra', () => {
    expect(normalizarTextoAlias('  GASTOS   GENERAL ')).toBe('GASTOS GENERAL');
  });

  it('devuelve string vacío para valores nulos/undefined', () => {
    expect(normalizarTextoAlias(null)).toBe('');
    expect(normalizarTextoAlias(undefined)).toBe('');
  });
});

describe('parseFechaSET', () => {
  it('parsea dd/mm/yyyy válido', () => {
    const fecha = parseFechaSET('01/05/2026');
    expect(fecha?.getUTCFullYear()).toBe(2026);
    expect(fecha?.getUTCMonth()).toBe(4); // mayo = índice 4
    expect(fecha?.getUTCDate()).toBe(1);
  });

  it('acepta un objeto Date real (cellDates:true de XLSX)', () => {
    const d = new Date('2026-05-14T00:00:00Z');
    expect(parseFechaSET(d)).toBe(d);
  });

  it('rechaza fechas imposibles (31/02)', () => {
    expect(parseFechaSET('31/02/2026')).toBeNull();
  });

  it('rechaza formato inválido', () => {
    expect(parseFechaSET('2026-05-01')).toBeNull();
    expect(parseFechaSET('')).toBeNull();
    expect(parseFechaSET(null)).toBeNull();
  });
});

describe('splitNumeroComprobante', () => {
  it('parsea el formato estándar EST-PEX-NUMERO', () => {
    const r = splitNumeroComprobante('001-001-0006806');
    expect(r).toEqual({ ok: true, establecimiento: '001', punto_expedicion: '001', numero_factura: '0006806' });
  });

  it('rellena con ceros a la izquierda si vienen sin padding', () => {
    const r = splitNumeroComprobante('1-1-6806');
    expect(r).toEqual({ ok: true, establecimiento: '001', punto_expedicion: '001', numero_factura: '0006806' });
  });

  it('parsea 13 dígitos sin guiones', () => {
    const r = splitNumeroComprobante('0010010006806');
    expect(r).toEqual({ ok: true, establecimiento: '001', punto_expedicion: '001', numero_factura: '0006806' });
  });

  it('reporta error para formato irreconocible (caso real: 1 fila del cliente)', () => {
    const r = splitNumeroComprobante('');
    expect(r.ok).toBe(false);
    if (r.ok === false) expect(r.error).toContain('inválido');
  });
});

describe('normalizarCondicionOperacion', () => {
  it('reconoce CONTADO', () => {
    expect(normalizarCondicionOperacion('CONTADO')).toBe('CONTADO');
  });

  it('reconoce CREDITO sin tilde', () => {
    expect(normalizarCondicionOperacion('CREDITO')).toBe('CREDITO');
  });

  it('reconoce CRÉDITO con tilde (caso real: 4 filas del cliente)', () => {
    expect(normalizarCondicionOperacion('CRÉDITO')).toBe('CREDITO');
  });

  it('default a CONTADO para valores desconocidos', () => {
    expect(normalizarCondicionOperacion('')).toBe('CONTADO');
  });
});

describe('normalizarRuc', () => {
  it('deja pasar un RUC de hasta 8 caracteres', () => {
    expect(normalizarRuc('80016096')).toEqual({ ruc: '80016096', truncado: false });
  });

  it('quita caracteres no alfanuméricos', () => {
    expect(normalizarRuc(' 3643813 ')).toEqual({ ruc: '3643813', truncado: false });
  });

  it('marca truncado si supera 8 caracteres', () => {
    const r = normalizarRuc('123456789');
    expect(r.truncado).toBe(true);
    expect(r.ruc).toHaveLength(8);
  });
});

describe('detectarFilaNoGasto', () => {
  it('detecta IMPORTACION EN CURSO', () => {
    expect(detectarFilaNoGasto('IMPORTACION EN CURSO')).toEqual({ esNoGasto: true, motivo: 'IMPORTACION_EN_CURSO' });
  });

  it('detecta MERCADERIAS GRAVADAS + IVA 10% (caso real del cliente)', () => {
    expect(detectarFilaNoGasto('MERCADERIAS GRAVADAS + IVA 10%')).toEqual({
      esNoGasto: true,
      motivo: 'MERCADERIA_NO_GASTO',
    });
  });

  it('detecta REMUNERACION AL PERSONAL SUPERIOR (excluida por decisión de negocio)', () => {
    expect(detectarFilaNoGasto('REMUNERACION AL PERSONAL SUPERIOR')).toEqual({
      esNoGasto: true,
      motivo: 'REMUNERACION_PERSONAL',
    });
  });

  it('no marca como no-gasto un tipo de gasto normal', () => {
    expect(detectarFilaNoGasto('COMBUSTIBLE Y LUBRICANTES')).toEqual({ esNoGasto: false });
  });

  it('no marca como no-gasto "GASTOS PARA VIATICOS" (es un gasto real)', () => {
    expect(detectarFilaNoGasto('GASTOS PARA VIATICOS')).toEqual({ esNoGasto: false });
  });
});

describe('esAnticipoViaticoHistorico / buildObservacionAnticipoHistorico', () => {
  it('detecta la contrapartida ANTICIPO DE VIATICO', () => {
    expect(esAnticipoViaticoHistorico('ANTICIPO DE VIATICO')).toBe(true);
  });

  it('no confunde ANTICIPO DE DESPACHO con anticipo de viático', () => {
    expect(esAnticipoViaticoHistorico('ANTICIPO DE DESPACHO')).toBe(false);
  });

  it('no marca ACREEDORES VARIOS', () => {
    expect(esAnticipoViaticoHistorico('ACREEDORES VARIOS')).toBe(false);
  });

  it('genera una observación de trazabilidad no vacía', () => {
    expect(buildObservacionAnticipoHistorico().length).toBeGreaterThan(0);
    expect(buildObservacionAnticipoHistorico()).toContain('Anticipo de viático');
  });
});

describe('resolverMapeoColumnas (con headers reales del cliente)', () => {
  it('resuelve todas las columnas del Libro de Compras SET real', () => {
    const mapeo = resolverMapeoColumnas(HEADERS_REALES_CLIENTE);
    expect(mapeo.ruc).toBe('RUC / Nº de Identificacion del Informado');
    expect(mapeo.razon_social).toBe('Nombre o Razón Social del Informado');
    expect(mapeo.fecha_emision).toBe('Fecha de Emisión');
    expect(mapeo.condicion_operacion).toBe('Condicion de la Operacion');
    expect(mapeo.timbrado).toBe('Timbrado del Comprobante');
    expect(mapeo.numero_comprobante).toBe('Numero de Comprobante');
    expect(mapeo.monto_gravado_10).toBe('Monto Gravado 10%');
    expect(mapeo.iva_10).toBe('IVA 10%');
    expect(mapeo.monto_gravado_5).toBe('Monto Gravado 5%');
    expect(mapeo.iva_5).toBe('IVA 5%');
    expect(mapeo.monto_no_gravado).toBe('Monto No Gravado / Exento ');
    expect(mapeo.total_comprobante).toBe('Total Comprobante');
    expect(mapeo.tipo_gasto_texto).toBe('TIPO CUENTA CONTABLE- GASTO');
    expect(mapeo.contrapartida_pasiva).toBe('TIPO CUENTA CONTABLE- CONTRAPARTIDA PASIVA');
  });

  it('devuelve null para un campo sin columna correspondiente', () => {
    const mapeo = resolverMapeoColumnas(['Algo totalmente distinto']);
    expect(mapeo.ruc).toBeNull();
  });
});

describe('construirFilaEstructurada (con filas reales del cliente)', () => {
  const mapeo = resolverMapeoColumnas(HEADERS_REALES_CLIENTE);

  interface FilaTest {
    ruc: string;
    razonSocial: string;
    fechaEmision: string;
    condicion: string;
    timbrado: string;
    numeroComprobante: string;
    gravado10?: number;
    iva10?: number;
    gravado5?: number;
    iva5?: number;
    noGravado?: number;
    total: number;
    tipoGasto: string;
    contrapartida: string;
  }

  /** Arma la fila por campos nombrados (evita errores de conteo posicional en las 19 columnas). */
  const filaComoObjeto = (f: FilaTest): Record<string, unknown> => ({
    'RUC / Nº de Identificacion del Informado': f.ruc,
    'Tipo de Identificación del Informado': 'RUC',
    'Nombre o Razón Social del Informado': f.razonSocial,
    'Tipo de Registro': 'COMPRAS',
    'Tipo de Comprobante': 'FACTURA',
    'Fecha de Emisión': f.fechaEmision,
    'Periodo de Emisión': '05/2026',
    'Condicion de la Operacion': f.condicion,
    'Timbrado del Comprobante': f.timbrado,
    'Numero de Comprobante': f.numeroComprobante,
    CDC: '',
    'Monto Gravado 10%': f.gravado10 ?? 0,
    'IVA 10%': f.iva10 ?? 0,
    'Monto Gravado 5%': f.gravado5 ?? 0,
    'IVA 5%': f.iva5 ?? 0,
    'Monto No Gravado / Exento ': f.noGravado ?? 0,
    'Total Comprobante': f.total,
    'TIPO CUENTA CONTABLE- GASTO': f.tipoGasto,
    'TIPO CUENTA CONTABLE- CONTRAPARTIDA PASIVA': f.contrapartida,
  });

  it('parsea una fila normal de combustible sin errores', () => {
    const row = filaComoObjeto({
      ruc: '80141642',
      razonSocial: 'ALMAX E.A.S.',
      fechaEmision: '06/05/2026',
      condicion: 'CONTADO',
      timbrado: '17219348',
      numeroComprobante: '011-001-0028591',
      noGravado: 300000,
      total: 300000,
      tipoGasto: 'COMBUSTIBLE Y LUBRICANTES',
      contrapartida: 'ANTICIPO DE VIATICO',
    });
    const fila = construirFilaEstructurada(row, mapeo);
    expect(fila.errores).toHaveLength(0);
    expect(fila.ruc).toBe('80141642');
    expect(fila.condicionOperacion).toBe('CONTADO');
    expect(fila.comprobante).toEqual({ ok: true, establecimiento: '011', punto_expedicion: '001', numero_factura: '0028591' });
    expect(fila.totalComprobante).toBe(300000);
    expect(fila.noGasto.esNoGasto).toBe(false);
    expect(fila.esAnticipoViatico).toBe(true);
  });

  it('marca IMPORTACION EN CURSO como no-gasto', () => {
    const row = filaComoObjeto({
      ruc: '3532618',
      razonSocial: 'MADHAI DE LOURDES BALBUENA ALARCON',
      fechaEmision: '02/05/2026',
      condicion: 'CONTADO',
      timbrado: '18781179',
      numeroComprobante: '001-001-0001557',
      gravado10: 640000,
      iva10: 58182,
      total: 640000,
      tipoGasto: 'IMPORTACION EN CURSO',
      contrapartida: 'ANTICIPO DE DESPACHO',
    });
    const fila = construirFilaEstructurada(row, mapeo);
    expect(fila.noGasto).toEqual({ esNoGasto: true, motivo: 'IMPORTACION_EN_CURSO' });
    expect(fila.esAnticipoViatico).toBe(false);
  });

  it('marca MERCADERIAS GRAVADAS como no-gasto (es una compra, no un gasto)', () => {
    const row = filaComoObjeto({
      ruc: '80099999',
      razonSocial: 'PROVEEDOR MERCADERIA SA',
      fechaEmision: '10/05/2026',
      condicion: 'CONTADO',
      timbrado: '12345678',
      numeroComprobante: '001-001-0000001',
      total: 100000,
      tipoGasto: 'MERCADERIAS GRAVADAS + IVA 10%',
      contrapartida: 'ACREEDORES VARIOS',
    });
    const fila = construirFilaEstructurada(row, mapeo);
    expect(fila.noGasto).toEqual({ esNoGasto: true, motivo: 'MERCADERIA_NO_GASTO' });
  });

  it('marca REMUNERACION AL PERSONAL SUPERIOR como no-gasto (decisión: siempre excluir, es nómina)', () => {
    const row = filaComoObjeto({
      ruc: '80088888',
      razonSocial: 'ALGUIEN SRL',
      fechaEmision: '10/05/2026',
      condicion: 'CONTADO',
      timbrado: '12345678',
      numeroComprobante: '001-001-0000002',
      total: 500000,
      tipoGasto: 'REMUNERACION AL PERSONAL SUPERIOR',
      contrapartida: 'ACREEDORES VARIOS',
    });
    const fila = construirFilaEstructurada(row, mapeo);
    expect(fila.noGasto).toEqual({ esNoGasto: true, motivo: 'REMUNERACION_PERSONAL' });
  });

  it('reporta error para número de comprobante vacío (caso real: 1 fila del cliente)', () => {
    const row = filaComoObjeto({
      ruc: '4679464',
      razonSocial: 'RESQUIN BARRIOS VANESSA ALEJANDRA',
      fechaEmision: '04/05/2026',
      condicion: 'CONTADO',
      timbrado: '16120058',
      numeroComprobante: '',
      gravado10: 1500000,
      iva10: 136364,
      total: 1500000,
      tipoGasto: 'HONORARIOS PROFESIONALES',
      contrapartida: 'ACREEDORES VARIOS',
    });
    const fila = construirFilaEstructurada(row, mapeo);
    expect(fila.comprobante.ok).toBe(false);
    expect(fila.errores.length).toBeGreaterThan(0);
  });

  it('reconoce CRÉDITO con tilde en una fila real', () => {
    const row = filaComoObjeto({
      ruc: '862074',
      razonSocial: 'BALBINO BALBUENA GIMENEZ',
      fechaEmision: '07/05/2026',
      condicion: 'CRÉDITO',
      timbrado: '18340616',
      numeroComprobante: '002-001-0001571',
      gravado10: 10468800,
      iva10: 951709,
      total: 10468800,
      tipoGasto: 'INTERESES PAGADOS A ENTIDADES BANCARIAS',
      contrapartida: 'ACREEDORES VARIOS',
    });
    const fila = construirFilaEstructurada(row, mapeo);
    expect(fila.condicionOperacion).toBe('CREDITO');
  });
});
