import { ContabilidadConciliacionService } from './contabilidad-conciliacion.service';
import { FaltaMapeoError } from '../contabilidad/services/falta-mapeo.error';

/**
 * Una cuenta desactivada / de agrupación ahora llega como FaltaMapeoError (era
 * BadRequestException). El diagnóstico debe seguir diciendo CUENTA_OLD (reasignar)
 * y no SIN_MAPEO (crear).
 */
function crear(error: Error | null, constancias: any[] = [], reglas: any[] = [], mapeosMedio: any[] = []) {
  const prisma: any = {
    cont_origen_registry: {
      findUnique: jest.fn().mockResolvedValue({
        origen_tipo: 'tes_movimientos',
        tabla_origen: 'tes_movimientos',
        campo_fecha: 'fecha',
        conceptos_req: ['INVENTARIO'],
      }),
    },
    tes_movimientos: {
      findFirst: jest.fn().mockResolvedValue({ id: 'm1', empresa_id: 'emp', fecha: new Date() }),
      findMany: jest.fn().mockResolvedValue([{ id: 'm1', fecha: new Date() }]),
    },
    cont_mapeo_cuentas: {
      findFirst: jest.fn().mockResolvedValue({ cuenta: { codigo: 'X' } }),
      findMany: jest.fn().mockResolvedValue([{ concepto: 'INVENTARIO', modo: 'CUENTA', cuenta: { codigo: '1.1', descripcion: 'INV', active: true, acepta_movimientos: true } }]),
    },
    cont_plan_cuentas: { findUnique: jest.fn().mockResolvedValue({ codigo: '1.1', active: true }) },
    audit_logs: { findMany: jest.fn().mockResolvedValue(constancias) },
    cont_mapeo_reglas: { findMany: jest.fn().mockResolvedValue(reglas) },
    cont_mapeo_medio_pago: { findMany: jest.fn().mockResolvedValue(mapeosMedio) },
  };
  const mapeo = { getCuentaPorConcepto: error ? jest.fn().mockRejectedValue(error) : jest.fn().mockResolvedValue('cta-inv') };
  const service = new ContabilidadConciliacionService(prisma, {} as any, mapeo as any, {} as any, {} as any, {} as any, {} as any, {} as any);
  jest.spyOn(service as any, 'tieneModuloContabilidad').mockResolvedValue(true);
  return service;
}

