import { ThrowsPromise } from "@livekit/throws-transformer/throws";
import EventEmitter from "events";
import { log } from "../../log.js";
import { asError } from "../../utils.js";
import { getDefaultInferenceUrl } from "../utils.js";
import { FRAMES_PER_SECOND, SAMPLE_RATE, interruptionOptionDefaults } from "./defaults.js";
import { InterruptionDetectionError } from "./errors.js";
import { InterruptionStreamBase } from "./interruption_stream.js";
class AdaptiveInterruptionDetector extends EventEmitter {
  options;
  _label;
  logger = log();
  // Use Set instead of WeakSet to allow iteration for propagating option updates
  streams = /* @__PURE__ */ new Set();
  constructor(options = {}) {
    super();
    const {
      maxAudioDurationInS,
      baseUrl,
      audioPrefixDurationInS,
      threshold,
      detectionIntervalInS,
      inferenceTimeout,
      minInterruptionDurationInS
    } = { ...interruptionOptionDefaults, ...options };
    const { apiKey, apiSecret } = options;
    if (maxAudioDurationInS > 3) {
      throw new RangeError("maxAudioDurationInS must be less than or equal to 3.0 seconds");
    }
    const lkBaseUrl = baseUrl ?? getDefaultInferenceUrl();
    const lkApiKey = apiKey || process.env.LIVEKIT_INFERENCE_API_KEY || process.env.LIVEKIT_API_KEY || "";
    if (!lkApiKey) {
      throw new TypeError(
        "apiKey is required, either as argument or set LIVEKIT_API_KEY environmental variable"
      );
    }
    const lkApiSecret = apiSecret || process.env.LIVEKIT_INFERENCE_API_SECRET || process.env.LIVEKIT_API_SECRET || "";
    if (!lkApiSecret) {
      throw new TypeError(
        "apiSecret is required, either as argument or set LIVEKIT_API_SECRET environmental variable"
      );
    }
    this.options = {
      sampleRate: SAMPLE_RATE,
      threshold,
      minFrames: Math.ceil(minInterruptionDurationInS * FRAMES_PER_SECOND),
      maxAudioDurationInS,
      audioPrefixDurationInS,
      detectionIntervalInS,
      inferenceTimeout,
      baseUrl: lkBaseUrl,
      apiKey: lkApiKey,
      apiSecret: lkApiSecret,
      minInterruptionDurationInS
    };
    this._label = `${this.constructor.name}`;
    this.logger.debug(
      {
        baseUrl: this.options.baseUrl,
        detectionIntervalInS: this.options.detectionIntervalInS,
        audioPrefixDurationInS: this.options.audioPrefixDurationInS,
        maxAudioDurationInS: this.options.maxAudioDurationInS,
        minFrames: this.options.minFrames,
        threshold: this.options.threshold ?? null,
        inferenceTimeout: this.options.inferenceTimeout
      },
      "=== Adaptive interruption detector initialized"
    );
  }
  /**
   * The model identifier for this detector.
   */
  get model() {
    return "adaptive interruption";
  }
  /**
   * The provider identifier for this detector.
   */
  get provider() {
    return "livekit";
  }
  /**
   * The label for this detector instance.
   */
  get label() {
    return this._label;
  }
  /**
   * The sample rate used for audio processing.
   */
  get sampleRate() {
    return this.options.sampleRate;
  }
  /**
   * Emit an error event from the detector.
   */
  emitError(error) {
    this.emit("error", error);
  }
  /**
   * Creates a new InterruptionStreamBase for internal use.
   * The stream can receive audio frames and sentinels via pushFrame().
   * Use this when you need direct access to the stream for pushing frames.
   */
  createStream() {
    try {
      const streamBase = new InterruptionStreamBase(this, {});
      this.streams.add(streamBase);
      return streamBase;
    } catch (e) {
      const cause = asError(e);
      this.emitError(new InterruptionDetectionError(cause.message, Date.now(), this._label, false));
      throw e;
    }
  }
  /**
   * Remove a stream from tracking (called when stream is closed).
   */
  removeStream(stream) {
    this.streams.delete(stream);
  }
  /**
   * Update options for the detector and propagate to all active streams.
   * For WebSocket streams, this triggers a reconnection with new settings.
   */
  async updateOptions(options) {
    if (options.threshold !== void 0) {
      this.options.threshold = options.threshold;
    }
    if (options.minInterruptionDurationInS !== void 0) {
      this.options.minInterruptionDurationInS = options.minInterruptionDurationInS;
      this.options.minFrames = Math.ceil(options.minInterruptionDurationInS * FRAMES_PER_SECOND);
    }
    const updatePromises = [];
    for (const stream of this.streams) {
      updatePromises.push(stream.updateOptions(options));
    }
    await ThrowsPromise.all(updatePromises);
  }
}
export {
  AdaptiveInterruptionDetector
};
//# sourceMappingURL=interruption_detector.js.map