import { BadRequestException } from '@nestjs/common';
import { MiddlewareSifenService } from './middleware-sifen.service';

/**
 * Acciones de las alertas de eventos SIFEN: Reconsultar y Liberar.
 * Ver docs/plan-alertas-empresa.md.
 */
describe('MiddlewareSifenService - reconsultar / liberar evento', () => {
  const pendiente = {
    id: 'fac-1',
    empresa_id: 'emp-1',
    dest: '001',
    dpunexp: '002',
    dnumdoc: '0000552',
    evento_aplicado: 'EINU',
    estado_evento: 'Pendiente',
    mensaje_evento: null,
    fecha_evento: null,
    estado_sifen: 'Rechazado',
  };

  const crearService = (documento: Record<string, unknown> | null, caso?: string) => {
    const prisma: any = {
      factura_cab: {
        findFirst: jest.fn().mockResolvedValue(documento),
        updateMany: jest.fn().mockResolvedValue({ count: 1 }),
      },
      usuario: { findUnique: jest.fn().mockResolvedValue({ username: 'contador' }) },
    };
    const audit = { log: jest.fn().mockResolvedValue(undefined) };
    const alertas = { resolver: jest.fn().mockResolvedValue(true), registrar: jest.fn() };
    const service = new MiddlewareSifenService(
      {} as any,
      prisma,
      audit as any,
      {} as any,
      {} as any,
      {} as any,
      {} as any,
      alertas as any,
      {} as any,
    );
    const sync = jest
      .spyOn(service, 'sincronizarSifen')
      .mockResolvedValue({ actualizados: 0, detalles: [], casosEvento: caso ? { 'fac-1': caso as any } : {} });
    return { service, prisma, audit, alertas, sync };
  };

  describe('reconsultarEvento', () => {
    it('consulta sólo ese documento y devuelve el caso', async () => {
      const { service, sync } = crearService(pendiente, 'sin_respuesta');

      const r = await service.reconsultarEvento('emp-1', 'factura', 'fac-1');

      expect(sync).toHaveBeenCalledWith('emp-1', 'factura', 'evento', undefined, 'fac-1');
      expect(r.caso).toBe('sin_respuesta');
    });

    it('sin evento pendiente no consulta nada', async () => {
      const { service, sync } = crearService({ ...pendiente, estado_evento: 'Aprobado' });

      const r = await service.reconsultarEvento('emp-1', 'factura', 'fac-1');

      expect(r).toMatchObject({ success: false, caso: 'sin_evento_pendiente' });
      expect(sync).not.toHaveBeenCalled();
    });
  });

  describe('liberarEvento', () => {
    it('exige motivo', async () => {
      const { service } = crearService(pendiente, 'no_registrado');

      await expect(service.liberarEvento('emp-1', 'factura', 'fac-1', '  ', 'usr-1')).rejects.toBeInstanceOf(
        BadRequestException,
      );
    });

    it('si SIFEN tiene el evento no libera', async () => {
      for (const caso of ['respondido', 'sin_respuesta', 'middleware_falla', 'en_espera']) {
        const { service, prisma, alertas } = crearService(pendiente, caso);

        const r = await service.liberarEvento('emp-1', 'factura', 'fac-1', 'nunca llegó a SIFEN', 'usr-1');

        expect(r.success).toBe(false);
        expect(prisma.factura_cab.updateMany).not.toHaveBeenCalled();
        expect(alertas.resolver).not.toHaveBeenCalled();
      }
    });

    it.each(['no_registrado', 'incoherente'])('con caso %s libera, resuelve la alerta y audita', async (caso) => {
      const { service, prisma, alertas, audit } = crearService(pendiente, caso);

      const r = await service.liberarEvento('emp-1', 'factura', 'fac-1', 'nunca llegó a SIFEN', 'usr-1');

      expect(r.success).toBe(true);
      const upd = prisma.factura_cab.updateMany.mock.calls[0][0];
      expect(upd.where).toEqual({ id: 'fac-1', empresa_id: 'emp-1', estado_evento: 'Pendiente' });
      expect(upd.data.estado_evento).toBe('Liberado');
      expect(upd.data.mensaje_evento).toContain('contador');
      expect(upd.data.mensaje_evento).toContain('nunca llegó a SIFEN');
      expect(alertas.resolver).toHaveBeenCalledWith('emp-1', 'sifen_evento:factura:fac-1', 'liberada', 'usr-1');
      expect(audit.log).toHaveBeenCalled();
    });

    it('si otro usuario lo liberó en paralelo no audita dos veces', async () => {
      const { service, prisma, audit } = crearService(pendiente, 'no_registrado');
      prisma.factura_cab.updateMany.mockResolvedValue({ count: 0 });

      const r = await service.liberarEvento('emp-1', 'factura', 'fac-1', 'nunca llegó a SIFEN', 'usr-1');

      expect(r.success).toBe(false);
      expect(audit.log).not.toHaveBeenCalled();
    });
  });
});
