export interface AudioChunkPlayer {
  addChunk(sort: number, data: string): void
  play(): void
  pause(): void
  stop(): void
  onPlaybackEnd(callback: () => void): void
  onPlaybackStart(callback: () => void): void
  isPlaying(): boolean
  getDuration(): number
  getCurrentTime(): number
  destroy(): void
  getAllAudioChunks(): AudioChunk[]
}

export interface AudioChunk {
  sort: number
  data: string
  audioBuffer?: AudioBuffer
  startTime?: number
}

class AudioChunkPlayerImpl implements AudioChunkPlayer {
  private audioContext: AudioContext | null = null
  private chunks: Map<number, AudioChunk> = new Map()
  private audioBuffers: AudioBuffer[] = []
  private sourceNodes: AudioBufferSourceNode[] = []
  private isPlayingState = false
  private isPaused = false
  private startTime = 0
  private pauseTime = 0
  private currentTime = 0
  private duration = 0
  private playbackEndCallbacks: (() => void)[] = []
  private playbackStartCallbacks: (() => void)[] = []
  private scheduledChunks: Set<number> = new Set()
  private completedChunks: Set<number> = new Set()

  constructor() {
    this.initializeAudioContext()
  }

  private async initializeAudioContext() {
    try {
      // 创建 AudioContext
      this.audioContext = new (window.AudioContext || (window as any).webkitAudioContext)()

      // 如果 AudioContext 被暂停，尝试恢复
      if (this.audioContext.state === 'suspended') {
        await this.audioContext.resume()
      }
    } catch (error) {
      console.error('Failed to initialize AudioContext:', error)
    }
  }

  private async ensureAudioContext() {
    if (!this.audioContext) {
      await this.initializeAudioContext()
    }

    if (this.audioContext?.state === 'suspended') {
      await this.audioContext.resume()
    }
  }

  addChunk(sort: number, data: string): void {
    const chunk: AudioChunk = {
      sort,
      data,
    }

    this.chunks.set(sort, chunk)

    // 如果正在播放，立即解码并调度播放
    if (this.isPlayingState && !this.isPaused) {
      this.decodeAndScheduleChunk(chunk)
    }
  }

  private async decodeAndScheduleChunk(chunk: AudioChunk): Promise<void> {
    if (!this.audioContext || this.scheduledChunks.has(chunk.sort)) {
      return
    }

    try {
      await this.ensureAudioContext()

      console.log('decodeAndScheduleChunk', chunk.sort)
      // 解码 base64 音频
      const audioData = this.base64ToArrayBuffer(chunk.data)
      const audioBuffer = await this.audioContext.decodeAudioData(audioData)

      chunk.audioBuffer = audioBuffer
      this.scheduledChunks.add(chunk.sort)

      // 计算这个片段的开始时间
      const chunkStartTime = this.calculateChunkStartTime(chunk.sort)
      chunk.startTime = chunkStartTime

      // 创建音频源节点
      const sourceNode = this.audioContext.createBufferSource()
      sourceNode.buffer = audioBuffer
      sourceNode.connect(this.audioContext.destination)

      // 调度播放
      sourceNode.start(chunkStartTime)
      this.sourceNodes.push(sourceNode)

      // 更新总时长
      this.updateDuration()

      // 监听播放结束
      sourceNode.onended = () => {
        this.handleChunkEnded(chunk.sort)
      }
    } catch (error) {
      console.error(`Failed to decode chunk ${chunk.sort}:`, error)
    }
  }

  private base64ToArrayBuffer(base64: string): ArrayBuffer {
    const binaryString = atob(base64)
    const bytes = new Uint8Array(binaryString.length)
    for (let i = 0; i < binaryString.length; i++) {
      bytes[i] = binaryString.charCodeAt(i)
    }
    return bytes.buffer
  }

  private calculateChunkStartTime(index: number): number {
    if (!this.audioContext) return 0

    const currentTime = this.audioContext.currentTime
    let startTime = currentTime

    // 如果是第一个片段，立即开始
    if (index === 0) {
      return startTime
    }

    // 计算前面所有片段的累计时长
    let totalDuration = 0
    for (let i = 0; i < index; i++) {
      const chunk = this.chunks.get(i)
      if (chunk?.audioBuffer) {
        totalDuration += chunk.audioBuffer.duration
      }
    }

    return startTime + totalDuration
  }

