import {
  buildBitacoraDiff,
  ImportacionesBitacoraPresenter,
  BitacoraRawEvent,
} from './importaciones-bitacora.presenter';

const UUIDS = {
  empresa: '11111111-1111-4111-8111-111111111111',
  embarque: '22222222-2222-4222-8222-222222222222',
  usuario: '33333333-3333-4333-8333-333333333333',
  componente: '44444444-4444-4444-8444-444444444444',
  proveedor: '55555555-5555-4555-8555-555555555555',
  moneda: '66666666-6666-4666-8666-666666666666',
  concepto: '77777777-7777-4777-8777-777777777777',
  desconocido: '88888888-8888-4888-8888-888888888888',
};

function prismaMock() {
  return {
    usuario: {
      findMany: jest.fn().mockResolvedValue([
        { id: UUIDS.usuario, nombres: 'María', apellidos: 'Gómez' },
      ]),
    },
    proveedores: {
      findMany: jest.fn().mockResolvedValue([
        { id: UUIDS.proveedor, personas: { razon_social: 'Proveedor Internacional SA' } },
      ]),
    },
    moneda: {
      findMany: jest.fn().mockResolvedValue([{ id: UUIDS.moneda, codigo: 'PYG', descripcion: 'Guaraní' }]),
    },
    imp_conceptos_costo: {
      findMany: jest.fn().mockResolvedValue([
        { id: UUIDS.concepto, codigo: 'FOB', nombre: 'FOB - Precio en origen' },
      ]),
    },
    imp_embarque_items: { findMany: jest.fn().mockResolvedValue([]) },
    productos: { findMany: jest.fn().mockResolvedValue([]) },
    depositos: { findMany: jest.fn().mockResolvedValue([]) },
    cuentas_pagar: { findMany: jest.fn().mockResolvedValue([]) },
  };
}

