/**
 * 语音合成工具类
 * @class SpeechSynthesisUtil
 * @description 用于处理文字转语音的工具类,包含音频播放、合成等功能
 */

// 事件类型枚举
export enum EventType {
  STATE_CHANGE = 'stateChange',
  SYNTHESIS_START = 'synthesisStart',
  SYNTHESIS_END = 'synthesisEnd',
  AUDIO_PLAY = 'audioPlay',
  AUDIO_END = 'audioEnd',
  ERROR = 'error',
  PROGRESS = 'progress',
  PAUSE = 'pause',
  RESUME = 'resume'
}

// 状态接口
interface State {
  isInterrupted: boolean;
  isStartPlayQueue: boolean;
  isPlaying: boolean;
  isEnded: boolean;
  isError: boolean;
  isPaused: boolean;
  isSynthesizing: boolean;
  needsUserInteraction: boolean;
}

// 构造函数选项接口
interface SpeechSynthesisOptions {
  API_KEY: string;
  GroupId: string;
  MAX_QUEUE_LENGTH?: number;
  modelConfig?: any;
}

// 音频文本队列项接口
interface AudioTextQueueItem {
  text: string;
  audio: string;
}

// API响应接口
interface ApiResponse {
  data: {
    data: {
      audio: string;
    };
  };
}

class SpeechSynthesisUtil {
  private API_KEY: string;
  private GroupId: string;
  private modelConfig: any;
  private audioContext: UniApp.InnerAudioContext | null = null;
  private audioQueue: string[] = [];
  private audioTextQueue: AudioTextQueueItem[] = [];
  private state: State;
  private fileManager: UniApp.FileSystemManager | null = null;
  private pendingAudioUrl: string | null = null;
  private eventListeners: Map<EventType, Set<Function>>;
  private timer: { start: number; end: number };
  private modelContent: string = '';
  private MAX_QUEUE_LENGTH: number;
  private totalAudioCount: number = 0;  // 记录总的音频数量

  constructor(options: SpeechSynthesisOptions) {
    this.API_KEY = options.API_KEY;
    this.GroupId = options.GroupId;
    this.MAX_QUEUE_LENGTH = options.MAX_QUEUE_LENGTH || 3;
    this.modelConfig = options.modelConfig || {};
    this.state = {
      isInterrupted: false,
      isStartPlayQueue: false,
      isPlaying: false,
      isEnded: false,
      isError: false,
      isPaused: false,
      isSynthesizing: false,
      needsUserInteraction: false
    };

    try {
      if (typeof uni !== 'undefined' && uni.getFileSystemManager) {
        this.fileManager = uni.getFileSystemManager();
      }
    } catch (error) {
      console.warn('文件系统管理器初始化失败，将使用临时文件存储');
    }

    this.eventListeners = new Map();
    this.timer = { start: 0, end: 0 };

    // 绑定方法
    this.handleAudioPlay = this.handleAudioPlay.bind(this);
    this.handleAudioEnd = this.handleAudioEnd.bind(this);
    this.handleAudioError = this.handleAudioError.bind(this);
  }

  /**
   * 文字转语音方法
   * @param text - 需要转换的文本
   */
  public async textToSpeech(text: string): Promise<void> {
    if (!text) return;

    // 检查是否已经在队列中
    if (this.audioTextQueue.some(item => item.text === text)) {
        return;
    }

    this.emit(EventType.SYNTHESIS_START, { text });

    if (this.audioQueue.length === 0) {
        this.startTimer();
        this.totalAudioCount = 0;
    }

    // 等待之前的合成完成
    if (this.state.isSynthesizing) {
        await new Promise<void>((resolve) => {
            const checkSynthesis = setInterval(() => {
                if (!this.state.isSynthesizing) {
                    clearInterval(checkSynthesis);
                    resolve();
                }
            }, 100);
        });
    }

    this.setState({ isSynthesizing: true, isError: false });

    try {
        const response = await this.requestSynthesis(text);
        await this.processAudioResponse(response, text);
        await this.startPlayingIfNeeded();
        this.emit(EventType.SYNTHESIS_END, { text });
    } catch (error) {
        this.emit(EventType.ERROR, { error, text });
        throw error;
    } finally {
        this.setState({ isSynthesizing: false });
    }
  }

