"use strict";
var import_rtc_node = require("@livekit/rtc-node");
var import_web = require("stream/web");
var import_vitest = require("vitest");
var import_log = require("../log.cjs");
var import_generation = require("./generation.cjs");
var import_io = require("./io.cjs");
function createSilentFrame(sampleRate = 24e3, channels = 1, durationMs = 20) {
  const samplesPerChannel = Math.floor(sampleRate * durationMs / 1e3);
  const data = new Int16Array(samplesPerChannel * channels);
  return new import_rtc_node.AudioFrame(data, sampleRate, channels, samplesPerChannel);
}
class MockAudioOutput extends import_io.AudioOutput {
  capturedFrames = [];
  constructor() {
    super(24e3);
  }
  async captureFrame(frame) {
    await super.captureFrame(frame);
    this.capturedFrames.push(frame);
    this.onPlaybackStarted(Date.now());
  }
  clearBuffer() {
  }
}
(0, import_vitest.describe)("TTS stream idle timeout", () => {
  (0, import_log.initializeLogger)({ pretty: false, level: "silent" });
  (0, import_vitest.it)("forwardAudio completes when TTS stream stalls after producing frames", async () => {
    const stalledStream = new import_web.ReadableStream({
      start(controller2) {
        controller2.enqueue(createSilentFrame());
        controller2.enqueue(createSilentFrame());
      }
    });
    const audioOutput = new MockAudioOutput();
    const controller = new AbortController();
    const [task, audioOut] = (0, import_generation.performAudioForwarding)(stalledStream, audioOutput, controller);
    import_vitest.vi.useFakeTimers();
    const taskPromise = task.result;
    await import_vitest.vi.advanceTimersByTimeAsync(11e3);
    await taskPromise;
    import_vitest.vi.useRealTimers();
    (0, import_vitest.expect)(audioOutput.capturedFrames.length).toBe(2);
    (0, import_vitest.expect)(audioOut.firstFrameFut.done).toBe(true);
  }, 1e4);
  (0, import_vitest.it)("forwardAudio honours a custom idle timeout", async () => {
    const stalledStream = new import_web.ReadableStream({
      start(controller2) {
        controller2.enqueue(createSilentFrame());
      }
    });
    const audioOutput = new MockAudioOutput();
    const controller = new AbortController();
    const [task, audioOut] = (0, import_generation.performAudioForwarding)(stalledStream, audioOutput, controller, 500);
    import_vitest.vi.useFakeTimers();
    const taskPromise = task.result;
    await import_vitest.vi.advanceTimersByTimeAsync(600);
    await taskPromise;
    import_vitest.vi.useRealTimers();
    (0, import_vitest.expect)(audioOutput.capturedFrames.length).toBe(1);
    (0, import_vitest.expect)(audioOut.firstFrameFut.done).toBe(true);
  });
  (0, import_vitest.it)("forwardAudio completes normally when TTS stream closes properly", async () => {
    const normalStream = new import_web.ReadableStream({
      start(controller2) {
        controller2.enqueue(createSilentFrame());
        controller2.enqueue(createSilentFrame());
        controller2.enqueue(createSilentFrame());
        controller2.close();
      }
    });
    const audioOutput = new MockAudioOutput();
    const controller = new AbortController();
    const [task, audioOut] = (0, import_generation.performAudioForwarding)(normalStream, audioOutput, controller);
    await task.result;
    (0, import_vitest.expect)(audioOutput.capturedFrames.length).toBe(3);
    (0, import_vitest.expect)(audioOut.firstFrameFut.done).toBe(true);
  });
  (0, import_vitest.it)("ignores PLAYBACK_STARTED from another segment before its own first frame", async () => {
    const stalledStream = new import_web.ReadableStream({ start() {
    } });
    const audioOutput = new MockAudioOutput();
    const controller = new AbortController();
    const [task, audioOut] = (0, import_generation.performAudioForwarding)(stalledStream, audioOutput, controller, 500);
    audioOut.firstFrameFut.await.catch(() => {
    });
    import_vitest.vi.useFakeTimers();
    audioOutput.onPlaybackStarted(Date.now());
    (0, import_vitest.expect)(audioOut.firstFrameFut.done).toBe(false);
    const taskPromise = task.result;
    await import_vitest.vi.advanceTimersByTimeAsync(600);
    await taskPromise;
    import_vitest.vi.useRealTimers();
    (0, import_vitest.expect)(audioOutput.capturedFrames.length).toBe(0);
    (0, import_vitest.expect)(audioOut.firstFrameFut.rejected).toBe(true);
  });
  (0, import_vitest.it)("resamples a rate-mismatched frame even after a stray PLAYBACK_STARTED", async () => {
    const stream = new import_web.ReadableStream({
      start(controller2) {
        controller2.enqueue(createSilentFrame(16e3));
        controller2.enqueue(createSilentFrame(16e3));
        controller2.close();
      }
    });
    const audioOutput = new MockAudioOutput();
    const controller = new AbortController();
    const [task, audioOut] = (0, import_generation.performAudioForwarding)(stream, audioOutput, controller);
    audioOutput.onPlaybackStarted(Date.now());
    await task.result;
    (0, import_vitest.expect)(audioOut.firstFrameFut.done).toBe(true);
    (0, import_vitest.expect)(audioOutput.capturedFrames.length).toBeGreaterThan(0);
    for (const f of audioOutput.capturedFrames) {
      (0, import_vitest.expect)(f.sampleRate).toBe(24e3);
    }
  });
  (0, import_vitest.it)("performTTSInference completes when TTS node returns stalled stream", async () => {
    const stalledTtsStream = new import_web.ReadableStream({
      start(controller2) {
        controller2.enqueue(createSilentFrame());
      }
    });
    const ttsNode = async () => stalledTtsStream;
    const textInput = new import_web.ReadableStream({
      start(controller2) {
        controller2.enqueue("Hello world");
        controller2.close();
      }
    });
    const controller = new AbortController();
    const [task, genData] = (0, import_generation.performTTSInference)(ttsNode, textInput, {}, controller);
    import_vitest.vi.useFakeTimers();
    const taskPromise = task.result;
    await import_vitest.vi.advanceTimersByTimeAsync(11e3);
    await taskPromise;
    import_vitest.vi.useRealTimers();
    (0, import_vitest.expect)(genData.ttfb).toBeDefined();
  }, 1e4);
  (0, import_vitest.it)("performTTSInference honours a custom read idle timeout", async () => {
    const stalledTtsStream = new import_web.ReadableStream({
      start(controller2) {
        controller2.enqueue(createSilentFrame());
      }
    });
    const ttsNode = async () => stalledTtsStream;
    const textInput = new import_web.ReadableStream({
      start(controller2) {
        controller2.enqueue("Hello world");
        controller2.close();
      }
    });
    const controller = new AbortController();
    const [task, genData] = (0, import_generation.performTTSInference)(
      ttsNode,
      textInput,
      {},
      controller,
      void 0,
      void 0,
      500
    );
    import_vitest.vi.useFakeTimers();
    const taskPromise = task.result;
    await import_vitest.vi.advanceTimersByTimeAsync(600);
    await taskPromise;
    import_vitest.vi.useRealTimers();
    (0, import_vitest.expect)(genData.ttfb).toBeDefined();
  }, 1e4);
});
//# sourceMappingURL=generation_tts_timeout.test.cjs.map