describe('diagnóstico con cuenta mapeada inutilizable', () => {
  const inutilizable = () => new FaltaMapeoError('emp', ['INVENTARIO'], 'mapeado a X, que está desactivada', 'CUENTA_INUTILIZABLE', 'X');

  it('diagnosticarLote: cuenta desactivada es CUENTA_OLD', async () => {
    const r = await crear(inutilizable()).diagnosticarLote('emp', 'tes_movimientos', ['m1']);
    expect(r[0].motivo).toBe('CUENTA_OLD');
  });

  it('diagnosticarRegistro: cuenta desactivada es CUENTA_OLD', async () => {
    const r = await crear(inutilizable()).diagnosticarRegistro('emp', 'tes_movimientos', 'm1');
    expect(r.motivo).toBe('CUENTA_OLD');
  });

  it('sin mapeo sigue siendo SIN_MAPEO', async () => {
    const e = new FaltaMapeoError('emp', ['INVENTARIO']);
    const service = crear(e);
    expect((await service.diagnosticarLote('emp', 'tes_movimientos', ['m1']))[0].motivo).toBe('SIN_MAPEO');
    expect((await service.diagnosticarRegistro('emp', 'tes_movimientos', 'm1')).motivo).toBe('SIN_MAPEO');
  });

  it('[I1] defecto usable pero una regla activa rota frenó el documento: CUENTA_OLD que nombra la regla', async () => {
    const constancias = [{ entity_id: 'm1', new_value: { origen_tipo: 'tes_movimientos', conceptos: ['INVENTARIO'], motivo: 'CUENTA_INUTILIZABLE', regla: { id: 'r9', orden: 3, concepto: 'INVENTARIO' } } }];
    const reglas = [{ id: 'r9', concepto: 'INVENTARIO', orden: 3, estado: 'ACTIVA', modo: 'CUENTA', cuenta_id: 'g', vigente_desde: new Date('2026-01-01'), vigente_hasta: null,
      cuenta: { active: false, acepta_movimientos: true, empresa_id: 'emp' } }];
    const service = crear(null, constancias, reglas);
    jest.spyOn(service as any, 'diagnosticoEspecifico').mockResolvedValue(null);
    (service as any).periodos = { resolverPeriodoPorFecha: jest.fn().mockResolvedValue('p') };
    const r = await service.diagnosticarRegistro('emp', 'tes_movimientos', 'm1');
    expect(r.motivo).toBe('CUENTA_OLD');
    expect(r.descripcion).toMatch(/regla n\.º 3 de INVENTARIO apunta a una cuenta que no sirve.*retirala y duplicala/);
    const lote = await service.diagnosticarLote('emp', 'tes_movimientos', ['m1']);
    expect(lote[0]).toMatchObject({ id: 'm1', motivo: 'CUENTA_OLD', descripcion: expect.stringMatching(/regla n\.º 3/) });
  });
  it('concepto que el integrador pidió y no está en conceptos_req (IVA_NO_DEDUCIBLE): SIN_MAPEO, no "listo para regenerar"', async () => {
    const constancias = [{ entity_id: 'm1', new_value: { origen_tipo: 'tes_movimientos', conceptos: ['IVA_NO_DEDUCIBLE'], motivo: 'SIN_MAPEO' } }];
    const service = crear(null, constancias);
    jest.spyOn(service as any, 'diagnosticoEspecifico').mockResolvedValue(null);
    (service as any).periodos = { resolverPeriodoPorFecha: jest.fn().mockResolvedValue('p') };
    const r = await service.diagnosticarRegistro('emp', 'tes_movimientos', 'm1');
    expect(r).toMatchObject({ motivo: 'SIN_MAPEO', contexto: { concepto_faltante: 'IVA_NO_DEDUCIBLE' } });
    expect(r.descripcion).toMatch(/IVA_NO_DEDUCIBLE/);
    const lote = await service.diagnosticarLote('emp', 'tes_movimientos', ['m1']);
    expect(lote[0]).toMatchObject({ id: 'm1', motivo: 'SIN_MAPEO' });
  });

  it('medio de pago con cuenta que no sirve: CUENTA_OLD con el detalle (medio y cuenta)', async () => {
    const detalle = 'la cuenta 1.01.02 del medio Transferencia (al cobrar) está desactivada; elegí otra en Contabilidad › Mapeo de cuentas › Cuentas por medio de pago';
    const constancias = [{ entity_id: 'm1', new_value: { origen_tipo: 'tes_movimientos', conceptos: ['MEDIO_PAGO_COBRO'], motivo: 'CUENTA_INUTILIZABLE', detalles: { MEDIO_PAGO_COBRO: detalle } } }];
    const roto = { medio_pago_id: 'mp', flujo: 'COBRO', cuenta_contable_id: 'c', medio_pago: { codigo: 5, descripcion: 'Transferencia' },
      cuenta: { codigo: '1.01.02', descripcion: 'Banco viejo', active: false, acepta_movimientos: true } };
    const service = crear(null, constancias, [], [roto]);
    jest.spyOn(service as any, 'diagnosticoEspecifico').mockResolvedValue(null);
    (service as any).periodos = { resolverPeriodoPorFecha: jest.fn().mockResolvedValue('p') };
    const r = await service.diagnosticarRegistro('emp', 'tes_movimientos', 'm1');
    expect(r).toMatchObject({ motivo: 'CUENTA_OLD', contexto: { concepto: 'MEDIO_PAGO_COBRO', detalle } });
    expect(r.descripcion).toContain('La cuenta 1.01.02 del medio Transferencia');
  });

  it('una constancia vieja no pisa un período cerrado', async () => {
    const constancias = [{ entity_id: 'm1', new_value: { origen_tipo: 'tes_movimientos', conceptos: ['IVA_NO_DEDUCIBLE'], motivo: 'SIN_MAPEO' } }];
    const service = crear(null, constancias);
    jest.spyOn(service as any, 'diagnosticoEspecifico').mockResolvedValue(null);
    (service as any).periodos = { resolverPeriodoPorFecha: jest.fn().mockRejectedValue(new Error('cerrado')) };
    expect((await service.diagnosticarRegistro('emp', 'tes_movimientos', 'm1')).motivo).toBe('PERIODO_CERRADO');
  });
});
