import { AiUsageService } from './ai-usage.service';

const TARIFAS = [
  { proveedor: 'deepseek', modelo: 'deepseek-v4-flash', precio_in_por_1m: 0.44, precio_out_por_1m: 1.32, activo: true },
  { proveedor: 'anthropic', modelo: 'claude-haiku-4-5-20251001', precio_in_por_1m: 1, precio_out_por_1m: 5, activo: true },
];

const crear = (tarifas = TARIFAS) => {
  const prisma = {
    ai_consumo: { create: jest.fn(async ({ data }: any) => data) },
    ai_tarifas: { findMany: jest.fn(async () => tarifas) },
  };
  const svc = new AiUsageService(prisma as any);
  const warn = jest.spyOn((svc as any).logger, 'warn').mockImplementation(() => undefined);
  return { svc, prisma, warn };
};
const filaGuardada = (prisma: any) => prisma.ai_consumo.create.mock.calls.at(-1)[0].data;

const base = {
  empresa_id: 'e1',
  feature: 'IMAGENES',
  proveedor: 'deepseek',
  tokens_in: 1_000_000,
  tokens_out: 1_000_000,
};

describe('AiUsageService.registrar — costo', () => {
  it('calcula el costo cuando el modelo reportado tiene tarifa', async () => {
    const { svc, prisma } = crear();
    await svc.registrar({ ...base, modelo: 'deepseek-v4-flash' });
    expect(filaGuardada(prisma).costo_usd).toBeCloseTo(0.44 + 1.32, 8);
  });

  it('cae al modelo configurado cuando el que devuelve la API no está tarifado', async () => {
    // DeepSeek responde "deepseek-flash" aunque la empresa tenga configurado "deepseek-v4-flash".
    const { svc, prisma, warn } = crear();
    await svc.registrar({ ...base, modelo: 'deepseek-flash', modelo_configurado: 'deepseek-v4-flash' });
    const fila = filaGuardada(prisma);
    expect(fila.costo_usd).toBeCloseTo(0.44 + 1.32, 8);
    // Se guarda el modelo que respondió la API (dato real), no el configurado.
    expect(fila.modelo).toBe('deepseek-flash');
    expect(warn).not.toHaveBeenCalled();
  });

  it('sin tarifa por ningún nombre deja el costo en null y avisa una sola vez por combinación', async () => {
    const { svc, prisma, warn } = crear();
    await svc.registrar({ ...base, modelo: 'modelo-raro', modelo_configurado: 'otro-raro' });
    expect(filaGuardada(prisma).costo_usd).toBeNull();
    expect(warn).toHaveBeenCalledTimes(1);
    expect(warn.mock.calls[0][0]).toContain('modelo-raro');
    await svc.registrar({ ...base, modelo: 'modelo-raro', modelo_configurado: 'otro-raro' });
    expect(warn).toHaveBeenCalledTimes(1); // no vuelve a avisar por lo mismo
    await svc.registrar({ ...base, modelo: 'otro-distinto' });
    expect(warn).toHaveBeenCalledTimes(2); // otra combinación sí avisa
  });

  it('el fallback no aplica si el modelo configurado es el mismo o está vacío', async () => {
    const { svc, prisma, warn } = crear();
    await svc.registrar({ ...base, modelo: 'modelo-raro', modelo_configurado: 'modelo-raro' });
    expect(filaGuardada(prisma).costo_usd).toBeNull();
    expect(warn).toHaveBeenCalledTimes(1);
  });

  it('la tarifa se busca sin distinguir mayúsculas', async () => {
    const { svc, prisma } = crear();
    await svc.registrar({ ...base, proveedor: 'DeepSeek', modelo: 'DeepSeek-V4-Flash' });
    expect(filaGuardada(prisma).costo_usd).toBeCloseTo(0.44 + 1.32, 8);
  });

  it('registrar nunca lanza aunque falle el insert', async () => {
    const { svc, prisma } = crear();
    prisma.ai_consumo.create.mockRejectedValueOnce(new Error('db caída'));
    await expect(svc.registrar({ ...base, modelo: 'deepseek-v4-flash' })).resolves.toBeUndefined();
  });
});
