import { beforeAll, describe, expect, it } from "vitest";
import { normalizeLanguage } from "../language.js";
import { initializeLogger } from "../log.js";
import { DEFAULT_API_CONNECT_OPTIONS } from "../types.js";
import { VAD } from "../vad.js";
import {
  STT,
  normalizeSTTFallback,
  parseSTTModelString
} from "./stt.js";
import { VAD as InferenceVAD } from "./vad.js";
beforeAll(() => {
  initializeLogger({ level: "silent", pretty: false });
});
function makeStt(overrides = {}) {
  const defaults = {
    model: "deepgram",
    apiKey: "test-key",
    apiSecret: "test-secret",
    baseURL: "https://example.livekit.cloud"
  };
  return new STT({ ...defaults, ...overrides });
}
describe("parseSTTModelString", () => {
  it("simple model without language", () => {
    const [model, language] = parseSTTModelString("deepgram");
    expect(model).toBe("deepgram");
    expect(language).toBeUndefined();
  });
  it("model with language suffix", () => {
    const [model, language] = parseSTTModelString("deepgram:en");
    expect(model).toBe("deepgram");
    expect(language).toBe("en");
  });
  it("normalizes language suffixes", () => {
    const [model, language] = parseSTTModelString("deepgram:english");
    expect(model).toBe("deepgram");
    expect(language).toBe("en");
  });
  it("provider/model format without language", () => {
    const [model, language] = parseSTTModelString("deepgram/nova-3");
    expect(model).toBe("deepgram/nova-3");
    expect(language).toBeUndefined();
  });
  it("provider/model format with language", () => {
    const [model, language] = parseSTTModelString("deepgram/nova-3:en");
    expect(model).toBe("deepgram/nova-3");
    expect(language).toBe("en");
  });
  it.each([
    ["cartesia/ink-whisper:de", "cartesia/ink-whisper", "de"],
    ["assemblyai:es", "assemblyai", "es"],
    ["deepgram/nova-2-medical:ja", "deepgram/nova-2-medical", "ja"],
    ["deepgram/nova-3:multi", "deepgram/nova-3", "multi"],
    ["cartesia:zh", "cartesia", "zh"]
  ])("various providers and languages: %s", (modelStr, expectedModel, expectedLang) => {
    const [model, language] = parseSTTModelString(modelStr);
    expect(model).toBe(expectedModel);
    expect(language).toBe(expectedLang);
  });
  it("auto model without language", () => {
    const [model, language] = parseSTTModelString("auto");
    expect(model).toBe("auto");
    expect(language).toBeUndefined();
  });
  it("auto model with language", () => {
    const [model, language] = parseSTTModelString("auto:pt");
    expect(model).toBe("auto");
    expect(language).toBe("pt");
  });
});
describe("normalizeSTTFallback", () => {
  it("single string model", () => {
    const result = normalizeSTTFallback("deepgram/nova-3");
    expect(result).toEqual([{ model: "deepgram/nova-3" }]);
  });
  it("single FallbackModel dict", () => {
    const fallback = { model: "deepgram/nova-3" };
    const result = normalizeSTTFallback(fallback);
    expect(result).toEqual([{ model: "deepgram/nova-3" }]);
  });
  it("list of string models", () => {
    const result = normalizeSTTFallback(["deepgram/nova-3", "cartesia/ink-whisper"]);
    expect(result).toEqual([{ model: "deepgram/nova-3" }, { model: "cartesia/ink-whisper" }]);
  });
  it("list of FallbackModel dicts", () => {
    const fallbacks = [{ model: "deepgram/nova-3" }, { model: "assemblyai" }];
    const result = normalizeSTTFallback(fallbacks);
    expect(result).toEqual([{ model: "deepgram/nova-3" }, { model: "assemblyai" }]);
  });
  it("mixed list of strings and dicts", () => {
    const result = normalizeSTTFallback([
      "deepgram/nova-3",
      { model: "cartesia/ink-whisper" },
      "assemblyai"
    ]);
    expect(result).toEqual([
      { model: "deepgram/nova-3" },
      { model: "cartesia/ink-whisper" },
      { model: "assemblyai" }
    ]);
  });
  it("string with language suffix discards language", () => {
    const result = normalizeSTTFallback("deepgram/nova-3:en");
    expect(result).toEqual([{ model: "deepgram/nova-3" }]);
  });
  it("FallbackModel with extraKwargs is preserved", () => {
    const fallback = {
      model: "deepgram/nova-3",
      extraKwargs: { keywords: [["livekit", 1.5]], punctuate: true }
    };
    const result = normalizeSTTFallback(fallback);
    expect(result).toEqual([
      {
        model: "deepgram/nova-3",
        extraKwargs: { keywords: [["livekit", 1.5]], punctuate: true }
      }
    ]);
  });
  it("list with extraKwargs preserved", () => {
    const result = normalizeSTTFallback([
      { model: "deepgram/nova-3", extraKwargs: { punctuate: true } },
      "cartesia/ink-whisper",
      { model: "assemblyai", extraKwargs: { format_turns: true } }
    ]);
    expect(result).toEqual([
      { model: "deepgram/nova-3", extraKwargs: { punctuate: true } },
      { model: "cartesia/ink-whisper" },
      { model: "assemblyai", extraKwargs: { format_turns: true } }
    ]);
  });
  it("empty list returns empty list", () => {
    const result = normalizeSTTFallback([]);
    expect(result).toEqual([]);
  });
  it("multiple colons in model string splits on last", () => {
    const result = normalizeSTTFallback("some:model:part:fr");
    expect(result).toEqual([{ model: "some:model:part" }]);
  });
});
describe("STT constructor fallback and connOptions", () => {
  it("normalizes language in constructor and model string", () => {
    const stt = makeStt({ model: "deepgram/nova-3:english" });
    expect(stt["opts"].language).toBe("en");
  });
  it("prefers explicit normalized language over model suffix", () => {
    const stt = makeStt({ model: "deepgram/nova-3:english", language: "en_US" });
    expect(stt["opts"].language).toBe(normalizeLanguage("en_US"));
  });
  it("fallback not given defaults to undefined", () => {
    const stt = makeStt();
    expect(stt["opts"].fallback).toBeUndefined();
  });
  it("fallback single string is normalized", () => {
    const stt = makeStt({ fallback: "cartesia/ink-whisper" });
    expect(stt["opts"].fallback).toEqual([{ model: "cartesia/ink-whisper" }]);
  });
  it("fallback list of strings is normalized", () => {
    const stt = makeStt({ fallback: ["deepgram/nova-3", "assemblyai"] });
    expect(stt["opts"].fallback).toEqual([{ model: "deepgram/nova-3" }, { model: "assemblyai" }]);
  });
  it("fallback single FallbackModel is normalized to list", () => {
    const stt = makeStt({ fallback: { model: "deepgram/nova-3" } });
    expect(stt["opts"].fallback).toEqual([{ model: "deepgram/nova-3" }]);
  });
  it("fallback with extraKwargs is preserved", () => {
    const stt = makeStt({
      fallback: {
        model: "deepgram/nova-3",
        extraKwargs: { punctuate: true, keywords: [["livekit", 1.5]] }
      }
    });
    expect(stt["opts"].fallback).toEqual([
      {
        model: "deepgram/nova-3",
        extraKwargs: { punctuate: true, keywords: [["livekit", 1.5]] }
      }
    ]);
  });
  it("fallback mixed list is normalized", () => {
    const stt = makeStt({
      fallback: [
        "deepgram/nova-3",
        { model: "cartesia", extraKwargs: { min_volume: 0.5 } },
        "assemblyai"
      ]
    });
    expect(stt["opts"].fallback).toEqual([
      { model: "deepgram/nova-3" },
      { model: "cartesia", extraKwargs: { min_volume: 0.5 } },
      { model: "assemblyai" }
    ]);
  });
  it("fallback string with language discards language", () => {
    const stt = makeStt({ fallback: "deepgram/nova-3:en" });
    expect(stt["opts"].fallback).toEqual([{ model: "deepgram/nova-3" }]);
  });
  it("connOptions not given uses default", () => {
    const stt = makeStt();
    expect(stt["opts"].connOptions).toEqual(DEFAULT_API_CONNECT_OPTIONS);
  });
  it("connOptions custom timeout", () => {
    const custom = { timeoutMs: 3e4, maxRetry: 3, retryIntervalMs: 2e3 };
    const stt = makeStt({ connOptions: custom });
    expect(stt["opts"].connOptions).toEqual(custom);
    expect(stt["opts"].connOptions.timeoutMs).toBe(3e4);
  });
  it("connOptions custom maxRetry", () => {
    const custom = { timeoutMs: 1e4, maxRetry: 5, retryIntervalMs: 2e3 };
    const stt = makeStt({ connOptions: custom });
    expect(stt["opts"].connOptions).toEqual(custom);
    expect(stt["opts"].connOptions.maxRetry).toBe(5);
  });
  it("connOptions full custom", () => {
    const custom = { timeoutMs: 6e4, maxRetry: 10, retryIntervalMs: 2e3 };
    const stt = makeStt({ connOptions: custom });
    expect(stt["opts"].connOptions).toEqual(custom);
    expect(stt["opts"].connOptions.timeoutMs).toBe(6e4);
    expect(stt["opts"].connOptions.maxRetry).toBe(10);
    expect(stt["opts"].connOptions.retryIntervalMs).toBe(2e3);
  });
});
describe("STT diarization capabilities", () => {
  it("no diarization by default", () => {
    const stt = makeStt();
    expect(stt.capabilities.diarization).toBe(false);
  });
  it("diarization enabled with deepgram diarize option", () => {
    const stt = makeStt({ modelOptions: { diarize: true } });
    expect(stt.capabilities.diarization).toBe(true);
  });
  it("diarization disabled with diarize false", () => {
    const stt = makeStt({ modelOptions: { diarize: false } });
    expect(stt.capabilities.diarization).toBe(false);
  });
  it("diarization enabled with assemblyai speaker_labels", () => {
    const stt = makeStt({
      model: "assemblyai/universal-streaming",
      modelOptions: { speaker_labels: true }
    });
    expect(stt.capabilities.diarization).toBe(true);
  });
  it("updateOptions toggles diarization capability", () => {
    const stt = makeStt();
    expect(stt.capabilities.diarization).toBe(false);
    stt.updateOptions({ modelOptions: { diarize: true } });
    expect(stt.capabilities.diarization).toBe(true);
    stt.updateOptions({ modelOptions: { diarize: false } });
    expect(stt.capabilities.diarization).toBe(false);
  });
  it("diarization enabled with xai diarize option", () => {
    const stt = makeStt({
      model: "xai/stt-1",
      modelOptions: { diarize: true }
    });
    expect(stt.capabilities.diarization).toBe(true);
  });
  it("updateOptions preserves unrelated flags when merging", () => {
    const stt = makeStt({ modelOptions: { diarize: true } });
    expect(stt.capabilities.diarization).toBe(true);
    stt.updateOptions({ modelOptions: { endpointing: 500 } });
    expect(stt["opts"].modelOptions).toHaveProperty("diarize", true);
    expect(stt["opts"].modelOptions).toHaveProperty("endpointing", 500);
    expect(stt.capabilities.diarization).toBe(true);
  });
  it("updateOptions merges modelOptions on associated streams", () => {
    const stt = makeStt({ modelOptions: { diarize: true } });
    const stream = stt.stream();
    stt.updateOptions({ modelOptions: { endpointing: 500 } });
    expect(stream["opts"].modelOptions).toHaveProperty("diarize", true);
    expect(stream["opts"].modelOptions).toHaveProperty("endpointing", 500);
    stream.close();
  });
});
describe("STT VAD handling for Speechmatics models", () => {
  class MockVAD extends VAD {
    label = "mock";
    constructor() {
      super({ updateInterval: 0 });
    }
    stream() {
      throw new Error("not implemented");
    }
  }
  it("non-speechmatics model has no VAD", async () => {
    const stt = makeStt({ model: "deepgram/nova-3" });
    expect(stt["vad"]).toBeUndefined();
    await expect(stt.vadPromise).resolves.toBeUndefined();
  });
  it("speechmatics model with no user vad falls back to the inference VAD", async () => {
    const stt = makeStt({ model: "speechmatics/enhanced" });
    expect(stt["vad"]).toBeInstanceOf(InferenceVAD);
    await expect(stt.vadPromise).resolves.toBe(stt["vad"]);
  });
  it("speechmatics model with user vad uses that vad", async () => {
    const vad = new MockVAD();
    const stt = makeStt({ model: "speechmatics/enhanced", vad });
    expect(stt["vad"]).toBe(vad);
    await expect(stt.vadPromise).resolves.toBe(vad);
  });
  it("user vad with non-speechmatics model is ignored", async () => {
    const vad = new MockVAD();
    const stt = makeStt({ model: "deepgram/nova-3", vad });
    expect(stt["vad"]).toBeUndefined();
    await expect(stt.vadPromise).resolves.toBeUndefined();
  });
  it("updateOptions speechmatics \u2192 non-speechmatics clears VAD", async () => {
    const vad = new MockVAD();
    const stt = makeStt({ model: "speechmatics/enhanced", vad });
    await expect(stt.vadPromise).resolves.toBe(vad);
    stt.updateOptions({ model: "deepgram/nova-3" });
    expect(stt["vad"]).toBeUndefined();
    await expect(stt.vadPromise).resolves.toBeUndefined();
  });
  it("updateOptions non-speechmatics \u2192 speechmatics falls back to the inference VAD", () => {
    const stt = makeStt({ model: "deepgram/nova-3" });
    expect(stt["vad"]).toBeUndefined();
    stt.updateOptions({ model: "speechmatics/enhanced" });
    expect(stt["vad"]).toBeInstanceOf(InferenceVAD);
  });
});
//# sourceMappingURL=stt.test.js.map