import { DELIM_CSV } from 'src/common/marangatu/registro-comprobantes.util';
import { ComprasService } from './compras.service';

jest.mock('src/utils/alcance-sucursal', () => ({
  ...jest.requireActual('src/utils/alcance-sucursal'),
  exigirAccesoElevado: jest.fn().mockResolvedValue(undefined),
}));

/**
 * El archivo para Marangatu tiene que traer lo mismo que el Libro IVA en pantalla
 * (Latincomp, oct-2026: el archivo no coincidía con la pantalla).
 */
describe('ComprasService - Libro IVA compras para Marangatu', () => {
  const persona = { razon_social: 'PROVEEDOR SA', ruc: '80000001', dv: '7', nro_documento: null };
  const gasto = (over: Record<string, unknown>) => ({
    id: 'g1',
    fecha_emision: new Date('2026-09-10T00:00:00Z'),
    timbrado: '12345678',
    establecimiento: '001',
    punto_expedicion: '001',
    numero_factura: '0000001',
    total_gravado_10: 110000,
    total_gravado_5: 0,
    total_exento: 0,
    forma_pago: 'CONTADO',
    deducible: true,
    proveedores: { personas: persona },
    moneda: { codigo: 'PYG' },
    ...over,
  });

  const crear = (gastos: any[], empresa: Record<string, unknown> = {}, despachos: any[] = []) => {
    const prisma: any = {
      empresas: {
        findUnique: jest
          .fn()
          .mockResolvedValue({ ruc: '80099999', imputa_iva: true, imputa_ire: false, imputa_irp_rsp: false, ...empresa }),
      },
      compra_cab: { findMany: jest.fn().mockResolvedValue([]) },
      gasto_cab: { findMany: jest.fn().mockResolvedValue(gastos) },
      nota_credito_compra_cab: { findMany: jest.fn().mockResolvedValue([]) },
      imp_despachos: { findMany: jest.fn().mockResolvedValue(despachos) },
    };
    return { service: new ComprasService(prisma, {} as any, {} as any, {} as any, {} as any), prisma };
  };

  const columnas = (linea: string) => linea.split(DELIM_CSV);

  it('deja afuera la factura del exterior y los comprobantes en cero, como la pantalla', async () => {
    const { service, prisma } = crear([]);
    await service.getLibroIvaComprasMarangatu('emp-1', 9, 2026, undefined, 'C0001', 'preimpresos', 'u1');
    const where = prisma.compra_cab.findMany.mock.calls[0][0].where;
    expect(where).toMatchObject({ anulado: false, total: { gt: 0 }, embarque_id: null, es_preimpreso: true });
  });

  it('un gasto no deducible no imputa IVA si la empresa imputa IRE', async () => {
    const { service } = crear([gasto({ id: 'g1', deducible: true }), gasto({ id: 'g2', numero_factura: '0000002', deducible: false })], {
      imputa_ire: true,
    });
    const r = await service.getLibroIvaComprasMarangatu('emp-1', 9, 2026, undefined, 'C0001', 'preimpresos', 'u1');
    const [deducible, noDeducible] = r.csv.split('\r\n').map(columnas);
    // Columnas 15-18: imputa IVA, IRE, IRP-RSP, no imputa.
    expect(deducible.slice(14, 18)).toEqual(['S', 'S', 'N', 'N']);
    expect(noDeducible.slice(14, 18)).toEqual(['N', 'S', 'N', 'N']);
  });

  it('nunca deja una fila sin imputación: si la empresa sólo imputa IVA, el no deducible sigue en IVA', async () => {
    const { service } = crear([gasto({ id: 'g2', deducible: false })]);
    const r = await service.getLibroIvaComprasMarangatu('emp-1', 9, 2026, undefined, 'C0001', 'preimpresos', 'u1');
    expect(columnas(r.csv).slice(14, 18)).toEqual(['S', 'N', 'N', 'N']);
  });

  const filas = async (gastos: any[], empresa: Record<string, unknown> = {}, despachos: any[] = []) => {
    const { service } = crear(gastos, empresa, despachos);
    const r = await service.getLibroIvaComprasMarangatu('emp-1', 9, 2026, undefined, 'C0001', 'preimpresos', 'u1');
    return { r, filas: r.csv ? r.csv.split('\r\n').map(columnas) : [] };
  };

  it('autofactura (101): sólo el total, sin condición', async () => {
    const { filas: [f] } = await filas([gasto({ tipo_comprobante: 'AUTOFACTURA' })]);
    expect(f[4]).toBe('101');
    expect(f.slice(8, 13)).toEqual(['0', '0', '0', '110000', '']);
  });

  it('despacho cargado como gasto (107): timbrado 0 y sin número', async () => {
    const { filas: [f] } = await filas([gasto({ tipo_comprobante: 'DESPACHO_IMPORTACION', timbrado: null, numero_factura: null })]);
    expect(f[4]).toBe('107');
    expect(f.slice(6, 8)).toEqual(['0', '']);
  });

  it('despachos del módulo Importaciones (107): base derivada del IVA importación', async () => {
    const { r, filas: [f] } = await filas([], {}, [
      { fecha_despacho: new Date('2026-09-15T00:00:00Z'), iva_importacion_gs: 1000, despachante: { personas: persona } },
    ]);
    expect(r.despachos).toBe(1);
    expect(f[0]).toBe('2');
    expect(f[4]).toBe('107');
    expect(f.slice(8, 12)).toEqual(['11000', '0', '0', '11000']);
  });

  it('comprobante del exterior (202): registro de egresos con identificación tributaria y sin IVA', async () => {
    const exterior = gasto({
      tipo_comprobante: 'COMPROBANTE_EXTERIOR',
      timbrado: null,
      establecimiento: null,
      punto_expedicion: null,
      numero_factura: 'INV-9',
      total: 500000,
      proveedores: { personas: { razon_social: 'ACME INC', ruc: null, nro_documento: 'US-123' } },
    });
    const { r, filas: [f] } = await filas([exterior], { imputa_ire: true });
    expect(r.egresos).toBe(1);
    expect(f).toEqual(['4', '202', '10/09/2026', 'INV-9', '17', 'US-123', 'ACME INC', '500000', 'N', 'S', 'N', 'N', '', '', '', '', '', '']);
  });

  it('un egreso que sin IVA no imputa a nada no sale (la SET lo rechazaría)', async () => {
    const { r, filas: f } = await filas([gasto({ tipo_comprobante: 'COMPROBANTE_EXTERIOR' })]);
    expect(f).toHaveLength(0);
    expect(r.egresos_sin_obligacion).toBe(1);
  });

  it('extracto de tarjeta (207): cuenta y entidad, puede imputar IVA', async () => {
    const { filas: [f] } = await filas([
      gasto({ tipo_comprobante: 'EXTRACTO_TARJETA', numero_factura: null, extracto_cuenta: '4111', extracto_entidad: 'BANCO X' }),
    ]);
    expect(f[1]).toBe('207');
    expect(f.slice(3, 6)).toEqual(['', '', '']);
    expect(f.slice(8, 14)).toEqual(['S', 'N', 'N', 'N', '4111', 'BANCO X']);
  });

  it('lo elegido en el comprobante manda sobre la empresa', async () => {
    const { filas: [f] } = await filas([gasto({ imputa_iva: false, imputa_ire: true })], { imputa_ire: true });
    expect(f.slice(14, 18)).toEqual(['N', 'S', 'N', 'N']);
  });

  it('un gasto en dólares sale en guaraníes con su cotización', async () => {
    const { filas: [f] } = await filas([
      gasto({ moneda: { codigo: 'USD' }, cotizacion: 7000, total_gravado_10: 110, total: 110 }),
    ]);
    expect(f[8]).toBe('770000');
    expect(f[13]).toBe('S');
  });
});
