import { ViajeCalculosService } from '../viaje-calculos.service';

describe('ViajeCalculosService.matriz', () => {
  const lineaFactura = (cliente: string, producto: string, cant: number, importe: number, moneda = 'PYG') => ({
    producto_id: producto,
    dcantproser: cant,
    dtotopeitem: importe,
    productos: { descripcion: producto.toUpperCase(), cod_producto: null },
    factura_cab: {
      cliente_id: cliente,
      moneda: { codigo: moneda },
      dticam: moneda === 'PYG' ? null : 7300,
      clientes: { personas: { razon_social: cliente.toUpperCase() } },
    },
  });

  const crear = ({ facturas = [], pedidos = [], presentaciones = [] }: any) => {
    const prisma: any = {
      factura_det: { findMany: jest.fn().mockResolvedValue(facturas) },
      pedido_detalle: { findMany: jest.fn().mockResolvedValue(pedidos) },
      presupuesto_det: { findMany: jest.fn().mockResolvedValue([]) },
      producto_presentaciones: { findMany: jest.fn().mockResolvedValue(presentaciones) },
    };
    const documentos: any = {
      idsPorTipo: jest.fn().mockImplementation((_id: string, tipo: string) =>
        Promise.resolve(tipo === 'FACTURA' ? ['f-1'] : tipo === 'PEDIDO' && pedidos.length ? ['p-1'] : []),
      ),
    };
    const viajes: any = { findOne: jest.fn().mockResolvedValue({ id: 'v-1', empresa_id: 'emp-1' }) };
    const monedas: any = { getCotizacionVigente: jest.fn().mockResolvedValue({ tasa: 7500 }) };
    const service = Object.create(ViajeCalculosService.prototype) as any;
    service.prisma = prisma;
    // Los cálculos piden solo los vigentes; acá el filtro de bajas no es lo que se prueba.
    documentos.idsVigentes ??= documentos.idsPorTipo;
    service.documentos = documentos;
    service.viajes = viajes;
    service.monedas = monedas;
    return { service };
  };

  it('viaje sin documentos devuelve matriz vacía', async () => {
    const { service } = crear({});
    const m = await service.matriz('v-1', 'emp-1', 'entregado');
    expect(m.clientes).toEqual([]);
    expect(m.productos).toEqual([]);
    expect(m.celdas).toEqual({});
  });

  it('arma la celda por producto y cliente con cajas e importe', async () => {
    const { service } = crear({
      facturas: [lineaFactura('cli-1', 'naranja', 2400, 1_200_000)],
      presentaciones: [{ producto_id: 'naranja', factor_conversion: 240 }],
    });

    const m = await service.matriz('v-1', 'emp-1', 'entregado');

    expect(m.clientes).toEqual([expect.objectContaining({ id: 'cli-1', nombre: 'CLI-1', moneda: 'PYG' })]);
    expect(m.celdas['naranja::cli-1']).toEqual(
      expect.objectContaining({ unid: 2400, cajas: 10, por_moneda: { PYG: 1_200_000 }, importe_base: 1_200_000 }),
    );
    expect(m.totales_producto['naranja']).toEqual(expect.objectContaining({ unid: 2400, cajas: 10 }));
    expect(m.totales_cliente['cli-1']).toEqual(
      expect.objectContaining({ por_moneda: { PYG: 1_200_000 }, importe_base: 1_200_000, moneda: 'PYG' }),
    );
  });

  it('sin pedidos ni presupuestos, la fuente planificado cae a entregado y avisa', async () => {
    const { service } = crear({ facturas: [lineaFactura('cli-1', 'naranja', 240, 120_000)] });
    (service as any).lineasPlanificadas = jest.fn().mockResolvedValue({ origen: null, lineas: [] });

    const m = await service.matriz('v-1', 'emp-1', 'planificado');

    expect(m.fuente_efectiva).toBe('entregado');
    expect(m.aviso).toMatch(/no tiene/i);
  });

  it('comparado pone pedido y entregado en la misma celda', async () => {
    const { service } = crear({
      facturas: [lineaFactura('cli-1', 'naranja', 240, 120_000)],
      pedidos: [{ producto_id: 'naranja', cantidad_base: 480 }],
    });
    (service as any).lineasPlanificadas = jest.fn().mockResolvedValue({
      origen: 'PEDIDO',
      lineas: [
        { producto_id: 'naranja', cliente_id: 'cli-1', cliente: 'CLI-1', cantidad_base: 480, producto: 'NARANJA', cod_producto: null },
      ],
    });

    const m = await service.matriz('v-1', 'emp-1', 'comparado');

    expect(m.celdas['naranja::cli-1']).toEqual(
      expect.objectContaining({ unid_plan: 480, unid: 240 }),
    );
  });

  it('dos monedas: cada cliente conserva la suya', async () => {
    const { service } = crear({
      facturas: [
        lineaFactura('cli-1', 'naranja', 240, 120_000),
        lineaFactura('cli-2', 'naranja', 240, 100, 'USD'),
      ],
    });

    const m = await service.matriz('v-1', 'emp-1', 'entregado');

    expect(m.totales_cliente['cli-1'].moneda).toBe('PYG');
    expect(m.totales_cliente['cli-2'].moneda).toBe('USD');
  });
});

