import { ParticipantKind } from "@livekit/rtc-node";
import { ReadableStream } from "node:stream/web";
import { afterEach, describe, expect, it, vi } from "vitest";
import { ChatContext } from "../llm/chat_context.js";
import { initializeLogger } from "../log.js";
import { SpeechEventType } from "../stt/stt.js";
import { VAD } from "../vad.js";
import {
  AudioRecognition
} from "./audio_recognition.js";
const fastTurnDetector = {
  model: "test-turn-detector",
  provider: "test-provider",
  supportsLanguage: async () => true,
  unlikelyThreshold: async () => void 0,
  predictEndOfTurn: async () => 1
};
class SilentVADStream extends Object {
  updateInputStream() {
  }
  detachInputStream() {
  }
  close() {
  }
  [Symbol.asyncIterator]() {
    return this;
  }
  async next() {
    return { done: true, value: void 0 };
  }
}
class SilentVAD extends VAD {
  label = "silent-vad";
  constructor() {
    super({ updateInterval: 1 });
  }
  stream() {
    return new SilentVADStream();
  }
}
function createHooks() {
  return {
    onInterruption: vi.fn(),
    onStartOfSpeech: vi.fn(),
    onVADInferenceDone: vi.fn(),
    onEndOfSpeech: vi.fn(),
    onInterimTranscript: vi.fn(),
    onFinalTranscript: vi.fn(),
    onPreemptiveGeneration: vi.fn(),
    onAgentBackchannelOpportunity: vi.fn(),
    onUserTurnExceeded: vi.fn(),
    retrieveChatCtx: () => ChatContext.empty(),
    onEndOfTurn: vi.fn(async () => true)
  };
}
describe("AudioRecognition EOU scheduling with STT timestamps", () => {
  initializeLogger({ pretty: false, level: "silent" });
  afterEach(() => {
    vi.useRealTimers();
  });
  async function runTurn(opts) {
    vi.useFakeTimers();
    const hooks = createHooks();
    let sttController;
    const sttNode = async () => new ReadableStream({
      start(controller) {
        sttController = controller;
      }
    });
    const ar = new AudioRecognition({
      recognitionHooks: hooks,
      stt: sttNode,
      vad: new SilentVAD(),
      turnDetector: fastTurnDetector,
      turnDetectionMode: "vad",
      minEndpointingDelay: 0,
      maxEndpointingDelay: 0,
      sttModel: "stt-model",
      sttProvider: "stt-provider",
      getLinkedParticipant: () => ({ sid: "p1", identity: "bob", kind: ParticipantKind.AGENT })
    });
    await ar.start({ inputStartedAt: Date.now() });
    await vi.advanceTimersByTimeAsync(0);
    sttController.enqueue({
      type: SpeechEventType.FINAL_TRANSCRIPT,
      alternatives: [
        {
          language: "en",
          text: "okay",
          startTime: 0,
          endTime: opts.sttEndTimeInS,
          confidence: 0.9
        }
      ]
    });
    return { ar, hooks };
  }
  async function closeRecognition(ar) {
    vi.useRealTimers();
    await ar.close();
  }
  it("commits the user turn promptly when STT endTime maps to the past", async () => {
    const { ar, hooks } = await runTurn({ sttEndTimeInS: 0.5 });
    try {
      await vi.advanceTimersByTimeAsync(2e3);
      expect(hooks.onEndOfTurn).toHaveBeenCalledTimes(1);
    } finally {
      await closeRecognition(ar);
    }
  });
  it("does not stall the user turn commit when STT endTime maps to the future", async () => {
    const { ar, hooks } = await runTurn({ sttEndTimeInS: 120 });
    try {
      await vi.advanceTimersByTimeAsync(2e3);
      expect(hooks.onEndOfTurn).toHaveBeenCalledTimes(1);
    } finally {
      await closeRecognition(ar);
    }
  });
});
//# sourceMappingURL=audio_recognition_eou.test.js.map