// ============================================================
// TTS Player — 语音播放器
// 职责：接收后端流式 tts_audio 片段，按句子排队播放
// 设计：第一句到达即开始播放，后续句子在 onended 回调中自动衔接
// ============================================================

type OnEndCallback = () => void;

export class TTSPlayer {
  /** 当前句子的音频片段缓冲（每句可能含多个 chunk） */
  private sentenceChunks: string[] = [];
  /** 已就绪的句子 Blob URL 播放队列 */
  private queue: string[] = [];
  private audio: HTMLAudioElement | null = null;
  private _isPlaying = false;
  private onEndCallback: OnEndCallback | null = null;

  /** 注册播放完成回调（所有句子播完后触发） */
  onEnd(cb: OnEndCallback): void {
    this.onEndCallback = cb;
  }

  /** 当前是否正在播放 */
  get isPlaying(): boolean {
    return this._isPlaying;
  }

  /**
   * 入队一个 TTS 音频片段
   * @param base64 Base64 编码的音频数据
   * @param mimeType 音频格式（"audio/mp3"）
   * @param isLast 当前句子是否合成完毕（每句结束时为 true）
   */
  enqueue(base64: string, mimeType: string, isLast: boolean): void {
    this.sentenceChunks.push(base64);

    if (isLast) {
      // 当前句子的音频已完整，拼接并加入播放队列
      const combined = this.sentenceChunks.join("");
      this.sentenceChunks = [];

      const url = this.base64ToBlobUrl(combined, mimeType);
      this.queue.push(url);

      // 如果当前没有在播，立即开始播放
      if (!this._isPlaying) {
        this.playNext();
      }
    }
  }

  /** 停止播放并清空所有队列 */
  stop(): void {
    if (this.audio) {
      this.audio.pause();
      this.audio.removeAttribute("src");
      this.audio = null;
    }
    // 释放队列中的 Blob URL
    for (const url of this.queue) {
      URL.revokeObjectURL(url);
    }
    this.queue = [];
    this.sentenceChunks = [];
    this._isPlaying = false;
  }

  /** 暂停播放 */
  pause(): void {
    this.audio?.pause();
  }

  /** 恢复播放 */
  resume(): void {
    this.audio?.play();
  }

  /** 播放队列中的下一个句子 */
  private playNext(): void {
    if (this.queue.length === 0) {
      this._isPlaying = false;
      this.onEndCallback?.();
      return;
    }

    const url = this.queue.shift()!;
    const audio = new Audio(url);
    this.audio = audio;
    this._isPlaying = true;

    audio.onended = () => {
      URL.revokeObjectURL(url);
      this.audio = null;
      this.playNext();
    };

    audio.onerror = () => {
      console.error("[TTS] 播放失败，跳过");
      URL.revokeObjectURL(url);
      this.audio = null;
      this.playNext();
    };

    audio.play().catch((err) => {
      console.error("[TTS] play() 被拒绝:", err);
      URL.revokeObjectURL(url);
      this.audio = null;
      this.playNext();
    });
  }

  /** Base64 字符串转 Blob URL */
  private base64ToBlobUrl(base64: string, mimeType: string): string {
    const binary = atob(base64);
    const bytes = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i++) {
      bytes[i] = binary.charCodeAt(i);
    }
    const blob = new Blob([bytes], { type: mimeType });
    return URL.createObjectURL(blob);
  }
}
