import { expect, tap } from '@git.zone/tstest/tapbundle';
import { MaryttsClient, MaryttsIntegration, type IMaryttsAudioResult } from '../../ts/integrations/marytts/index.js';

tap.test('posts MaryTTS /process form data and returns audio bytes', async () => {
  const originalFetch = globalThis.fetch;
  const calls: Array<{ url: string; method?: string; body: string; contentType?: string }> = [];
  globalThis.fetch = (async (inputArg: RequestInfo | URL, initArg?: RequestInit) => {
    calls.push({
      url: String(inputArg),
      method: initArg?.method,
      body: String(initArg?.body || ''),
      contentType: (initArg?.headers as Record<string, string> | undefined)?.['content-type'],
    });
    return new Response(Buffer.from([0x52, 0x49, 0x46, 0x46]), { status: 200, headers: { 'content-type': 'audio/wav' } });
  }) as typeof globalThis.fetch;

  try {
    const client = new MaryttsClient({ host: '127.0.0.1', port: 59126, effect: { Robot: 'amount:100.0;' } });
    const result = await client.speak('Hello world', { codec: 'WAVE_FILE', language: 'en_US', effects: { Volume: 'amount:2.0;' } });
    const params = new URLSearchParams(calls[0].body);

    expect(calls[0].url).toEqual('http://127.0.0.1:59126/process');
    expect(calls[0].method).toEqual('POST');
    expect(calls[0].contentType).toEqual('application/x-www-form-urlencoded');
    expect(params.get('INPUT_TEXT')).toEqual('Hello world');
    expect(params.get('OUTPUT_TYPE')).toEqual('AUDIO');
    expect(params.get('INPUT_TYPE')).toEqual('TEXT');
    expect(params.get('LOCALE')).toEqual('en_US');
    expect(params.get('AUDIO')).toEqual('WAVE_FILE');
    expect(params.get('VOICE')).toEqual('cmu-slt-hsmm');
    expect(params.get('effect_Robot_selected')).toEqual('on');
    expect(params.get('effect_Robot_parameters')).toEqual('amount:100.0;');
    expect(params.get('effect_Volume_selected')).toEqual('on');
    expect(result.format).toEqual('wav');
    expect(result.contentType).toEqual('audio/wav');
    expect(Buffer.isBuffer(result.audio)).toBeTrue();
    expect(result.audio.toString('ascii')).toEqual('RIFF');
  } finally {
    globalThis.fetch = originalFetch;
  }
});

tap.test('routes tts.speak through the MaryTTS runtime and reports HTTP failures', async () => {
  const originalFetch = globalThis.fetch;
  const calls: string[] = [];
  globalThis.fetch = (async (inputArg: RequestInfo | URL, initArg?: RequestInit) => {
    calls.push(`${initArg?.method || 'GET'} ${String(inputArg)}`);
    if (String(inputArg).endsWith('/process')) {
      return new Response(Buffer.from([1, 2, 3]), { status: 200, headers: { 'content-type': 'audio/aiff' } });
    }
    return new Response('MaryTTS demo', { status: 200 });
  }) as typeof globalThis.fetch;

  try {
    const runtime = await new MaryttsIntegration().setup({ url: 'http://mary.test/base', codec: 'AIFF_FILE' }, {});
    const result = await runtime.callService?.({
      domain: 'tts',
      service: 'speak',
      target: {},
      data: { message: 'Runtime hello', options: { effect: { Robot: 'amount:100.0;' } } },
    });
    const audio = (result?.data as IMaryttsAudioResult).audio;

    expect(result?.success).toBeTrue();
    expect((result?.data as IMaryttsAudioResult).format).toEqual('aiff');
    expect(Buffer.isBuffer(audio)).toBeTrue();
    expect(calls.includes('POST http://mary.test/base/process')).toBeTrue();

    await runtime.destroy();
  } finally {
    globalThis.fetch = originalFetch;
  }
});

tap.test('does not report MaryTTS synthesis success on non-2xx response', async () => {
  const originalFetch = globalThis.fetch;
  globalThis.fetch = (async () => new Response('boom', { status: 500 })) as typeof globalThis.fetch;

  try {
    const runtime = await new MaryttsIntegration().setup({ host: '127.0.0.1', port: 59126 }, {});
    const result = await runtime.callService?.({ domain: 'tts', service: 'speak', target: {}, data: { message: 'fail' } });
    expect(result?.success).toBeFalse();
    expect(result?.error?.includes('HTTP 500')).toBeTrue();
    await runtime.destroy();
  } finally {
    globalThis.fetch = originalFetch;
  }
});

export default tap.start();
