const { test } = require('node:test');
const assert = require('node:assert/strict');

const ENV_KEYS = ['TTS_PROVIDER', 'LITELLM_API_BASE', 'LITELLM_API_KEY', 'LITELLM_MASTER_KEY', 'LITELLM_TTS_VOICES', 'LITELLM_TTS_VOICE'];

function withEnv(overrides, fn) {
  const previous = {};
  ENV_KEYS.forEach((key) => { previous[key] = process.env[key]; delete process.env[key]; });
  Object.entries(overrides).forEach(([key, value]) => { process.env[key] = value; });
  try {
    return fn();
  } finally {
    ENV_KEYS.forEach((key) => {
      if (previous[key] === undefined) delete process.env[key];
      else process.env[key] = previous[key];
    });
  }
}

test('TTS provider defaults to LiteLLM when gateway is configured', () => {
  const ttsProvider = require('../src/utils/ttsProvider');
  withEnv({ LITELLM_API_BASE: 'https://llm.example.com' }, () => {
    assert.equal(ttsProvider.getTTSEnvProvider(), 'auto');
    assert.equal(ttsProvider.getTTSProvider(), 'litellm');
  });
});

test('TTS provider ignores non-LiteLLM provider overrides', () => {
  const ttsProvider = require('../src/utils/ttsProvider');
  withEnv({ TTS_PROVIDER: 'elevenlabs', LITELLM_API_BASE: 'https://llm.example.com' }, () => {
    assert.equal(ttsProvider.getTTSEnvProvider(), 'elevenlabs');
    assert.equal(ttsProvider.getTTSProvider(), 'litellm');
  });
});

test('LiteLLM TTS voice list comes from configured voice catalog', () => {
  const ttsProvider = require('../src/utils/ttsProvider');
  withEnv({ LITELLM_TTS_VOICES: 'sherpa/kokoro:am_adam, sherpa/kokoro:af_bella' }, () => {
    const voices = ttsProvider.getTTSVoiceLists().litellm;
    assert.equal(voices[0], 'sherpa/kokoro:am_adam');
    assert.equal(voices[1], 'sherpa/kokoro:af_bella');
    assert.equal(voices.includes('Jasper'), true);
    assert.equal(voices.includes('F1'), true);
  });
});

test('TTS model discovery uses audio_speech metadata only', () => {
  const ttsProvider = require('../src/utils/ttsProvider');
  assert.equal(ttsProvider.isOpenAITTSModel, undefined);
  assert.equal(ttsProvider.isLiteLLMTTSModel({ id: 'local-chatterbox-turbo' }), false);
  assert.equal(ttsProvider.isLiteLLMTTSModel({ id: 'local-kokoro-tts' }), false);
  assert.equal(ttsProvider.isLiteLLMTTSModel({ id: 'openai-tts-1' }), false);
  assert.equal(ttsProvider.isLiteLLMTTSModel({ id: 'openai-gpt-4o-mini-tts' }), false);
  assert.equal(ttsProvider.isLiteLLMTTSModel({ id: 'mistral-voxtral-mini-tts' }), false);
  assert.equal(ttsProvider.isLiteLLMTTSModel({ id: 'vertex-gemini-2.5-flash-tts' }), false);
  assert.equal(ttsProvider.isLiteLLMTTSModel({ model_name: 'local-chatterbox-turbo', model_info: { mode: 'audio_speech' } }), true);
  assert.equal(ttsProvider.isLiteLLMTTSModel({ id: 'custom-provider-model', model_info: { mode: 'audio_speech' } }), true);
  assert.equal(ttsProvider.isLiteLLMTTSModel({ id: 'local-parakeet-v3', model_info: { mode: 'audio_transcription' } }), false);
});

