import { describe, expect, it, vi } from 'vitest';
import { collectBestChunks } from '../../src/core/embeddings/types.js';

describe('collectBestChunks', () => {
  it('keeps fetching until enough unique nodeIds are available', async () => {
    const rows = [
      ...Array.from({ length: 6 }, (_, i) => ({
        nodeId: 'Function:a',
        chunkIndex: i,
        startLine: 10 + i,
        endLine: 11 + i,
        distance: 0.1 + i * 0.01,
      })),
      ...Array.from({ length: 6 }, (_, i) => ({
        nodeId: 'Function:b',
        chunkIndex: i,
        startLine: 20 + i,
        endLine: 21 + i,
        distance: 0.2 + i * 0.01,
      })),
      {
        nodeId: 'Function:c',
        chunkIndex: 0,
        startLine: 30,
        endLine: 31,
        distance: 0.3,
      },
      {
        nodeId: 'Function:d',
        chunkIndex: 0,
        startLine: 40,
        endLine: 41,
        distance: 0.4,
      },
    ];

    const fetchRows = vi.fn(async (fetchLimit: number) => rows.slice(0, fetchLimit));

    const bestChunks = await collectBestChunks(3, fetchRows);

    expect(fetchRows).toHaveBeenCalledTimes(2);
    expect(fetchRows).toHaveBeenNthCalledWith(1, 12);
    expect(fetchRows).toHaveBeenNthCalledWith(2, 24);
    expect(Array.from(bestChunks.keys()).slice(0, 3)).toEqual([
      'Function:a',
      'Function:b',
      'Function:c',
    ]);
  });

  it('stops at the max fetch window when unique nodes are still missing', async () => {
    const rows = [
      ...Array.from({ length: 200 }, (_, i) => ({
        nodeId: i < 120 ? 'Function:a' : 'Function:b',
        chunkIndex: i,
        startLine: i + 1,
        endLine: i + 2,
        distance: 0.01 + i * 0.001,
      })),
      {
        nodeId: 'Function:c',
        chunkIndex: 0,
        startLine: 500,
        endLine: 501,
        distance: 0.5,
      },
      {
        nodeId: 'Function:d',
        chunkIndex: 0,
        startLine: 600,
        endLine: 601,
        distance: 0.6,
      },
      {
        nodeId: 'Function:e',
        chunkIndex: 0,
        startLine: 700,
        endLine: 701,
        distance: 0.7,
      },
    ];

    const fetchRows = vi.fn(async (fetchLimit: number) => rows.slice(0, fetchLimit));

    const bestChunks = await collectBestChunks(5, fetchRows);

    expect(fetchRows).toHaveBeenCalledTimes(5);
    expect(fetchRows).toHaveBeenNthCalledWith(1, 20);
    expect(fetchRows).toHaveBeenNthCalledWith(2, 40);
    expect(fetchRows).toHaveBeenNthCalledWith(3, 80);
    expect(fetchRows).toHaveBeenNthCalledWith(4, 160);
    expect(fetchRows).toHaveBeenNthCalledWith(5, 200);
    expect(Array.from(bestChunks.keys())).toEqual(['Function:a', 'Function:b']);
  });

  it('stops when the vector search is exhausted before reaching the limit', async () => {
    const rows = [
      {
        nodeId: 'Function:a',
        chunkIndex: 0,
        startLine: 1,
        endLine: 2,
        distance: 0.1,
      },
      {
        nodeId: 'Function:a',
        chunkIndex: 1,
        startLine: 3,
        endLine: 4,
        distance: 0.2,
      },
      {
        nodeId: 'Function:b',
        chunkIndex: 0,
        startLine: 5,
        endLine: 6,
        distance: 0.3,
      },
    ];

    const fetchRows = vi.fn(async (_fetchLimit: number) => rows);

    const bestChunks = await collectBestChunks(5, fetchRows);

    expect(fetchRows).toHaveBeenCalledTimes(1);
    expect(Array.from(bestChunks.keys())).toEqual(['Function:a', 'Function:b']);
  });

  it('allows an initial large fetch but does not grow without bound', async () => {
    const rows = [
      ...Array.from({ length: 200 }, (_, i) => ({
        nodeId: `Function:${Math.floor(i / 50)}`,
        chunkIndex: i,
        startLine: i + 1,
        endLine: i + 2,
        distance: 0.01 + i * 0.001,
      })),
      ...Array.from({ length: 60 }, (_, i) => ({
        nodeId: `Function:extra-${i}`,
        chunkIndex: 0,
        startLine: 300 + i,
        endLine: 301 + i,
        distance: 1 + i * 0.001,
      })),
    ];

    const fetchRows = vi.fn(async (fetchLimit: number) => rows.slice(0, fetchLimit));

    const bestChunks = await collectBestChunks(60, fetchRows);

    expect(fetchRows).toHaveBeenCalledTimes(1);
    expect(fetchRows).toHaveBeenNthCalledWith(1, 240);
    expect(bestChunks.size).toBe(44);
  });
});
