import { FacturasService } from './facturas.service';

/**
 * Análisis de clientes con varias monedas: los montos se informan en su moneda
 * original y, además, en guaraníes con la cotización guardada en cada documento
 * (dticam de la factura, cotizacion del recibo). Si falta, se usa la vigente y
 * la fila queda marcada como estimada.
 */
describe('FacturasService.getReporteClientes (multi-moneda)', () => {
  const crear = ({ facturas, recibos, cuotas, notas = [] }: { facturas: any[]; recibos: any[]; cuotas: any[]; notas?: any[] }) => {
    const prisma: any = {
      usuario: { findUnique: jest.fn().mockResolvedValue({ is_superadmin: true }) },
      empresas: { findUnique: jest.fn().mockResolvedValue({ type: 'subsidiary' }) },
      rubros: { findFirst: jest.fn().mockResolvedValue(null) },
      asignacion_sucursal_caja_usuario: { findMany: jest.fn().mockResolvedValue([]) },
      empresas_sucursales: { findMany: jest.fn().mockResolvedValue([]) },
      factura_cab: {
        findMany: jest.fn().mockResolvedValue(facturas),
        groupBy: jest.fn().mockResolvedValue([{ cliente_id: 'cli-1', _min: { dfeemide: new Date('2026-01-10') } }]),
      },
      recibos_cobro: { findMany: jest.fn().mockResolvedValue(recibos) },
      nota_credito_cab: { findMany: jest.fn().mockResolvedValue(notas) },
      factura_cuotas: { findMany: jest.fn().mockResolvedValue(cuotas) },
      clientes: {
        findMany: jest.fn().mockResolvedValue([
          { id: 'cli-1', nombre_fantasia: null, cod_cliente: null, personas: { razon_social: 'CLIENTE UNO', ruc: '800', dv: '1' } },
        ]),
      },
      moneda: {
        findMany: jest.fn().mockResolvedValue([
          { id: 'm-pyg', codigo: 'PYG' },
          { id: 'm-usd', codigo: 'USD' },
        ]),
      },
    };
    const monedasService = { getCotizacionVigente: jest.fn().mockResolvedValue({ tasa: 7500 }) };
    const service = Object.create(FacturasService.prototype) as any;
    service.prisma = prisma;
    service.monedasService = monedasService;
    return { service, monedasService, prisma };
  };

  const factura = (moneda: 'PYG' | 'USD', total: number, dticam: number | null) => ({
    cliente_id: 'cli-1',
    moneda_id: moneda === 'PYG' ? 'm-pyg' : 'm-usd',
    dfeemide: new Date('2026-09-05'),
    dticam,
    moneda: { codigo: moneda },
    factura_subtotales: [{ dtotgralope: total }],
  });

  it('separa las filas por moneda y convierte con la cotización de cada documento', async () => {
    const { service, monedasService } = crear({
      facturas: [factura('PYG', 1_000_000, null), factura('USD', 100, 7300), factura('USD', 50, 7400)],
      recibos: [{ cliente_id: 'cli-1', moneda_id: 'm-usd', monto_total: 20, cotizacion: 7350, moneda: { codigo: 'USD' } }],
      cuotas: [
        {
          cmonecuo: 'USD',
          dvenccuo: new Date('2020-01-01'),
          saldo_pendiente: 30,
          factura_cab: { cliente_id: 'cli-1', moneda_id: 'm-usd', dticam: 7300 },
        },
      ],
    });

    const r = await service.getReporteClientes('emp-1', { fecha_desde: '2026-09-01', fecha_hasta: '2026-09-30' });

    const usd = r.data.find((d: any) => d.moneda === 'USD');
    const pyg = r.data.find((d: any) => d.moneda === 'PYG');
    expect(usd.total_facturado).toBe(150);
    expect(usd.facturado_base).toBe(100 * 7300 + 50 * 7400);
    expect(usd.total_cobrado).toBe(20);
    expect(usd.cobrado_base).toBe(20 * 7350);
    expect(usd.saldo_pendiente).toBe(30);
    expect(usd.saldo_pendiente_base).toBe(30 * 7300);
    expect(usd.saldo_vencido_base).toBe(30 * 7300);
    expect(usd.cotizacion_estimada).toBe(false);
    // Guaraníes: sin conversión aunque la factura no tenga dticam.
    expect(pyg.facturado_base).toBe(1_000_000);
    expect(monedasService.getCotizacionVigente).not.toHaveBeenCalled();
    expect(r.moneda_base).toBe('PYG');
    expect(r.resumen.total_facturado).toBe(1_000_000 + 100 * 7300 + 50 * 7400);
  });

  it('sin cotización en el documento usa la vigente y marca la fila como estimada', async () => {
    const { service, monedasService } = crear({ facturas: [factura('USD', 100, null)], recibos: [], cuotas: [] });

    const r = await service.getReporteClientes('emp-1', { fecha_desde: '2026-09-01', fecha_hasta: '2026-09-30' });

    expect(r.data[0].facturado_base).toBe(100 * 7500);
    expect(r.data[0].cotizacion_estimada).toBe(true);
    expect(monedasService.getCotizacionVigente).toHaveBeenCalledWith('emp-1', 'USD');
  });

  it('descuenta las notas de crédito del período con su propia cotización', async () => {
    const { service, prisma } = crear({
      facturas: [factura('USD', 100, 7300)],
      recibos: [],
      cuotas: [],
      notas: [
        { cliente_id: 'cli-1', moneda_id: 'm-usd', dticam: 7400, moneda: { codigo: 'USD' }, nota_credito_subtotal: [{ dtotgralope: 10 }] },
        // Cliente que en el período sólo tuvo una NC: igual aparece.
        { cliente_id: 'cli-2', moneda_id: 'm-pyg', dticam: null, moneda: { codigo: 'PYG' }, nota_credito_subtotal: [{ dtotgralope: 50000 }] },
      ],
    });

    const r = await service.getReporteClientes('emp-1', { fecha_desde: '2026-09-01', fecha_hasta: '2026-09-30' });

    const usd = r.data.find((d: any) => d.moneda === 'USD');
    expect(usd.cant_nc).toBe(1);
    expect(usd.total_nc).toBe(10);
    expect(usd.facturado_neto).toBe(90);
    expect(usd.nc_base).toBe(10 * 7400);
    expect(usd.facturado_neto_base).toBe(100 * 7300 - 10 * 7400);
    expect(r.data.find((d: any) => d.cliente_id === 'cli-2').total_nc).toBe(50000);
    // Las NC anuladas/rechazadas o canceladas por evento no cuentan.
    const where = prisma.nota_credito_cab.findMany.mock.calls[0][0].where;
    expect(where.estado.notIn).toEqual(expect.arrayContaining(['Anulado', 'Rechazado']));
    expect(where.dfeemide.gte).toBeInstanceOf(Date);
  });
});
