import { describe, it, expect, vi, afterEach } from 'vitest';
import { sparkcontrol } from '@/lib/ai/providers/sparkcontrol';
import type { GenerateChunk } from '@/lib/ai/types';

/**
 * SparkControl provider: OpenAI-style streaming over the internal same-box
 * URL, with NO API key (and therefore no Authorization header).
 */

function sse(frames: string[]): string {
  return frames.map((f) => `data: ${f}\n\n`).join('');
}

async function collect(
  gen: AsyncIterable<GenerateChunk>,
): Promise<GenerateChunk[]> {
  const out: GenerateChunk[] = [];
  for await (const c of gen) out.push(c);
  return out;
}

afterEach(() => {
  vi.unstubAllGlobals();
});

describe('sparkcontrol provider', () => {
  it('errors when no base URL is configured', async () => {
    const chunks = await collect(
      sparkcontrol.generate({
        model: 'whatever',
        systemPrompt: 'sys',
        userPrompt: 'hi',
      }),
    );
    expect(chunks[0]).toEqual({
      type: 'error',
      message: expect.stringContaining('Base URL is required'),
    });
  });

  it('streams deltas + usage and sends NO Authorization header (keyless)', async () => {
    const fetchMock = vi.fn(
      // Typed params so mock.calls[0] is a [url, init] tuple, not [].
      async (_url: string, _init: RequestInit) =>
        new Response(
          sse([
            '{"choices":[{"delta":{"content":"Hello"}}]}',
            '{"choices":[{"delta":{"content":" there"}}]}',
            '{"usage":{"prompt_tokens":5,"completion_tokens":2}}',
            '[DONE]',
          ]),
          { status: 200, headers: { 'content-type': 'text/event-stream' } },
        ),
    );
    vi.stubGlobal('fetch', fetchMock);

    const chunks = await collect(
      sparkcontrol.generate({
        // Loopback literal → passes the SSRF guard without DNS.
        baseUrl: 'http://127.0.0.1:9999/v1',
        model: 'RedHatAI/Qwen3.6-35B-A3B-NVFP4',
        systemPrompt: 'sys',
        userPrompt: 'hi',
      }),
    );

    // POSTs to {baseUrl}/chat/completions.
    expect(fetchMock).toHaveBeenCalledTimes(1);
    const [url, init] = fetchMock.mock.calls[0];
    expect(url).toBe('http://127.0.0.1:9999/v1/chat/completions');

    // Keyless: no Authorization header at all.
    const headers = init.headers as Record<string, string>;
    expect('authorization' in headers).toBe(false);
    expect(headers.authorization).toBeUndefined();

    // Streamed text + usage made it through.
    const text = chunks
      .filter((c): c is Extract<GenerateChunk, { type: 'text' }> => c.type === 'text')
      .map((c) => c.delta)
      .join('');
    expect(text).toBe('Hello there');
    expect(chunks).toContainEqual({ type: 'usage', tokensIn: 5, tokensOut: 2 });
    expect(chunks.some((c) => c.type === 'done')).toBe(true);
  });

  it('blocks an SSRF-unsafe base URL (cloud metadata)', async () => {
    const chunks = await collect(
      sparkcontrol.generate({
        baseUrl: 'http://169.254.169.254/v1',
        model: 'x',
        systemPrompt: 'sys',
        userPrompt: 'hi',
      }),
    );
    expect(chunks[0].type).toBe('error');
    expect((chunks[0] as { message: string }).message).toContain('SparkControl');
  });
});