test('LiteLLM TTS model extraction filters gateway model objects', () => {
  const ttsProvider = require('../src/utils/ttsProvider');
  assert.deepEqual(ttsProvider.getLiteLLMTTSModels([
    { id: 'openai-tts-1' },
    { id: 'mistral-voxtral-mini-tts' },
    { id: 'openai-gpt-4o-mini-tts' },
    { id: 'local-chatterbox-turbo' },
    { model_name: 'local-chatterbox-turbo', model_info: { mode: 'audio_speech' } },
    { id: 'custom-provider-model', model_info: { mode: 'audio_speech' } },
    { id: 'local-parakeet-v3', model_info: { mode: 'audio_transcription' } },
    { id: 'vertex-gemini-2.5-flash-tts' },
    { id: 'gpt-4.1' }
  ]), ['local-chatterbox-turbo', 'custom-provider-model']);
});

test('LiteLLM TTS discovery includes metadata models and configured fallbacks', () => {
  const ttsProvider = require('../src/utils/ttsProvider');
  withEnv({ LITELLM_TTS_VOICES: 'sherpa/kokoro:am_adam,sherpa/kokoro:af_bella' }, () => {
    const items = ttsProvider.getLiteLLMTTSDiscoveryItems([
      { model_name: 'local-kokoro-tts', model_info: { mode: 'audio_speech' } },
      { model_name: 'not-tts-by-name-only' },
      { model_name: 'local-parakeet-v3', model_info: { mode: 'audio_transcription' } }
    ], {
      currentModel: 'local-kokoro-tts',
      currentVoice: 'sherpa/kokoro:am_adam'
    });
    assert.deepEqual(items.slice(0, 3), [
      { id: 'local-kokoro-tts', name: 'local-kokoro-tts', source: 'gateway-api', kind: 'model' },
      { id: 'sherpa/kokoro:am_adam', name: 'sherpa/kokoro:am_adam', source: 'configured-voice', kind: 'voice' },
      { id: 'sherpa/kokoro:af_bella', name: 'sherpa/kokoro:af_bella', source: 'configured-voice-list', kind: 'voice' }
    ]);
    assert.equal(items.some(function(item) { return item.id === 'not-tts-by-name-only'; }), false);
  });
});

test('LiteLLM TTS discovery expands Kitten and Supertonic voices', () => {
  const ttsProvider = require('../src/utils/ttsProvider');
  withEnv({}, () => {
    const items = ttsProvider.getLiteLLMTTSDiscoveryItems([
      { model_name: 'local-kitten-tts', model_info: { mode: 'audio_speech' } },
      { model_name: 'local-supertonic-tts', model_info: { mode: 'audio_speech' } }
    ], {});
    assert.equal(items.some(function(item) { return item.id === 'local-kitten-tts' && item.kind === 'model'; }), true);
    assert.equal(items.some(function(item) { return item.id === 'Jasper' && item.kind === 'voice'; }), true);
    assert.equal(items.some(function(item) { return item.id === 'F1' && item.kind === 'voice'; }), true);
  });
});

test('LiteLLM TTS discovery still shows configured model if metadata lookup fails', () => {
  const ttsProvider = require('../src/utils/ttsProvider');
  assert.deepEqual(ttsProvider.getLiteLLMTTSDiscoveryItems([], {
    currentModel: 'local-kokoro-tts',
    currentVoice: 'sherpa/kokoro:am_adam'
  }).slice(0, 2), [
    { id: 'local-kokoro-tts', name: 'local-kokoro-tts', source: 'configured-model', kind: 'model' },
    { id: 'sherpa/kokoro:am_adam', name: 'sherpa/kokoro:am_adam', source: 'configured-voice', kind: 'voice' }
  ]);
});