describe('ViajeCalculosService.matriz — un cliente con dos monedas', () => {
  const linea = (cliente, producto, cant, importe, moneda, dticam) => ({
    producto_id: producto,
    dcantproser: cant,
    dtotopeitem: importe,
    productos: { descripcion: producto.toUpperCase(), cod_producto: null },
    factura_cab: {
      cliente_id: cliente,
      moneda: { codigo: moneda },
      dticam,
      clientes: { personas: { razon_social: cliente.toUpperCase() } },
    },
  });

  it('no mezcla monedas: informa cada una y convierte el total con la cotización del documento', async () => {
    const prisma: any = {
      factura_det: {
        findMany: jest.fn().mockResolvedValue([
          linea('cli-1', 'naranja', 100, 700_000, 'PYG', null),
          linea('cli-1', 'naranja', 10, 100, 'USD', 7300),
        ]),
      },
      pedido_detalle: { findMany: jest.fn().mockResolvedValue([]) },
      presupuesto_det: { findMany: jest.fn().mockResolvedValue([]) },
      producto_presentaciones: { findMany: jest.fn().mockResolvedValue([]) },
    };
    const documentos: any = {
      idsPorTipo: jest.fn().mockImplementation((_id, tipo) => Promise.resolve(tipo === 'FACTURA' ? ['f-1', 'f-2'] : [])),
    };
    const viajes: any = { findOne: jest.fn().mockResolvedValue({ id: 'v-1', empresa_id: 'emp-1' }) };
    const monedas: any = { getCotizacionVigente: jest.fn().mockResolvedValue({ tasa: 7500 }) };
    const service = Object.create(ViajeCalculosService.prototype) as any;
    service.prisma = prisma;
    // Los cálculos piden solo los vigentes; acá el filtro de bajas no es lo que se prueba.
    documentos.idsVigentes ??= documentos.idsPorTipo;
    service.documentos = documentos;
    service.viajes = viajes;
    service.monedas = monedas;

    const m = await service.matriz('v-1', 'emp-1', 'entregado');

    // Cada moneda por separado, no un número mezclado.
    expect(m.totales_cliente['cli-1'].por_moneda).toEqual({ PYG: 700_000, USD: 100 });
    // Equivalente en la base usando el dticam de cada documento.
    expect(m.totales_cliente['cli-1'].importe_base).toBe(700_000 + 100 * 7300);
    expect(m.totales_cliente['cli-1'].moneda).toBe('PYG');
    // La celda también informa su desglose.
    expect(m.celdas['naranja::cli-1'].por_moneda).toEqual({ PYG: 700_000, USD: 100 });
    expect(m.celdas['naranja::cli-1'].importe_base).toBe(700_000 + 100 * 7300);
  });
});