  private updateDuration(): void {
    this.duration = 0
    this.chunks.forEach((chunk) => {
      if (chunk.audioBuffer) {
        this.duration += chunk.audioBuffer.duration
      }
    })
    console.log('updateDuration', this.duration)
  }

  private handleChunkEnded(index: number): void {
    // 标记这个片段为已完成
    this.completedChunks.add(index)

    // 检查是否所有已调度的片段都播放完成
    const allScheduledChunksCompleted = Array.from(this.scheduledChunks).every((chunkIndex) => {
      return this.completedChunks.has(chunkIndex)
    })

    // 如果所有已调度的片段都完成了，并且没有更多片段在等待，则结束播放
    if (allScheduledChunksCompleted && this.isPlayingState) {
      // 检查是否还有未调度的片段
      const hasUnscheduledChunks = Array.from(this.chunks.keys()).some((chunkIndex) => {
        return !this.scheduledChunks.has(chunkIndex)
      })

      // 如果没有未调度的片段，则结束播放
      if (!hasUnscheduledChunks) {
        this.handlePlaybackEnd()
      }
    }
  }

  private handlePlaybackEnd(): void {
    this.isPlayingState = false
    this.isPaused = false
    this.currentTime = 0

    // 调用结束回调
    this.playbackEndCallbacks.forEach((callback) => callback())
  }

  async play(): Promise<void> {
    if (this.isPlayingState && !this.isPaused) {
      return
    }

    await this.ensureAudioContext()

    if (this.isPaused) {
      // 恢复播放
      this.isPaused = false
      this.startTime = this.audioContext!.currentTime - this.pauseTime
    } else {
      // 开始播放
      this.isPlayingState = true
      this.startTime = this.audioContext!.currentTime
      this.pauseTime = 0

      // 调用开始回调
      this.playbackStartCallbacks.forEach((callback) => callback())

      // 调度所有已解码的片段
      const sortedChunks = Array.from(this.chunks.values()).sort((a, b) => a.sort - b.sort)
      for (const chunk of sortedChunks) {
        if (chunk.audioBuffer && !this.scheduledChunks.has(chunk.sort)) {
          await this.decodeAndScheduleChunk(chunk)
        }
      }
    }
  }

  pause(): void {
    if (!this.isPlayingState || this.isPaused) {
      return
    }

    this.isPaused = true
    this.pauseTime = this.audioContext ? this.audioContext.currentTime - this.startTime : 0

    // 停止所有音频源
    this.sourceNodes.forEach((node) => {
      try {
        node.stop()
      } catch (error) {
        // 忽略已经停止的节点错误
      }
    })
    this.sourceNodes = []
  }

  stop(): void {
    this.isPlayingState = false
    this.isPaused = false
    this.currentTime = 0
    this.pauseTime = 0

    // 停止所有音频源
    this.sourceNodes.forEach((node) => {
      try {
        node.stop()
      } catch (error) {
        // 忽略已经停止的节点错误
      }
    })
    this.sourceNodes = []
    this.scheduledChunks.clear()
    this.completedChunks.clear()
  }

  onPlaybackEnd(callback: () => void): void {
    this.playbackEndCallbacks.push(callback)
  }

  onPlaybackStart(callback: () => void): void {
    this.playbackStartCallbacks.push(callback)
  }

  isPlaying(): boolean {
    return this.isPlayingState && !this.isPaused
  }

  getDuration(): number {
    return this.duration
  }

  getCurrentTime(): number {
    if (!this.audioContext) return 0

    if (this.isPaused) {
      return this.pauseTime
    }

    if (this.isPlayingState) {
      return this.audioContext.currentTime - this.startTime
    }

    return 0
  }

  getAllAudioChunks(): AudioChunk[] {
    return Array.from(this.chunks.values()).sort((a, b) => a.sort - b.sort)
  }

  destroy(): void {
    this.stop()

    if (this.audioContext) {
      this.audioContext.close()
      this.audioContext = null
    }

    this.chunks.clear()
    this.audioBuffers = []
    this.sourceNodes = []
    this.playbackEndCallbacks = []
    this.playbackStartCallbacks = []
    this.scheduledChunks.clear()
    this.completedChunks.clear()
  }
}

// 工厂函数
export function createAudioChunkPlayer(): AudioChunkPlayer {
  return new AudioChunkPlayerImpl()
}
