import { afterEach, describe, expect, it, vi } from 'vitest';

import { synthesizeSpeechAttachment } from './tts.js';

describe('synthesizeSpeechAttachment', () => {
  afterEach(() => {
    vi.restoreAllMocks();
  });

  it('returns null when text is empty', async () => {
    const result = await synthesizeSpeechAttachment('   ', {
      endpoint: 'https://example.com/v1/audio/speech',
    });
    expect(result).toBeNull();
  });

  it('returns null when endpoint is missing', async () => {
    const result = await synthesizeSpeechAttachment('hello', {});
    expect(result).toBeNull();
  });

  it('returns an outbound audio attachment on success', async () => {
    const fetchSpy = vi.spyOn(globalThis, 'fetch').mockResolvedValue({
      ok: true,
      status: 200,
      statusText: 'OK',
      arrayBuffer: async () => Uint8Array.from([1, 2, 3, 4]).buffer,
    } as Response);

    const result = await synthesizeSpeechAttachment('Hello from Flynn', {
      endpoint: 'https://example.com/v1/audio/speech',
      apiKey: 'sk-test',
      model: 'gpt-4o-mini-tts',
      voice: 'alloy',
      format: 'mp3',
    });

    expect(fetchSpy).toHaveBeenCalledWith(
      'https://example.com/v1/audio/speech',
      expect.objectContaining({
        method: 'POST',
        headers: expect.objectContaining({
          'Content-Type': 'application/json',
          Authorization: 'Bearer sk-test',
        }),
      }),
    );
    expect(result).toMatchObject({
      mimeType: 'audio/mpeg',
      data: 'AQIDBA==',
    });
    expect(result?.filename).toMatch(/^flynn-reply-\d+\.mp3$/);
  });

  it('throws when the tts endpoint returns an error', async () => {
    vi.spyOn(globalThis, 'fetch').mockResolvedValue({
      ok: false,
      status: 429,
      statusText: 'Too Many Requests',
      text: async () => 'rate limit',
    } as Response);

    await expect(synthesizeSpeechAttachment('Hello', {
      endpoint: 'https://example.com/v1/audio/speech',
    })).rejects.toThrow(/TTS request failed: 429 Too Many Requests/i);
  });
});