  /**
   * 请求语音合成
   * @param text - 需要合成的文本
   * @private
   */
  private async requestSynthesis(text: string): Promise<ApiResponse> {
    try {
      return new Promise((resolve, reject) => {
        uni.request({
          url: `https://api.minimax.chat/v1/t2a_v2?GroupId=${this.GroupId}`,
          method: 'POST',
          header: {
            "Authorization": `Bearer ${this.API_KEY}`,
            "Content-Type": "application/json;charset=utf-8"
          },
          data: {
            // 默认配置
            model: "speech-01-turbo-240228",
            text,
            stream: false,
            voice_setting: {
              voice_id: 'female-yujie', 
              speed: 1.2,
              vol: 1.0,
              ...(this.modelConfig?.voice_setting || {}) // 合并用户配置的voice_setting
            },
            audio_setting: {
              audio_sample_rate: 32000,
              bitrate: 128000, 
              format: "mp3",
              channel: 2,
              ...(this.modelConfig?.audio_setting || {}) // 合并用户配置的audio_setting
            },
            ...(this.modelConfig || {}) // 合并其他用户配置
          },
          success: (res: ApiResponse) => {
            if (!res.data?.data?.audio) {
              reject(new Error('API 响应格式错误: ' + JSON.stringify(res.data)));
              return;
            }
            resolve(res);
          },
          fail: (error: any) => {
            reject(new Error('请求失败: ' + JSON.stringify(error)));
          }
        });
      });
    } catch (error) {
      console.error('语音合成请求失败:', error);
      throw error;
    }
  }

  /**
   * 处理音频响应数据
   * @param response - 响应数据
   * @param text - 对应的文本内容
   * @private
   */
  private async processAudioResponse(response: ApiResponse, text: string): Promise<void> {
    if (!response?.data?.data?.audio) {
        throw new Error('无效的音频数据应答');
    }

    try {
        // 检查是否已经存在相同的文本
        if (this.audioTextQueue.some(item => item.text === text)) {
            return;
        }

        const audio = response.data.data.audio;
        if (typeof audio !== 'string' || audio.length === 0) {
            throw new Error('无效的音频数据格式');
        }

        const binaryArray = new Uint8Array(audio.match(/.{1,2}/g)!.map(byte => parseInt(byte, 16)));
        
        let tempFilePath: string;
        if (this.fileManager) {
            tempFilePath = `${uni.env.USER_DATA_PATH}/audio-${Date.now()}.mp3`;
            await this.writeAudioFile(tempFilePath, binaryArray.buffer);
        } else {
            const blob = new Blob([binaryArray], { type: 'audio/mp3' });
            tempFilePath = URL.createObjectURL(blob);
        }
        
        this.audioQueue.push(tempFilePath);
        this.audioTextQueue.push({
            text,
            audio: tempFilePath
        });
        this.totalAudioCount++;  // 增加总数计数
    } catch (error) {
        console.error('处理音频数据失败:', error);
        throw new Error(`音频数据处理失败: ${error}`);
    }
  }

  /**
   * 写入音频文件
   * @param filePath - 文件路径
   * @param buffer - 音频数据
   * @private
   */
  private writeAudioFile(filePath: string, buffer: ArrayBuffer): Promise<void> {
    if (!this.fileManager) {
      return Promise.resolve();
    }
    
    return new Promise((resolve, reject) => {
      this.fileManager!.writeFile({
        filePath,
        data: buffer,
        encoding: 'binary',
        success: () => resolve(),
        fail: reject
      });
    });
  }

  /**
   * 开始播放音频队列
   * @private
   */
  private async playAudioQueue(): Promise<void> {
    if (this.audioQueue.length === 0) {
        this.setState({ 
            isStartPlayQueue: false,
            isEnded: true,
            isPlaying: false
        });
        return;
    }

    if (this.state.isPlaying) return;

    // 销毁旧的音频上下文（如果存在）
    this.destroyAudioContext();
    
    // 创建新的音频上下文
    this.audioContext = uni.createInnerAudioContext();
    this.bindAudioEvents();

    this.setState({ 
        isStartPlayQueue: true,
        isPlaying: true,
        isEnded: false
    });

    const currentAudio = this.audioQueue[0];
    const currentItem = this.audioTextQueue[0];
    
    this.audioContext.src = currentAudio;
    
    try {
        await this.safePlay();
    } catch (error) {
        console.warn('播放失败:', error);
        this.audioQueue.shift();
        this.audioTextQueue.shift();
        this.setState({ 
            isPlaying: false,
            needsUserInteraction: true 
        });
        this.pendingAudioUrl = currentAudio;
        this.destroyAudioContext();
    }
  }

