import { ViajesService } from '../viajes.service';

describe('ViajesService — estados', () => {
  const crear = (viaje: any) => {
    const prisma: any = {
      viajes: {
        findFirst: jest.fn().mockResolvedValue(viaje),
        update: jest.fn().mockImplementation(({ data }) => Promise.resolve({ ...viaje, ...data })),
      },
      viaje_documentos: { deleteMany: jest.fn().mockResolvedValue({ count: 3 }) },
      viaje_rendiciones: { deleteMany: jest.fn().mockResolvedValue({ count: 2 }) },
      $transaction: jest.fn().mockImplementation((ops: any[]) => Promise.all(ops)),
    };
    const service = Object.create(ViajesService.prototype) as any;
    service.prisma = prisma;
    return { service, prisma };
  };

  const base = { id: 'v-1', empresa_id: 'emp-1', estado: 'EN_RUTA', km_salida: 25187, documentos: [] };

  it('cerrar exige fecha y km de retorno', async () => {
    const { service } = crear(base);
    await expect(service.cerrar('v-1', {}, 'emp-1', 'usr-1')).rejects.toThrow(/km de retorno/i);
  });

  it('el km de retorno no puede ser menor al de salida', async () => {
    const { service } = crear(base);
    await expect(
      service.cerrar('v-1', { fecha_retorno: '2026-09-25', km_retorno: 25000 }, 'emp-1', 'usr-1'),
    ).rejects.toThrow(/menor/i);
  });

  it('cierra y deja registrado quién y cuándo', async () => {
    const { service, prisma } = crear(base);
    const cerrado = await service.cerrar('v-1', { fecha_retorno: '2026-09-25', km_retorno: 25790 }, 'emp-1', 'usr-1');
    expect(cerrado.estado).toBe('CERRADO');
    const data = prisma.viajes.update.mock.calls[0][0].data;
    expect(data.cerrado_por).toBe('usr-1');
    expect(data.fecha_cierre).toBeInstanceOf(Date);
    expect(data.km_retorno).toBe(25790);
  });

  it('un viaje cerrado no se edita hasta reabrirlo', async () => {
    const { service } = crear({ ...base, estado: 'CERRADO' });
    await expect(service.update('v-1', { zona: 'Norte' }, 'emp-1')).rejects.toThrow(/cerrado/i);
  });

  it('reabrir vuelve a EN_RUTA y registra quién', async () => {
    const { service, prisma } = crear({ ...base, estado: 'CERRADO' });
    const reabierto = await service.reabrir('v-1', 'emp-1', 'usr-2');
    expect(reabierto.estado).toBe('EN_RUTA');
    expect(prisma.viajes.update.mock.calls[0][0].data.reabierto_por).toBe('usr-2');
  });

  it('anular exige motivo y no se puede anular dos veces', async () => {
    const { service } = crear({ ...base, estado: 'ANULADO' });
    await expect(service.anular('v-1', 'se suspendió', 'emp-1', 'usr-1')).rejects.toThrow(/anulado/i);
  });

  it('anular guarda lo que tenía y libera documentos y rendiciones en la misma transacción', async () => {
    const { service, prisma } = crear(base);
    const registro = { documentos: [{ tipo: 'FACTURA', numero: '001-001-0000009' }], rendiciones: [] };

    const r = await service.anular('v-1', 'No se realizó', 'emp-1', 'usr-1', registro);

    expect(prisma.$transaction).toHaveBeenCalledTimes(1);
    expect(prisma.viajes.update.mock.calls[0][0].data).toEqual(expect.objectContaining({
      estado: 'ANULADO', motivo_anulacion: 'No se realizó', anulado_por: 'usr-1', documentos_al_anular: registro,
    }));
    expect(prisma.viaje_documentos.deleteMany).toHaveBeenCalledWith({ where: { viaje_id: 'v-1' } });
    expect(prisma.viaje_rendiciones.deleteMany).toHaveBeenCalledWith({ where: { viaje_id: 'v-1' } });
    expect(r.estado).toBe('ANULADO');
  });
});
