import { describe, it, expect } from 'vitest';
import { estimateTokens } from '../src/llm/token-counter.js';

describe('estimateTokens', () => {
  it('returns 0 for empty string', () => {
    expect(estimateTokens('')).toBe(0);
  });

  it('returns 1 for strings of 1-4 characters', () => {
    expect(estimateTokens('a')).toBe(1);
    expect(estimateTokens('ab')).toBe(1);
    expect(estimateTokens('abc')).toBe(1);
    expect(estimateTokens('abcd')).toBe(1);
  });

  it('returns 2 for strings of 5-8 characters', () => {
    expect(estimateTokens('abcde')).toBe(2);
    expect(estimateTokens('abcdefgh')).toBe(2);
  });

  it('estimates roughly 4 chars per token for longer text', () => {
    const text = 'a'.repeat(1000);
    expect(estimateTokens(text)).toBe(250);
  });

  it('rounds up partial tokens', () => {
    // 7 chars / 4 = 1.75 -> ceil = 2
    expect(estimateTokens('abcdefg')).toBe(2);
    // 9 chars / 4 = 2.25 -> ceil = 3
    expect(estimateTokens('abcdefghi')).toBe(3);
  });

  it('handles JSON payloads', () => {
    const json = JSON.stringify({ key: 'value', nested: { a: 1, b: [1, 2, 3] } });
    const expected = Math.ceil(json.length / 4);
    expect(estimateTokens(json)).toBe(expected);
  });

  it('handles unicode text', () => {
    // Note: estimation is by string length (code units), not bytes
    const text = '\u{1F600}'.repeat(10); // emoji
    const expected = Math.ceil(text.length / 4);
    expect(estimateTokens(text)).toBe(expected);
  });
});