  /**
   * 安全播放方法
   * @private
   */
  private async safePlay(): Promise<void> {
    return new Promise((resolve, reject) => {
        try {
            this.audioContext!.play();
            resolve();
        } catch (error) {
            console.error('播放失败:', error);
            reject(error);
        }
    });
  }

  /**
   * 手动触发播放
   */
  public async manualPlay(): Promise<void> {
    if (this.state.needsUserInteraction && this.pendingAudioUrl) {
      this.state.needsUserInteraction = false;
      if (!this.audioContext) {
        this.audioContext = uni.createInnerAudioContext();
        this.bindAudioEvents();
      }
      this.audioContext.src = this.pendingAudioUrl;
      try {
        await this.safePlay();
        this.pendingAudioUrl = null;
      } catch (error) {
        console.error('手动播放失败:', error);
        this.handleError(error);
      }
    }
  }

  /**
   * 绑定音频事件
   * @private
   */
  private bindAudioEvents(): void {
    if (!this.audioContext) return;
    
    // 先解绑之前的事件（如果有的话）
    try {
        this.audioContext.offPlay(this.handleAudioPlay);
    } catch (e) {
        console.warn('移除播放事件监听失败:', e);
    }
    
    // 重新绑定事件
    this.audioContext.onPlay(this.handleAudioPlay);
    this.audioContext.onEnded(this.handleAudioEnd);
    this.audioContext.onError(this.handleAudioError);
  }

  /**
   * 处理音频播放开始事件
   * @private
   */
  private handleAudioPlay(): void {
    const currentAudio = this.audioQueue[0];
    const currentItem = this.audioTextQueue[0];
    
    // 确保只有在真正开始播放时才更新状态和触发事件
    if (currentAudio && currentItem) {
        this.setState({ isPlaying: true, isPaused: false });
        this.emit(EventType.AUDIO_PLAY, {
            currentAudio,
            currentText: currentItem.text,
            remainingCount: this.audioQueue.length - 1,
            totalCount: this.totalAudioCount,
            progress: this.calculateProgress()
        });
    }
  }

  /**
   * 处理音频播放结束事件
   * @private
   */
  private handleAudioEnd(): void {
    const finishedAudio = this.audioQueue[0];
    const finishedItem = this.audioTextQueue[0];
    
    if (!finishedAudio || !finishedItem) return;
    
    // 先设置播放状态为 false
    this.setState({ isPlaying: false });
    
    // 检查是否还有待播放的音频
    if (this.audioQueue.length > 1) {
        // 移除当前音频
        this.audioQueue.shift();
        this.audioTextQueue.shift();
        
        // 发送进度事件
        this.emit(EventType.PROGRESS, {
            progress: this.calculateProgress(),
            playedCount: this.totalAudioCount - this.audioQueue.length,
            totalCount: this.totalAudioCount,
            currentText: finishedItem.text,
            remainingCount: this.audioQueue.length - 1
        });

        // 确保在开始下一个播放前销毁当前上下文
        this.destroyAudioContext();
        
        // 使用 setTimeout 确保异步执行下一个播放
        setTimeout(() => {
            this.playAudioQueue();
        }, 0);
    } else {
        // 最后一个音频播放完成
        this.audioQueue.shift();
        this.audioTextQueue.shift();
        
        this.setState({ 
            isEnded: true,
            isStartPlayQueue: false,
            isPlaying: false,
            isPaused: false
        });
        
        this.emit(EventType.AUDIO_END, {
            finishedAudio,
            finishedText: finishedItem.text,
            remainingCount: 0,
            isComplete: true,
            progress: 100,
            totalCount: this.totalAudioCount,
            playedCount: this.totalAudioCount
        });
        
        this.destroyAudioContext();
        this.resetTextProcessor();
    }
  }

  /**
   * 处理音频错误事件
   * @private
   */
  private handleAudioError(res: any): void {
    console.error('音频播放错误:', res);
    this.state.isError = true;
    this.destroyAudioContext();
  }

