import { describe, expect, it } from 'vitest';
import { buildIndex, retrieveTopK, tokenize } from '../retrieval';

const sampleTopics = [
  { id: 'software-engineer', label: 'Software Engineer', description: 'Bouwt en onderhoudt applicaties; werkt in agile teams.' },
  { id: 'onboarding-buddy', label: 'Onboarding Buddy', description: 'Begeleidt nieuwe medewerkers in hun eerste weken.' },
  { id: 'kennisbeheer', label: 'Kennisbeheer', description: 'Het proces van het vastleggen en ontsluiten van organisatiekennis.' },
  { id: 'wekelijkse-sessie', label: 'Wekelijkse Leersessie', description: 'Microlearning sessie waarin medewerkers wekelijks leren via AI-gegenereerde quizzen.' },
];

describe('tokenize', () => {
  it('lowercases and splits on non-alphanumeric', () => {
    expect(tokenize('Hello, World!')).toEqual(['hello', 'world']);
  });

  it('drops stopwords and short tokens', () => {
    expect(tokenize('de software engineer is hier')).toEqual(['software', 'engineer', 'hier']);
  });

  it('keeps hyphenated identifiers', () => {
    expect(tokenize('software-engineer onboarding-buddy')).toEqual(['software-engineer', 'onboarding-buddy']);
  });
});

describe('buildIndex / retrieveTopK', () => {
  it('returns empty for empty topics', () => {
    const idx = buildIndex([]);
    expect(retrieveTopK(idx, 'anything')).toEqual([]);
  });

  it('returns empty for empty query', () => {
    const idx = buildIndex(sampleTopics);
    expect(retrieveTopK(idx, '')).toEqual([]);
  });

  it('ranks the most relevant topic first', () => {
    const idx = buildIndex(sampleTopics);
    const hits = retrieveTopK(idx, 'wat doet een onboarding buddy?', 2);
    expect(hits[0].id).toBe('onboarding-buddy');
  });

  it('matches on description when label does not contain query terms', () => {
    const idx = buildIndex(sampleTopics);
    const hits = retrieveTopK(idx, 'microlearning quizzen', 3);
    expect(hits.map(h => h.id)).toContain('wekelijkse-sessie');
  });

  it('returns no hits when no terms match', () => {
    const idx = buildIndex(sampleTopics);
    expect(retrieveTopK(idx, 'kwantumfysica raketten')).toEqual([]);
  });

  it('matches Dutch compound words on a shared stem', () => {
    const pensionTopics = [
      { id: 'pensioenregeling', label: 'Pensioenregeling', description: 'De beschikbare premieregeling bij a.s.r. Doen Pensioen.' },
      { id: 'partnerpensioen', label: 'Partnerpensioen', description: 'Uitkering aan de partner bij overlijden.' },
      { id: 'reiskostenvergoeding', label: 'Reiskostenvergoeding', description: 'EUR 0,23 per kilometer voor woon-werkverkeer.' },
    ];
    const idx = buildIndex(pensionTopics);
    // "pensioen" never appears as a standalone token in a label, yet the stem is
    // a prefix of "pensioenregeling" and an infix of "partnerpensioen".
    const hits = retrieveTopK(idx, 'wat dekt mijn pensioen?', 3).map(h => h.id);
    expect(hits).toContain('pensioenregeling');
    expect(hits).toContain('partnerpensioen');
    expect(hits).not.toContain('reiskostenvergoeding');
  });

  it('does not partial-match on short shared prefixes', () => {
    const topics = [
      { id: 'onderhoud', label: 'Onderhoud', description: 'Technisch beheer van systemen.' },
    ];
    const idx = buildIndex(topics);
    // "onderneming" shares only "onder" (5) with "onderhoud" — below the overlap
    // needed for a query token this size to count.
    expect(retrieveTopK(idx, 'onderneming')).toEqual([]);
  });

  it('caches the index per topics array reference', () => {
    const idx1 = buildIndex(sampleTopics);
    const idx2 = buildIndex(sampleTopics);
    expect(idx1).toBe(idx2);
  });
});
