import { describe, expect, it, vi } from 'vitest';
import { createKtxEmbeddingProvider } from '../../src/llm/embedding-provider.js';
import type { KtxEmbeddingConfig } from '../../src/llm/types.js';

describe('createKtxEmbeddingProvider', () => {
  it('rejects deterministic embeddings', () => {
    const config = JSON.parse(
      JSON.stringify({
        backend: 'deterministic',
        model: 'sha256',
        dimensions: 6,
      }),
    ) as KtxEmbeddingConfig;

    expect(() => createKtxEmbeddingProvider(config)).toThrow('Unsupported KTX embedding backend: deterministic');
  });

  it('rejects gateway embeddings', () => {
    const config = JSON.parse(
      JSON.stringify({
        backend: 'gateway',
        model: 'provider/text-embedding',
        dimensions: 2,
        gateway: { apiKey: 'gateway-key' }, // pragma: allowlist secret
      }),
    ) as KtxEmbeddingConfig;

    expect(() => createKtxEmbeddingProvider(config)).toThrow('Unsupported KTX embedding backend: gateway');
  });

  it('uses OpenAI embeddings with configured dimensions', async () => {
    const createOpenAIClient = vi.fn(() => ({
      embeddings: {
        create: vi.fn().mockResolvedValue({
          data: [{ index: 0, embedding: [0.1, 0.2] }],
          usage: { total_tokens: 7 },
        }),
      },
    }));

    const provider = createKtxEmbeddingProvider(
      {
        backend: 'openai',
        model: 'text-embedding-3-small',
        dimensions: 2,
        openai: { apiKey: 'openai-key', baseURL: 'https://openai.test/v1' }, // pragma: allowlist secret
      },
      { createOpenAIClient },
    );

    await expect(provider.embed('hello')).resolves.toEqual([0.1, 0.2]);
    expect(createOpenAIClient).toHaveBeenCalledWith({
      apiKey: 'openai-key', // pragma: allowlist secret
      baseURL: 'https://openai.test/v1',
    });
  });

  it('supports sentence-transformers pathPrefix defaults and explicit empty prefix', async () => {
    const fetch = vi
      .fn()
      .mockResolvedValueOnce(new Response(JSON.stringify({ embedding: [0.1, 0.2] }), { status: 200 }))
      .mockResolvedValueOnce(new Response(JSON.stringify({ embedding: [0.3, 0.4] }), { status: 200 }));

    const provider = createKtxEmbeddingProvider(
      {
        backend: 'sentence-transformers',
        model: 'all-MiniLM-L6-v2',
        dimensions: 2,
        sentenceTransformers: { baseURL: 'https://python.test/' },
      },
      { fetch },
    );

    await expect(provider.embed('hello')).resolves.toEqual([0.3, 0.4]);
    expect(fetch).toHaveBeenNthCalledWith(
      1,
      'https://python.test/api/embeddings/compute',
      expect.objectContaining({ method: 'POST' }),
    );
    expect(fetch).toHaveBeenNthCalledWith(
      2,
      'https://python.test/api/embeddings/compute',
      expect.objectContaining({ method: 'POST' }),
    );

    const daemonFetch = vi
      .fn()
      .mockResolvedValueOnce(new Response(JSON.stringify({ embedding: [0.1, 0.2] }), { status: 200 }))
      .mockResolvedValueOnce(new Response(JSON.stringify({ embeddings: [[0.5, 0.6]] }), { status: 200 }));

    const daemonProvider = createKtxEmbeddingProvider(
      {
        backend: 'sentence-transformers',
        model: 'all-MiniLM-L6-v2',
        dimensions: 2,
        sentenceTransformers: { baseURL: 'https://daemon.test/base/', pathPrefix: '' },
      },
      { fetch: daemonFetch },
    );

    await expect(daemonProvider.embedMany(['hello'])).resolves.toEqual([[0.5, 0.6]]);
    expect(daemonFetch).toHaveBeenNthCalledWith(
      1,
      'https://daemon.test/base/embeddings/compute',
      expect.objectContaining({ method: 'POST' }),
    );
    expect(daemonFetch).toHaveBeenNthCalledWith(
      2,
      'https://daemon.test/base/embeddings/compute-bulk',
      expect.objectContaining({ method: 'POST' }),
    );
  });

  it('reports local HTTP daemon failures without a ktx-daemon spawn fallback cascade', async () => {
    const fetch = vi
      .fn()
      .mockResolvedValue(
        new Response('Embedding compute failed: httpx.InvalidURL: Invalid port', { status: 500 }),
      );

    const provider = createKtxEmbeddingProvider(
      {
        backend: 'sentence-transformers',
        model: 'all-MiniLM-L6-v2',
        dimensions: 2,
        sentenceTransformers: { baseURL: 'http://127.0.0.1:8765', pathPrefix: '' },
      },
      { fetch },
    );

    await expect(provider.embed('hello')).rejects.toThrow(
      'Embedding provider sentence-transformers request failed with HTTP 500: Embedding compute failed: httpx.InvalidURL: Invalid port',
    );
    await expect(provider.embed('hello')).rejects.not.toThrow('ktx-daemon fallback failed');
    expect(fetch).toHaveBeenCalledTimes(1);
  });
});