  /**
   * 销毁音频上下文
   * @private
   */
  private destroyAudioContext(): void {
    if (this.audioContext) {
        // 在销毁前先停止播放
        try {
            this.audioContext.stop();
        } catch (e) {
            console.warn('停止音频播放失败:', e);
        }

        // 移除事件监听 (只使用 offPlay，因为其他的 off 方法不存在)
        try {
            this.audioContext.offPlay(this.handleAudioPlay);
        } catch (e) {
            console.warn('移除播放事件监听失败:', e);
        }

        // 销毁音频上下文
        this.audioContext.destroy();
        this.audioContext = null;
    }
  }

  /**
   * 设置状态
   * @private
   */
  private setState(newState: Partial<State>): void {
    const oldState = { ...this.state };
    this.state = { ...this.state, ...newState };
    
    this.emit(EventType.STATE_CHANGE, {
      oldState,
      newState: this.getState(),
      changes: Object.keys(newState)
    });
  }

  /**
   * 获取当前状态
   */
  public getState(): State {
    return { ...this.state };
  }

  /**
   * 重置所有状态
   */
  public reset(): void {
    Object.keys(this.state).forEach(key => {
      (this.state as any)[key] = false;
    });
    this.audioQueue = [];
    this.audioTextQueue = [];
    this.totalAudioCount = 0;  // 重置总数计数
    this.destroyAudioContext();
    this.setState({ isEnded: false });
  }

  /**
   * 添加事件监听
   */
  public on(event: EventType, callback: Function): void {
    if (!this.eventListeners.has(event)) {
      this.eventListeners.set(event, new Set());
    }
    this.eventListeners.get(event)!.add(callback);
  }

  /**
   * 移除事件监听
   */
  public off(event: EventType, callback: Function): void {
    const listeners = this.eventListeners.get(event);
    if (listeners) {
      listeners.delete(callback);
    }
  }

  /**
   * 触发事件
   * @private
   */
  private emit(event: EventType, data: any): void {
    const listeners = this.eventListeners.get(event);
    if (listeners) {
      listeners.forEach(callback => callback(data));
    }
  }

  /**
   * 开始计时
   * @private
   */
  private startTimer(): void {
    this.timer.start = performance.now();
  }

  /**
   * 结束计时并返回时（毫秒）
   * @private
   */
  private endTimer(): number {
    this.timer.end = performance.now();
    return this.timer.end - this.timer.start;
  }

  /**
   * 批量处理文本
   */
  public async processText(text: string): Promise<void> {
    if (!text.trim()) return;

    // 累积文本到 modelContent
    this.modelContent += text;

    // 检查是否有完整的句子（以句号、感叹号、问号结尾）
    const endPunctuations = ['。', '！', '？'];
    let hasCompleteSegment = endPunctuations.some(punct => this.modelContent.includes(punct));

    // 如果没有完整的句子，继续累积
    if (!hasCompleteSegment) {
        return;
    }

    // 获取分段
    const segments = this.processContent(this.modelContent);
    this.modelContent = ''; // 清空累积的内容

    if (segments.length === 0) return;

    // 创建已处理文本集合
    const processedTexts = new Set<string>();

    // 处理每个分段
    for (const segment of segments) {
        // 检查是否已经处理过
        if (processedTexts.has(segment) || 
            this.audioTextQueue.some(item => item.text === segment)) {
            continue;
        }

        // 添加到已处理集合
        processedTexts.add(segment);

        // 合成语音
        await this.textToSpeech(segment);
    }
  }

  /**
   * 处理文本分段
   * @private
   */
  private processContent(inputText: string): string[] {
    // 定义标点符号及其优先级
    const punctuationGroups = [
        ['。', '！', '？'],  // 高优先级
        ['；', '：'],        // 中优先级
        ['，', '、']         // 低优先级
    ];
    
    // 最大分段长度（字符数）
    const MAX_SEGMENT_LENGTH = 100;
    
    let currentText = inputText;
    const segments: string[] = [];

    // 如果文本很短，直接作为一个分段
    if (currentText.length <= MAX_SEGMENT_LENGTH) {
        if (currentText.trim()) {
            segments.push(currentText.trim());
        }
        return segments;
    }

    // 处理长文本
    while (currentText.length > 0) {
        let segmentEnd = -1;
        let foundPunctuation = '';
        
        // 按优先级查找标点符号
        for (const punctuationGroup of punctuationGroups) {
            for (const punctuation of punctuationGroup) {
                const index = currentText.indexOf(punctuation);
                if (index !== -1 && (index <= MAX_SEGMENT_LENGTH || segmentEnd === -1)) {
                    segmentEnd = index;
                    foundPunctuation = punctuation;
                    break;
                }
            }
            if (segmentEnd !== -1 && segmentEnd <= MAX_SEGMENT_LENGTH) break;
        }

        // 如果找到的分段点超过最大长度，或者没找到分段点
        if (segmentEnd === -1 || segmentEnd > MAX_SEGMENT_LENGTH) {
            segmentEnd = Math.min(MAX_SEGMENT_LENGTH, currentText.length);
            // 向前查找合适的分段点
            while (segmentEnd > MAX_SEGMENT_LENGTH * 0.8) {
                if (this.isGoodSplitPoint(currentText, segmentEnd)) {
                    break;
                }
                segmentEnd--;
            }
        }

        // 提取分段
        const segment = currentText.substring(0, segmentEnd + 1);
        if (segment.trim()) {
            segments.push(segment.trim());
        }

        // 更新剩余文本
        currentText = currentText.substring(segmentEnd + 1);
    }

    return segments;
  }