test('LiteLLM TTS voices are scoped to the active local model', () => {
  const ttsProvider = require('../src/utils/ttsProvider');
  withEnv({ LITELLM_TTS_VOICES: 'sherpa/kokoro:am_adam,sherpa/kokoro:af_bella', LITELLM_TTS_VOICE: 'sherpa/kokoro:am_adam' }, () => {
    assert.deepEqual(ttsProvider.getLiteLLMTTSVoicesForModel('local-kitten-tts'), ['Bella', 'Jasper', 'Luna', 'Bruno', 'Rosie', 'Hugo', 'Kiki', 'Leo']);
    assert.deepEqual(ttsProvider.getLiteLLMTTSVoicesForModel('local-supertonic-tts'), ['F1', 'F2', 'F3', 'F4', 'F5', 'M1', 'M2', 'M3', 'M4', 'M5']);
    assert.deepEqual(ttsProvider.getLiteLLMTTSVoicesForModel('local-kokoro-tts'), ['sherpa/kokoro:am_adam', 'sherpa/kokoro:af_bella']);
    assert.deepEqual(ttsProvider.getLiteLLMTTSVoicesForModel('groq-orpheus-english'), ['autumn', 'diana', 'hannah', 'austin', 'daniel', 'troy']);
    assert.deepEqual(ttsProvider.getLiteLLMTTSVoicesForModel('canopylabs/orpheus-arabic-saudi'), ['abdullah', 'fahad', 'sultan', 'lulwa', 'noura', 'aisha']);
  });
});

test('LiteLLM TTS compatibility rejects cross-model local voices', () => {
  const ttsProvider = require('../src/utils/ttsProvider');
  assert.equal(ttsProvider.isLiteLLMTTSVoiceCompatible('local-kokoro-tts', 'Bella'), false);
  assert.equal(ttsProvider.isLiteLLMTTSVoiceCompatible('local-kokoro-tts', 'M1'), false);
  assert.equal(ttsProvider.isLiteLLMTTSVoiceCompatible('local-kitten-tts', 'Bella'), true);
  assert.equal(ttsProvider.isLiteLLMTTSVoiceCompatible('local-kitten-tts', 'M1'), false);
  assert.equal(ttsProvider.isLiteLLMTTSVoiceCompatible('local-supertonic-tts', 'M1'), true);
  assert.equal(ttsProvider.isLiteLLMTTSVoiceCompatible('local-supertonic-tts', 'Bella'), false);
  assert.equal(ttsProvider.isLiteLLMTTSVoiceCompatible('groq-orpheus-english', 'hannah'), true);
  assert.equal(ttsProvider.isLiteLLMTTSVoiceCompatible('groq-orpheus-english', 'sherpa/kokoro:af_bella'), false);
  assert.equal(ttsProvider.isLiteLLMTTSVoiceCompatible('groq-orpheus-arabic-saudi', 'aisha'), true);
  assert.equal(ttsProvider.isLiteLLMTTSVoiceCompatible('groq-orpheus-arabic-saudi', 'troy'), false);
});

test('Groq Orpheus TTS requests force wav response format', () => {
  const ttsProvider = require('../src/utils/ttsProvider');
  assert.deepEqual(ttsProvider.getLiteLLMTTSRequestOptions('groq-orpheus-english'), { response_format: 'wav' });
  assert.deepEqual(ttsProvider.getLiteLLMTTSRequestOptions('canopylabs/orpheus-arabic-saudi'), { response_format: 'wav' });
  assert.deepEqual(ttsProvider.getLiteLLMTTSRequestOptions('local-kokoro-tts'), {});
});

test('LiteLLM headers include optional content type without exposing key value', () => {
  const ttsProvider = require('../src/utils/ttsProvider');
  withEnv({ LITELLM_API_KEY: 'secret-token' }, () => {
    assert.deepEqual(ttsProvider.getLiteLLMHeaders('application/json'), {
      'Content-Type': 'application/json',
      Authorization: 'Bearer secret-token'
    });
  });
});

test('LiteLLM admin headers prefer master key for metadata routes', () => {
  const { getLiteLLMHeaders, getLiteLLMAdminHeaders } = require('../src/utils/litellm');
  withEnv({ LITELLM_API_KEY: 'virtual-token', LITELLM_MASTER_KEY: 'master-token' }, () => {
    assert.equal(getLiteLLMHeaders().Authorization, 'Bearer virtual-token');
    assert.equal(getLiteLLMAdminHeaders().Authorization, 'Bearer master-token');
  });
});