describe('ImportacionesBitacoraPresenter', () => {
  it('registra únicamente valores que cambiaron con anterior y nuevo', () => {
    const result = buildBitacoraDiff(
      {
        importe: { toNumber: () => 100000 },
        fecha_pago: new Date('2026-07-20T00:00:00.000Z'),
        proveedor_id: UUIDS.proveedor,
      },
      {
        importe: 120000,
        fecha_pago: '2026-07-20',
        proveedor_id: UUIDS.desconocido,
      },
      ['importe', 'fecha_pago', 'proveedor_id', 'importe'],
    );

    expect(result).toEqual({
      importe: { anterior: 100000, nuevo: 120000 },
      proveedor_id: { anterior: UUIDS.proveedor, nuevo: UUIDS.desconocido },
    });
  });

  it('convierte el detalle técnico en cambios funcionales sin exponer UUID', async () => {
    const prisma = prismaMock();
    const presenter = new ImportacionesBitacoraPresenter(prisma as any);
    const events: BitacoraRawEvent[] = [
      {
        evento: 'COSTO_EDITADO',
        estado_anterior: null,
        estado_nuevo: null,
        observacion: null,
        usuario_id: UUIDS.usuario,
        created_at: new Date('2026-07-21T14:06:18.000Z'),
        detalle: {
          componente_id: UUIDS.componente,
          concepto: 'FOB - Precio en origen',
          cambios: {
            importe: { anterior: 100000, nuevo: 120000 },
            proveedor_id: { anterior: null, nuevo: UUIDS.proveedor },
            moneda_id: { anterior: UUIDS.desconocido, nuevo: UUIDS.moneda },
            concepto_id: UUIDS.concepto,
          },
        },
      },
    ];

    const result = await presenter.present(events, UUIDS.empresa, UUIDS.embarque);

    expect(result).toEqual([
      expect.objectContaining({
        evento: 'COSTO_EDITADO',
        usuario_nombre: 'María Gómez',
        resumen: 'Se editó un componente de costo: FOB - Precio en origen',
        datos: [{ campo: 'Concepto', tipo: 'texto', valor: 'FOB - Precio en origen' }],
        cambios: [
          {
            campo: 'Importe',
            tipo: 'numero',
            anterior: 100000,
            nuevo: 120000,
            tiene_anterior: true,
          },
          {
            campo: 'Proveedor',
            tipo: 'texto',
            anterior: null,
            nuevo: 'Proveedor Internacional SA',
            tiene_anterior: true,
          },
          {
            campo: 'Moneda',
            tipo: 'texto',
            anterior: 'Referencia no disponible',
            nuevo: 'PYG',
            tiene_anterior: true,
          },
          {
            campo: 'Concepto de costo',
            tipo: 'texto',
            anterior: null,
            nuevo: 'FOB - Precio en origen',
            tiene_anterior: false,
          },
        ],
      }),
    ]);
    const payload = JSON.stringify(result);
    Object.values(UUIDS).forEach((uuid) => expect(payload).not.toContain(uuid));
    expect(payload).not.toContain('componente_id');
    expect(payload).not.toContain('usuario_id');
  });

  it('mantiene compatibilidad con eventos históricos y acciones del sistema', async () => {
    const prisma = prismaMock();
    prisma.usuario.findMany.mockResolvedValue([]);
    const presenter = new ImportacionesBitacoraPresenter(prisma as any);
    const events: BitacoraRawEvent[] = [
      {
        evento: 'EMBARQUE_LIBERADO',
        estado_anterior: null,
        estado_nuevo: null,
        observacion: 'Liberación confirmada',
        usuario_id: null,
        created_at: new Date('2026-07-22T12:00:00.000Z'),
        detalle: {
          deposito_id: UUIDS.desconocido,
          deposito_nombre: 'Depósito Central',
          productos_creados: 1,
          items_actualizados: 1,
          identificador: UUIDS.embarque,
          clave_no_permitida: UUIDS.componente,
        },
      },
    ];

    const result = await presenter.present(events, UUIDS.empresa, UUIDS.embarque);

    expect(result[0]).toEqual(
      expect.objectContaining({
        usuario_nombre: 'Sistema',
        resumen: 'Se liberó el embarque al depósito: Depósito Central',
        observacion: 'Liberación confirmada',
      }),
    );
    expect(result[0].datos).toEqual([
      { campo: 'Depósito', tipo: 'texto', valor: 'Depósito Central' },
      { campo: 'Productos creados', tipo: 'numero', valor: 1 },
      { campo: 'Ítems actualizados', tipo: 'numero', valor: 1 },
      { campo: 'Ítem', tipo: 'texto', valor: 'Referencia no disponible' },
    ]);
    expect(JSON.stringify(result)).not.toContain(UUIDS.componente);
    expect(JSON.stringify(result)).not.toContain(UUIDS.embarque);
  });

  it('presenta de forma segura los eventos operativos del flujo de importación', async () => {
    const prisma = prismaMock();
    const presenter = new ImportacionesBitacoraPresenter(prisma as any);
    const base = {
      estado_anterior: null,
      estado_nuevo: null,
      observacion: null,
      usuario_id: UUIDS.usuario,
      created_at: new Date('2026-07-23T10:00:00.000Z'),
    };
    const events: BitacoraRawEvent[] = [
      {
        ...base,
        evento: 'EMBARQUE_CREADO',
        detalle: { numero: 'IMP-0001', proveedor_id: UUIDS.proveedor, perfil: 'AUTOS' },
      },
      {
        ...base,
        evento: 'ESTADO_CAMBIO',
        estado_anterior: 'BORRADOR',
        estado_nuevo: 'EN_TRANSITO',
        detalle: null,
      },
      {
        ...base,
        evento: 'ITEM_AGREGADO',
        detalle: { item_id: UUIDS.desconocido, identificador: 'KMH123456789', cantidad: 1 },
      },
      {
        ...base,
        evento: 'DESPACHO_CREADO',
        detalle: {
          despacho_id: UUIDS.componente,
          numero_despacho: 'DSP-100',
          total_tributos_gs: 5000000,
        },
      },
      {
        ...base,
        evento: 'DESPACHO_CONFIRMADO',
        detalle: {
          despacho_id: UUIDS.componente,
          numero_despacho: 'DSP-100',
          cotizacion_usada: 7200,
        },
      },
      {
        ...base,
        evento: 'CXP_GENERADA',
        detalle: {
          componente_id: UUIDS.componente,
          proveedor_id: UUIDS.proveedor,
          total_gs: 2500000,
          cuotas: 2,
        },
      },
    ];

    const result = await presenter.present(events, UUIDS.empresa, UUIDS.embarque);
    const payload = JSON.stringify(result);

    expect(result.map((event) => event.evento)).toEqual(events.map((event) => event.evento));
    expect(result[0].datos).toContainEqual({
      campo: 'Proveedor',
      tipo: 'texto',
      valor: 'Proveedor Internacional SA',
    });
    expect(result[1]).toEqual(
      expect.objectContaining({
        estado_anterior: 'BORRADOR',
        estado_nuevo: 'EN_TRANSITO',
      }),
    );
    expect(result[3].resumen).toContain('DSP-100');
    expect(result[5].datos).toContainEqual({
      campo: 'Total de la CxP',
      tipo: 'moneda_gs',
      valor: 2500000,
    });
    Object.values(UUIDS).forEach((uuid) => expect(payload).not.toContain(uuid));
    expect(payload).not.toMatch(/"(?:id|empresa_id|embarque_id|usuario_id|componente_id|despacho_id)":/);
  });
});
