import { crearIntegracionMock } from './__helpers__/integracion-mock';
import { FaltaMapeoError } from './falta-mapeo.error';

const embarque = { id: 'e1', empresa_id: 'emp', numero: 'IMP-1', estado: 'CERRADO', costo_total_gs: 1000, usuario_id: 'u1', despacho: { id: 'd1', fecha_despacho: new Date() } };

const crear = (saldo: { debe: number; haber: number }, emb: any = embarque) =>
  crearIntegracionMock({
    imp_embarques: { findUnique: jest.fn().mockResolvedValue(emb) },
    imp_despachos: { findMany: jest.fn().mockResolvedValue([{ id: 'd1' }]) },
    imp_componentes_costo: { findMany: jest.fn().mockResolvedValue([{ compra_id: 'c1', gasto_id: null }, { compra_id: null, gasto_id: 'g1' }]), findFirst: jest.fn() },
    tes_movimientos: { findMany: jest.fn().mockResolvedValue([{ id: 't1' }]) },
    nota_credito_compra_cab: { findMany: jest.fn().mockResolvedValue([{ id: 'nc1' }]) },
    cont_documentos: {
      findFirst: jest.fn().mockResolvedValue(null),
      findMany: jest.fn().mockResolvedValue([{ id: 'doc-d1' }, { id: 'doc-c1' }]),
      create: jest.fn().mockResolvedValue({ id: 'doc-cierre' }),
    },
    cont_asientos: { findMany: jest.fn().mockResolvedValue([{ id: 'a1' }, { id: 'a2' }]) },
    cont_asientos_det: { aggregate: jest.fn().mockResolvedValue({ _sum: { debe_pyg: saldo.debe, haber_pyg: saldo.haber } }) },
  });

describe('cierre de importación', () => {
  it('traslada solo lo que entró a la cuenta puente', async () => {
    const s = crear({ debe: 800, haber: 0 });
    await s.integrarCierreImportacion('e1');
    const l = s.asientos.crearConfirmado.mock.calls[0][3].lineas;
    expect(l).toEqual([
      expect.objectContaining({ cuenta_id: 'cta-INVENTARIO', debe_moneda: 800 }),
      expect.objectContaining({ cuenta_id: 'cta-IMPORTACIONES_EN_CURSO', haber_moneda: 800 }),
    ]);
  });

  it('[RF-4] con saldo 0 (despacho revertido) no crea asiento', async () => {
    const s = crear({ debe: 500, haber: 500 });
    await s.integrarCierreImportacion('e1');
    expect(s.asientos.crearConfirmado).not.toHaveBeenCalled();
  });

  it('busca los documentos del despacho, compras, sus NC, gastos y pagos de la importación', async () => {
    const s = crear({ debe: 800, haber: 0 });
    await s.integrarCierreImportacion('e1');
    const where = s.prisma.cont_documentos.findMany.mock.calls[0][0].where;
    expect(where.OR).toEqual(expect.arrayContaining([
      { origen_tipo: 'imp_despacho', origen_id: { in: ['d1'] } },
      { origen_tipo: 'compra_cab', origen_id: { in: ['c1'] } },
      { origen_tipo: 'nota_credito_compra_cab', origen_id: { in: ['nc1'] } },
      { origen_tipo: 'gasto_cab', origen_id: { in: ['g1'] } },
      { origen_tipo: 'tes_movimientos', origen_id: { in: ['t1'] } },
    ]));
    expect(s.prisma.nota_credito_compra_cab.findMany.mock.calls[0][0].where).toEqual({ empresa_id: 'emp', compra_id: { in: ['c1'] } });
    expect(s.prisma.cont_asientos_det.aggregate.mock.calls[0][0].where).toEqual({ asiento_id: { in: ['a1', 'a2'] }, cuenta_id: 'cta-IMPORTACIONES_EN_CURSO' });
  });

  it('solo cuenta asientos vigentes (los revertidos no suman)', async () => {
    const s = crear({ debe: 800, haber: 0 });
    await s.integrarCierreImportacion('e1');
    expect(s.prisma.cont_asientos.findMany.mock.calls[0][0].where).toEqual(
      expect.objectContaining({ estado: 'CONFIRMADO' }),
    );
    expect(s.prisma.tes_movimientos.findMany.mock.calls[0][0].where).toEqual(
      expect.objectContaining({ embarque_id: 'e1', estado: { not: 'ANULADO' } }),
    );
  });

  it('el asiento lleva el usuario del embarque, nunca el id de la empresa', async () => {
    const s = crear({ debe: 800, haber: 0 });
    await s.integrarCierreImportacion('e1');
    expect(s.asientos.crearConfirmado.mock.calls[0][1]).toBe('u1');
  });

  it('sin usuario en el embarque, el asiento va sin usuario (null)', async () => {
    const s = crear({ debe: 800, haber: 0 }, { ...embarque, usuario_id: null });
    await s.integrarCierreImportacion('e1');
    expect(s.asientos.crearConfirmado.mock.calls[0][1]).toBeNull();
  });
  it('sin fila de mapeo de la puente: la empresa no usa el modelo de puente (sin asiento ni aviso)', async () => {
    const s = crearIntegracionMock({ imp_embarques: { findUnique: jest.fn().mockResolvedValue(embarque) } }, ['IMPORTACIONES_EN_CURSO']);
    await expect(s.integrarCierreImportacion('e1')).resolves.toBeUndefined();
    expect(s.asientos.crearConfirmado).not.toHaveBeenCalled();
    expect(s.avisosMapeo.registrarFaltaMapeo).not.toHaveBeenCalled();
  });

  it('puente mapeada a una cuenta inutilizable: queda pendiente con aviso (no se descarta en silencio)', async () => {
    const s = crear({ debe: 800, haber: 0 });
    const original = s.mapeo.getCuentaPorConcepto.getMockImplementation();
    s.mapeo.getCuentaPorConcepto.mockImplementation((e: string, c: string) =>
      c === 'IMPORTACIONES_EN_CURSO'
        ? Promise.reject(new FaltaMapeoError(e, [c], 'mapeado a 1.1.4.04, que está desactivada', 'CUENTA_INUTILIZABLE', '1.1.4.04'))
        : original(e, c),
    );
    await expect(s.integrarCierreImportacion('e1')).resolves.toBeUndefined();
    expect(s.asientos.crearConfirmado).not.toHaveBeenCalled();
    expect(s.avisosMapeo.registrarFaltaMapeo).toHaveBeenCalledWith({
      empresaId: 'emp', origenTipo: 'imp_cierre', origenId: 'e1', conceptos: ['IMPORTACIONES_EN_CURSO'], motivo: 'CUENTA_INUTILIZABLE',
    });
  });

  it('un error que no es de mapeo al leer la puente se relanza', async () => {
    const s = crear({ debe: 800, haber: 0 });
    s.mapeo.getCuentaPorConcepto.mockRejectedValue(new Error('conexión caída'));
    await expect(s.integrarCierreImportacion('e1')).rejects.toThrow('conexión caída');
  });
});
