import { AsientosService } from './asientos.service';

/**
 * La traza de la línea (concepto, regla, nivel) viaja con el asiento: se graba al
 * crearlo y la reversión espejo la copia tal cual, sin re-resolver ninguna cuenta.
 */
describe('Traza de mapeo por línea del asiento', () => {
  const armarAsientosService = () => {
    const create = jest.fn().mockResolvedValue({ id: 'as-1', numero: 1 });
    const service = Object.create(AsientosService.prototype) as any;
    service.prisma = {
      cont_asientos: { findFirst: jest.fn().mockResolvedValue(null), create },
    };
    service.tipoCambioService = { convertirAPYG: jest.fn().mockImplementation((_e: string, m: number) => m) };
    return { service, create };
  };

  const armarRevertir = (original: any) => {
    const txCreate = jest.fn().mockResolvedValue({ id: 'as-rev' });
    const tx = {
      cont_asientos: { findFirst: jest.fn().mockResolvedValue(null), create: txCreate, update: jest.fn() },
      cont_documentos: { create: jest.fn().mockResolvedValue({ id: 'doc-rev' }), update: jest.fn() },
    };
    const service = Object.create(AsientosService.prototype) as any;
    service.prisma = {
      cont_asientos: { findFirst: jest.fn().mockResolvedValue(original), findUnique: jest.fn().mockResolvedValue(null) },
      cont_periodos: { findUnique: jest.fn().mockResolvedValue({ estado: 'ABIERTO', mes: 10, anio: 2026 }) },
      $transaction: jest.fn().mockImplementation((fn: any) => fn(tx)),
    };
    service.audit = { log: jest.fn() };
    return { service, txCreate };
  };

  it('crearConfirmado graba la traza de cada línea', async () => {
    const { service, create } = armarAsientosService();
    await service.crearConfirmado('emp', null, 'doc', {
      periodo_id: 'p',
      fecha: '2026-10-13',
      lineas: [
        { cuenta_id: 'c1', debe_moneda: 100, haber_moneda: 0, mapeo_concepto: 'CLIENTES', mapeo_nivel: 'DEFECTO' },
        { cuenta_id: 'c2', debe_moneda: 0, haber_moneda: 100, mapeo_concepto: 'VENTAS_10', mapeo_nivel: 'REGLA', mapeo_regla_id: 'r1' },
      ],
    } as any);
    const lineas = create.mock.calls[0][0].data.lineas.create;
    expect(lineas[0]).toMatchObject({ mapeo_concepto: 'CLIENTES', mapeo_nivel: 'DEFECTO', mapeo_regla_id: null });
    expect(lineas[1]).toMatchObject({ mapeo_concepto: 'VENTAS_10', mapeo_nivel: 'REGLA', mapeo_regla_id: 'r1' });
  });

  it('[RF-5] la reversión espejo copia cuentas y traza del original', async () => {
    const original = {
      id: 'a1', empresa_id: 'emp', documento_id: 'd1', documento: { origen_tipo: 'VENTA', numero_documento: '1' },
      numero: 7, glosa: 'Venta', estado: 'CONFIRMADO', moneda_origen: 'PYG',
      tipo_cambio_id: null, total_debe_pyg: 100, total_haber_pyg: 100, fecha: new Date('2026-10-13'), periodo_id: 'p',
      lineas: [
        { cuenta_id: 'c1', centro_costo_id: null, descripcion: 'Clientes', debe_moneda: 100, haber_moneda: 0, debe_pyg: 100, haber_pyg: 0,
          mapeo_concepto: 'CLIENTES', mapeo_regla_id: null, mapeo_nivel: 'DEFECTO' },
        { cuenta_id: 'c2', centro_costo_id: null, descripcion: 'Ventas 10%', debe_moneda: 0, haber_moneda: 100, debe_pyg: 0, haber_pyg: 100,
          mapeo_concepto: 'VENTAS_10', mapeo_regla_id: 'r1', mapeo_nivel: 'REGLA' },
      ],
    };
    const { service, txCreate } = armarRevertir(original);
    await service.revertir('a1', 'emp', 'u1');
    const espejo = txCreate.mock.calls[0][0].data.lineas.create;
    expect(espejo[0]).toMatchObject({ cuenta_id: 'c1', debe_moneda: 0, haber_moneda: 100, mapeo_concepto: 'CLIENTES', mapeo_nivel: 'DEFECTO' });
    expect(espejo[1]).toMatchObject({ cuenta_id: 'c2', debe_moneda: 100, haber_moneda: 0, mapeo_regla_id: 'r1', mapeo_nivel: 'REGLA' });
  });

  describe('getById: "¿Por qué esta cuenta?" (spec §6.4)', () => {
    const armarDetalle = (asiento: any) => {
      const service = Object.create(AsientosService.prototype) as any;
      service.prisma = {
        cont_asientos: { findFirst: jest.fn().mockResolvedValue(asiento) },
        empresas_sucursales: { findMany: jest.fn().mockResolvedValue([{ id: 'suc-cde', descripcion: 'Ciudad del Este' }]) },
        categorias: { findMany: jest.fn().mockResolvedValue([]) },
        rubros: { findMany: jest.fn().mockResolvedValue([]) },
      };
      return service;
    };

    it('cada línea trae concepto, nivel y la regla con su frase y quién la activó; filtra por empresa', async () => {
      const activadoAt = new Date('2026-10-10T12:00:00Z');
      const service = armarDetalle({
        id: 'a1',
        lineas: [
          {
            id: 'l1', cuenta_id: 'c-ext', mapeo_concepto: 'VENTAS_10', mapeo_nivel: 'REGLA', mapeo_regla_id: 'r1',
            mapeo_regla: {
              id: 'r1', concepto: 'VENTAS_10', orden: 2, modo: 'CUENTA', cuenta_id: 'c-ext', estado: 'ACTIVA',
              vigente_desde: new Date('2026-10-01'), vigente_hasta: null,
              condiciones: [{ dimension: 'SUCURSAL', operador: 'EN', valores: ['suc-cde'] }],
              activado_por: 'u1', activado_at: activadoAt,
              cuenta: { codigo: '4.01.02', descripcion: 'VENTAS EXTERIOR' },
              activador: { nombres: 'Ana', apellidos: 'Paz' },
            },
          },
          { id: 'l2', cuenta_id: 'caja', mapeo_concepto: null, mapeo_nivel: null, mapeo_regla_id: null, mapeo_regla: null },
        ],
      });

      const r = await service.getById('a1', 'emp');

      const args = service.prisma.cont_asientos.findFirst.mock.calls[0][0];
      expect(args.where).toEqual({ id: 'a1', empresa_id: 'emp' });
      expect(args.include.lineas.include.mapeo_regla).toBeTruthy();
      expect(service.prisma.empresas_sucursales.findMany.mock.calls[0][0].where).toMatchObject({ empresa_id: 'emp' });
      expect(r.lineas[0]).toMatchObject({ mapeo_concepto: 'VENTAS_10', mapeo_nivel: 'REGLA' });
      expect(r.lineas[0].mapeo_regla).toEqual({
        orden: 2,
        estado: 'ACTIVA',
        condiciones: [{ dimension: 'SUCURSAL', operador: 'EN', valores: ['suc-cde'] }],
        frase: 'Si sucursal es Ciudad del Este → 4.01.02 VENTAS EXTERIOR',
        activado_por: 'u1',
        activado_por_nombre: 'Ana Paz',
        activado_at: activadoAt,
      });
      // Anterior a la traza: todo null, sin inventar nada.
      expect(r.lineas[1]).toMatchObject({ mapeo_nivel: null, mapeo_regla: null });
    });

    it('asiento de otra empresa → 404', async () => {
      const service = armarDetalle(null);
      await expect(service.getById('a1', 'otra')).rejects.toThrow('Asiento no encontrado');
    });
  });
});
