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

describe('ViajeCalculosService.carga', () => {
  const crear = ({ remision = [], facturas = [], pedidos = [], presentaciones = [] }: any) => {
    const prisma: any = {
      nota_remision_det: { findMany: jest.fn().mockResolvedValue(remision) },
      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 === 'REMISION' ? ['r-1'] : tipo === 'FACTURA' ? ['f-1'] : []),
      ),
    };
    const viajes: any = { findOne: jest.fn().mockResolvedValue({ id: 'v-1', empresa_id: 'emp-1' }) };
    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;
    return { service };
  };

  it('viaje sin documentos devuelve lista vacía', async () => {
    const { service } = crear({});
    const r = await service.carga('v-1', 'emp-1');
    expect(r.items).toEqual([]);
  });

  it('convierte a cajas con el factor de la presentación por defecto', async () => {
    const { service } = crear({
      remision: [{ producto_id: 'p-1', dcantproser: 2400, productos: { descripcion: 'NARANJA', cod_producto: 'J-01' } }],
      facturas: [{ producto_id: 'p-1', dcantproser: 1200, productos: { descripcion: 'NARANJA', cod_producto: 'J-01' } }],
      presentaciones: [{ producto_id: 'p-1', factor_conversion: 240 }],
    });

    const { items } = await service.carga('v-1', 'emp-1');

    expect(items).toHaveLength(1);
    expect(items[0]).toEqual(
      expect.objectContaining({
        producto: 'NARANJA',
        unidad_por_caja: 240,
        remitido_unid: 2400, remitido_cajas: 10,
        facturado_unid: 1200, facturado_cajas: 5,
        diferencia_unid: 1200, diferencia_cajas: 5,
      }),
    );
  });

  it('sin presentación deja las cajas en null y mantiene las unidades', async () => {
    const { service } = crear({
      remision: [{ producto_id: 'p-2', dcantproser: 30, productos: { descripcion: 'CEPILLO', cod_producto: null } }],
      presentaciones: [],
    });

    const { items } = await service.carga('v-1', 'emp-1');

    expect(items[0]).toEqual(
      expect.objectContaining({ producto: 'CEPILLO', unidad_por_caja: null, remitido_unid: 30, remitido_cajas: null }),
    );
  });

  it('producto facturado sin remisión da diferencia negativa y no se pierde', async () => {
    const { service } = crear({
      facturas: [{ producto_id: 'p-3', dcantproser: 5, productos: { descripcion: 'WAFER', cod_producto: 'W-1' } }],
    });

    const { items } = await service.carga('v-1', 'emp-1');

    expect(items[0]).toEqual(
      expect.objectContaining({ producto: 'WAFER', remitido_unid: 0, facturado_unid: 5, diferencia_unid: -5 }),
    );
  });

  it('sin órdenes de venta, lo planificado sale de los presupuestos', async () => {
    const prisma: any = {
      nota_remision_det: { findMany: jest.fn().mockResolvedValue([]) },
      factura_det: { findMany: jest.fn().mockResolvedValue([]) },
      pedido_detalle: { findMany: jest.fn().mockResolvedValue([]) },
      presupuesto_det: {
        findMany: jest.fn().mockResolvedValue([
          {
            producto_id: 'p-1',
            cantidad: 480,
            productos: { descripcion: 'NARANJA', cod_producto: 'J-01' },
            presupuesto: { cliente_id: 'cli-1', pedido_id: null, clientes: { personas: { razon_social: 'CLI-1' } } },
          },
        ]),
      },
      producto_presentaciones: { findMany: jest.fn().mockResolvedValue([{ producto_id: 'p-1', factor_conversion: 240 }]) },
    };
    const documentos: any = {
      idsPorTipo: jest.fn().mockImplementation((_id: string, tipo: string) =>
        Promise.resolve(tipo === 'PRESUPUESTO' ? ['pr-1'] : []),
      ),
    };
    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 = { findOne: jest.fn().mockResolvedValue({ id: 'v-1', empresa_id: 'emp-1' }) };

    const { items, origen_planificado } = await service.carga('v-1', 'emp-1');

    expect(origen_planificado).toBe('PRESUPUESTO');
    expect(items[0]).toEqual(expect.objectContaining({ planificado_unid: 480, planificado_cajas: 2 }));
  });

  it('el presupuesto que originó una orden del viaje no se cuenta dos veces', async () => {
    const prisma: any = {
      nota_remision_det: { findMany: jest.fn().mockResolvedValue([]) },
      factura_det: { findMany: jest.fn().mockResolvedValue([]) },
      pedido_detalle: {
        findMany: jest.fn().mockResolvedValue([
          { producto_id: 'p-1', cantidad_base: 480, producto: { descripcion: 'NARANJA', cod_producto: 'J-01' },
            pedido: { cliente_id: 'cli-1', cliente: { personas: { razon_social: 'CLI-1' } } } },
        ]),
      },
      presupuesto_det: {
        findMany: jest.fn().mockResolvedValue([
          // Derivó en ped-1, que está enganchado: sus líneas se descartan.
          { producto_id: 'p-1', cantidad: 480, productos: { descripcion: 'NARANJA', cod_producto: 'J-01' },
            presupuesto: { cliente_id: 'cli-1', pedido_id: 'ped-1', clientes: { personas: { razon_social: 'CLI-1' } } } },
        ]),
      },
      producto_presentaciones: { findMany: jest.fn().mockResolvedValue([]) },
    };
    const documentos: any = {
      idsPorTipo: jest.fn().mockImplementation((_id: string, tipo: string) =>
        Promise.resolve(tipo === 'PEDIDO' ? ['ped-1'] : tipo === 'PRESUPUESTO' ? ['pr-1'] : []),
      ),
    };
    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 = { findOne: jest.fn().mockResolvedValue({ id: 'v-1', empresa_id: 'emp-1' }) };

    const { items, origen_planificado } = await service.carga('v-1', 'emp-1');

    expect(origen_planificado).toBe('PEDIDO');
    // 480, no 960: el presupuesto se leyó pero sus líneas se descartaron.
    expect(items[0].planificado_unid).toBe(480);
  });

  it('un presupuesto SIN orden de venta sigue sumando aunque el viaje tenga otros pedidos', async () => {
    const prisma: any = {
      nota_remision_det: { findMany: jest.fn().mockResolvedValue([]) },
      factura_det: { findMany: jest.fn().mockResolvedValue([]) },
      pedido_detalle: {
        findMany: jest.fn().mockResolvedValue([
          { producto_id: 'p-1', cantidad_base: 480, producto: { descripcion: 'NARANJA', cod_producto: null },
            pedido: { cliente_id: 'cli-1', cliente: { personas: { razon_social: 'CLI-1' } } } },
        ]),
      },
      presupuesto_det: {
        findMany: jest.fn().mockResolvedValue([
          { producto_id: 'p-2', cantidad: 240, productos: { descripcion: 'MANZANA', cod_producto: null },
            presupuesto: { cliente_id: 'cli-2', pedido_id: null, clientes: { personas: { razon_social: 'CLI-2' } } } },
        ]),
      },
      producto_presentaciones: { findMany: jest.fn().mockResolvedValue([]) },
    };
    const documentos: any = {
      idsPorTipo: jest.fn().mockImplementation((_id: string, tipo: string) =>
        Promise.resolve(tipo === 'PEDIDO' ? ['ped-1'] : tipo === 'PRESUPUESTO' ? ['pr-2'] : []),
      ),
    };
    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 = { findOne: jest.fn().mockResolvedValue({ id: 'v-1', empresa_id: 'emp-1' }) };

    const { items } = await service.carga('v-1', 'emp-1');

    // Los dos clientes aportan: antes el pedido borraba el presupuesto del otro.
    expect(items.map((i: any) => i.producto).sort()).toEqual(['MANZANA', 'NARANJA']);
    expect(items.find((i: any) => i.producto === 'MANZANA').planificado_unid).toBe(240);
  });
});
