import { describe, it, expect } from 'vitest';
import { findPrice, estimateCost, formatCost } from '@/lib/ai/pricing';

describe('findPrice', () => {
  it('matches a known model exactly', () => {
    expect(findPrice('claude-sonnet-4-5')).toBeTruthy();
  });

  it('matches a known model with a date suffix (longest-prefix)', () => {
    const p = findPrice('claude-sonnet-4-5-20251022');
    expect(p?.inputPerM).toBe(3);
    expect(p?.outputPerM).toBe(15);
  });

  it('is case-insensitive', () => {
    expect(findPrice('GPT-5-Mini')).toBeTruthy();
  });

  it('returns null for unknown models', () => {
    expect(findPrice('mistral-medium-9000')).toBeNull();
  });

  it('prefers longer-prefix when multiple keys match', () => {
    // claude-sonnet-4-5 is more specific than claude-sonnet-4
    const p = findPrice('claude-sonnet-4-5');
    expect(p).toEqual({ inputPerM: 3, outputPerM: 15 });
  });
});

describe('estimateCost', () => {
  it('returns 0 for ollama (self-hosted)', () => {
    expect(
      estimateCost({
        provider: 'ollama',
        model: 'llama3.1:8b',
        tokensIn: 1000,
        tokensOut: 500,
      }),
    ).toBe(0);
  });

  it('returns null for openai-compatible (unknown gateway pricing)', () => {
    expect(
      estimateCost({
        provider: 'openai-compatible',
        model: 'meta-llama/llama-3.1-8b-instruct',
        tokensIn: 1000,
        tokensOut: 500,
      }),
    ).toBeNull();
  });

  it('returns null when the model isn\'t in the price table', () => {
    expect(
      estimateCost({
        provider: 'claude',
        model: 'claude-vintage-edition',
        tokensIn: 1000,
        tokensOut: 500,
      }),
    ).toBeNull();
  });

  it('returns null when token counts are missing', () => {
    expect(
      estimateCost({
        provider: 'claude',
        model: 'claude-sonnet-4-5',
        tokensIn: null,
        tokensOut: 500,
      }),
    ).toBeNull();
  });

  it('computes the right $ for a known model', () => {
    // claude-sonnet-4-5: $3/M in, $15/M out
    // 100K in + 50K out = 0.1*3 + 0.05*15 = 0.3 + 0.75 = 1.05
    const cost = estimateCost({
      provider: 'claude',
      model: 'claude-sonnet-4-5',
      tokensIn: 100_000,
      tokensOut: 50_000,
    });
    expect(cost).toBeCloseTo(1.05, 5);
  });

  it('computes correctly for gpt-5-nano (very cheap)', () => {
    // gpt-5-nano: $0.05/M in, $0.4/M out
    // 1000 in + 500 out = 0.001*0.05 + 0.0005*0.4 = 0.00005 + 0.0002 = 0.00025
    const cost = estimateCost({
      provider: 'openai',
      model: 'gpt-5-nano',
      tokensIn: 1000,
      tokensOut: 500,
    });
    expect(cost).toBeCloseTo(0.00025, 8);
  });
});

describe('formatCost', () => {
  it('formats null as em dash', () => {
    expect(formatCost(null)).toBe('—');
  });
  it('formats 0 as "free"', () => {
    expect(formatCost(0)).toBe('free');
  });
  it('formats sub-cent costs as "<$0.01"', () => {
    expect(formatCost(0.0023)).toBe('<$0.01');
  });
  it('formats sub-dollar costs with 3 decimal places', () => {
    expect(formatCost(0.123)).toBe('$0.123');
  });
  it('formats dollar+ costs with 2 decimal places', () => {
    expect(formatCost(2.567)).toBe('$2.57');
  });
});