  /**
   * 强制处理剩余文本
   */
  public async flushRemainingText(): Promise<void> {
    if (this.modelContent.trim()) {
        const segments = this.processContent(this.modelContent);
        this.modelContent = '';
        
        for (const segment of segments) {
            if (segment.trim() && !this.audioTextQueue.some(item => item.text === segment)) {
                await this.textToSpeech(segment);
            }
        }
    }
  }

  /**
   * 重置文本处理状态
   */
  public resetTextProcessor(): void {
    this.modelContent = '';
  }

  /**
   * 暂停播放
   */
  public pause(): void {
    if (this.audioContext && this.state.isPlaying) {
      this.audioContext.pause();
      this.setState({ 
        isPaused: true,
        isPlaying: false 
      });
      this.emit(EventType.PAUSE, {});
    }
  }

  /**
   * 恢复播放
   */
  public resume(): void {
    if (this.audioContext && this.state.isPaused) {
      this.audioContext.play();
      this.setState({ 
        isPaused: false,
        isPlaying: true 
      });
      this.emit(EventType.RESUME, {});
    }
  }

  /**
   * 切换播放/暂停状态
   */
  public togglePlay(): void {
    if (this.state.isPaused) {
      this.resume();
    } else if (this.state.isPlaying) {
      this.pause();
    }
  }

  /**
   * 如果需要，开始播放音频
   * @private
   */
  private async startPlayingIfNeeded(): Promise<void> {
    if (!this.state.isStartPlayQueue) {
      await this.playAudioQueue();
    }
  }

  /**
   * 处理错误
   * @private
   */
  private handleError(error: any): void {
    console.error('语音合成错误:', error);
    this.setState({ isError: true });
    this.destroyAudioContext();
    
    // 清理队列的所有文件
    if (this.fileManager) {
      this.audioQueue.forEach(filePath => {
        try {
          this.fileManager!.unlinkSync(filePath);
        } catch (e) {
          console.warn('清理音频文件失败:', e);
        }
      });
    }
    
    this.audioQueue = [];
    throw error;
  }

  // 添加进度计算辅助方法
  private calculateProgress(): number {
    if (this.totalAudioCount === 0) return 0;
    const playedCount = this.totalAudioCount - this.audioQueue.length;
    return Math.round((playedCount / this.totalAudioCount) * 100);
  }

  /**
   * 判断是否是合适的分段点
   * @private
   */
  private isGoodSplitPoint(text: string, index: number): boolean {
    // 检查前后字符，避免切断词语
    const prevChar = text[index - 1];
    const nextChar = text[index];
    
    // 定义可以作为分段点的字符
    const splitChars = new Set([' ', '，', '、', '。', '！', '？', '；', '：']);
    
    // 如果当前位置是标点符号，可以分段
    if (splitChars.has(prevChar) || splitChars.has(nextChar)) {
        return true;
    }
    
    // 检查是否会切断英文单词
    const isPartOfEnglishWord = (char: string) => /[a-zA-Z]/.test(char);
    if (isPartOfEnglishWord(prevChar) && isPartOfEnglishWord(nextChar)) {
        return false;
    }
    
    // 检查是否会切断数字
    const isPartOfNumber = (char: string) => /[0-9]/.test(char);
    if (isPartOfNumber(prevChar) && isPartOfNumber(nextChar)) {
        return false;
    }
    
    // 其他情况，可以分段
    return true;
  }
}

export default SpeechSynthesisUtil; 