// PRUEBA DE LAS ALICUOTAS DE LA LEY DEL IVA (2026-07-31)
//
// El requisito (g) del art. 3 de la 000121 pide "aplicacion correcta de la Ley
// del IVA", y eso hay que poder DEMOSTRARLO en la evaluacion: general 16%,
// reducida 8%, adicional suntuaria 31% y exento, mezcladas en un mismo
// documento, con su desglose por alicuota, y la nota de credito devolviendo
// cada tasa por su lado.
//
// Y el hueco que cierra: la via de TERCEROS (API y buzon de carpeta) entrega
// renglones con su propia alicuota, y NADIE la validaba. Un administrativo que
// mandara alicuota 12 sellaba una factura con un IVA que no existe en la ley.
// Ahora el armador (el unico cuello por donde pasa todo documento) la rechaza.
import { mkdtempSync, rmSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { armarDocumento, armarNotaCredito, ALICUOTAS_LEY } from '../dist/armador.js';
import { EstacionFiscal } from '../dist/estacion.js';
import { guardarConfig } from '../dist/config.js';

let fallos = 0;
const check = (cond, msg) => {
  console.log((cond ? '  OK  ' : '  XX  ') + msg);
  if (!cond) fallos++;
};

const EMISOR = {
  rif: 'J-50856106-6',
  razon_social: 'Prueba',
  domicilio_fiscal: 'Araure',
  precios_con_iva: false,
};

const venta = (renglones, extra = {}) => ({
  origen_id: `alicuotas:${Math.random().toString(36).slice(2)}`,
  canal: 'mostrador',
  cliente: { nombre: 'Consumidor Final' },
  renglones,
  tasa_bs: 746.63,
  pago: { metodo: 'efectivo' },
  ...extra,
});

try {
  console.log('\n== 1. Las cuatro tasas de la ley, mezcladas en un documento ==');
  const doc = armarDocumento(
    venta([
      { nombre: 'Parrilla', cantidad: 1, precio_usd: 100, alicuota: 16 },
      { nombre: 'Harina de maiz', cantidad: 2, precio_usd: 25, alicuota: 8 },
      { nombre: 'Libro de cocina', cantidad: 1, precio_usd: 30, alicuota: 0 },
      { nombre: 'Whisky importado', cantidad: 1, precio_usd: 200, alicuota: 31 },
    ]),
    EMISOR,
  );
  check(doc.por_alicuota.length === 4, `el desglose trae las 4 tasas (${doc.por_alicuota.length})`);
  check(doc.por_alicuota.map((a) => a.alicuota).join(',') === '0,8,16,31', 'ordenadas de menor a mayor');
  const de = (t) => doc.por_alicuota.find((a) => a.alicuota === t);
  check(de(16)?.base_usd === 100 && de(16)?.iva_usd === 16, `la general: base 100, IVA 16 (${de(16)?.iva_usd})`);
  check(de(8)?.base_usd === 50 && de(8)?.iva_usd === 4, `la reducida: base 50, IVA 4 (${de(8)?.iva_usd})`);
  check(de(0)?.base_usd === 30 && de(0)?.iva_usd === 0, 'lo exento: base 30, IVA cero');
  check(de(31)?.base_usd === 200 && de(31)?.iva_usd === 62, `la suntuaria: base 200, IVA 62 (${de(31)?.iva_usd})`);
  check(doc.base_usd === 380 && doc.iva_usd === 82 && doc.total_usd === 462,
    `y los totales cuadran (${doc.base_usd} + ${doc.iva_usd} = ${doc.total_usd})`);

  console.log('\n== 2. Con el precio ya trayendo el IVA, la base sale hacia atras ==');
  const conIva = armarDocumento(
    venta([{ nombre: 'Cachapa', cantidad: 1, precio_usd: 10.8, alicuota: 8 }]),
    { ...EMISOR, precios_con_iva: true },
  );
  check(conIva.base_usd === 10 && conIva.iva_usd === 0.8, `10,80 al 8%: base 10, IVA 0,80 (${conIva.base_usd}/${conIva.iva_usd})`);

  console.log('\n== 3. La nota de credito devuelve cada tasa por su lado ==');
  const nc = armarNotaCredito(doc, 'devolucion completa');
  check(nc.por_alicuota.length === 4, 'la nota trae el mismo desglose');
  const ncDe = (t) => nc.por_alicuota.find((a) => a.alicuota === t);
  check(ncDe(31)?.iva_usd === -62 && ncDe(8)?.iva_usd === -4, 'cada IVA en negativo por su tasa');
  check(nc.total_usd === -462, `y el total completo en negativo (${nc.total_usd})`);

  console.log('\n== 4. Una alicuota que no existe en la ley NO se sella ==');
  check(ALICUOTAS_LEY.join(',') === '0,8,16,31', 'la lista de la ley es una sola y esta exportada');
  for (const mala of [12, 15, -8, null, undefined, 'dieciseis']) {
    let rebota = false;
    try {
      armarDocumento(venta([{ nombre: 'Invento', cantidad: 1, precio_usd: 10, alicuota: mala }]), EMISOR);
    } catch (e) {
      rebota = /Ley del IVA/.test(e.message);
    }
    check(rebota, `alicuota ${String(mala)} rebota con su porque`);
  }
  let cargoMalo = false;
  try {
    armarDocumento(
      venta([{ nombre: 'Arepa', cantidad: 1, precio_usd: 10, alicuota: 16 }], {
        otros_cargos: [{ nombre: 'Delivery', monto_usd: 2, alicuota: 12 }],
      }),
      EMISOR,
    );
  } catch (e) {
    cargoMalo = /Ley del IVA/.test(e.message);
  }
  check(cargoMalo, 'y un cargo extra con alicuota inventada rebota igual');

  console.log('\n== 5. Por la puerta de terceros pasa lo mismo, sin reventar nada ==');
  const dir = mkdtempSync(join(tmpdir(), 'fiscal-alic-'));
  try {
    guardarConfig(dir, {
      emisor: { ...EMISOR, precios_con_iva: true, alicuota_general: 16 },
      serie: 'A',
      sucursal: '1',
      equipo: 'FISCAL-01',
      imprenta: { adaptador: 'practica', nombre: 'Practica' },
      respaldo: null,
      usando: 'imprenta',
      hora_url: 'http://127.0.0.1:9/no-existe',
    });
    const est = await EstacionFiscal.abrir(dir);
    const buena = await est.recibirVenta(
      venta([
        { nombre: 'Plato', cantidad: 1, precio_usd: 23.2, alicuota: 16 },
        { nombre: 'Cafe del programa', cantidad: 1, precio_usd: 5, alicuota: 0 },
      ]),
      'administrativo-tercero',
    );
    check(buena.sellada === true, 'una venta de tercero con tasas de la ley se sella');
    const mala = await est.recibirVenta(
      venta([{ nombre: 'Plato', cantidad: 1, precio_usd: 10, alicuota: 12 }]),
      'administrativo-tercero',
    );
    check(mala.sellada === false && /Ley del IVA/.test(mala.problema ?? ''),
      `una con alicuota 12 rebota con su porque (${(mala.problema ?? '').slice(0, 40)}...)`);
    check(est.estado().documentos === 1, 'y el libro quedo con UN documento, no con basura');
  } finally {
    rmSync(dir, { recursive: true, force: true });
  }
} catch (e) {
  console.log('  XX  ' + e.message);
  fallos++;
}

console.log(fallos === 0 ? '\nALICUOTAS DE LA LEY: TODO VERDE' : `\n${fallos} FALLO(S)`);
process.exitCode = fallos === 0 ? 0 : 1; // salida natural: process.exit() con undici cerrando revienta libuv en Node 24/Windows
