import EventEmitter from 'eventemitter3'
// import { Dialog, Toast, Notify } from 'vant'
// import Pizzicato from 'pizzicato'
import EventEmitterType from 'eventemitter3/index.d'
// import request from 'umi-request'
// import runtime from '/src/pages/detail/runtime'

const ee = new EventEmitter()

let playStartTime: number = 0
let starttime = 0
export default class MultipleAudio {
  audios: any = {}

  audioList: string[] = []

  length: number = 0

  status: 'init' | 'play' | 'pause' = 'init'

  speed: number = 90

  muted: boolean = false

  audio: null | HTMLAudioElement = null

  event: EventEmitterType = ee

  // group = new Pizzicato.Group([])

  currentTime: number = 0

  duration: number = 0

  timer: any = null

  accelerateRefreshPlayer = () => {
    if (this.timer) {
      return
    }
    const prevTime = this.currentTime
    let now = new Date().getTime()
    this.timer = setInterval(() => {
      this.currentTime = (new Date().getTime() - now) / 1000 + prevTime
      // console.log(this.currentTime)
      this.event.emit('timeupdate', this)
    }, 10)
  }

  clearAccelerateRefreshPlayer = () => {
    clearInterval(this.timer)
    this.timer = null
  }

  constructor(list: string[]) {
    this.setSongs(list)
    // this.event.on('timeupdate', () => {
    //   console.log(this.currentTime)
    // })
  }

  async setSongs(list: string[]) {
    this.audioList = list.filter((item) => !!item).map(url => url)
    this.audio = null
    this.event.removeAllListeners()
    this.audios = {}
    const filterReqs = list
      .filter((item) => !!item)
      .map(async (url) => ({
        // bolb: await request.get(url, {responseType: 'blob'}),
        url: url,
      }))
    const res = await Promise.all(filterReqs)
    for (const item of res) {
      const audio = new Audio(item.url)
      // audio.controls = true
      // document.body.append(audio)
      audio.load()
      this.audios[item.url] = audio
      if (!this.audio) {
        this.audio = audio
      }
    }
    this.length = filterReqs.length
    if (this.audio) {
      this.audio.addEventListener('loadedmetadata', (evt) => {
        // this.duration = this.audio?.duration || 0
        // this.event.emit('loadedmetadata', evt, this.audio?.duration)
        this.setDuration()
      })
      this.audio.addEventListener('timeupdate', (evt) => {
        this.currentTime = this.audio?.currentTime || 0
        this.event.emit('timeupdate', evt)
        let used = false
        if (this.currentTime === this.duration && !used) {
          used = true
          // this.event.emit('ended')
          // this.audio?.dispatchEvent(endedEvent)
          // for (const key in this.audios) {
          //   if (Object.prototype.hasOwnProperty.call(this.audios, key)) {
          //     const audio = this.audios[key]
          //     const endedEvent = new Event('ended')
          //     audio.dispatchEvent(endedEvent)
          //   }
          // }
          // console.log('ended')
        }
      })
      // this.audio.addEventListener('ended', () => {
      //   this.setCurrentTime(0)
      //   // this.play()
      // })
    }
    if (list.length) {
      this.status = this.getStatus()
    }
    this.event.on('allWaiting', () => {
      if (this.hasWaitng()) {
        this.event.emit('waiting')
      }
    })
    this.event.on('allPlaying', () => {
      if (!this.hasWaitng()) {
        this.event.emit('playing')
      }
    })
    this.syncEvent()
  }

  setDuration(aus?: any) {
    const audios: HTMLAudioElement[] = Object.values(aus || this.audios || {})
    if (audios.length) {
      const times: number[] = []
      for (const item of audios) {
        const duration = (item as HTMLAudioElement).duration
        if (duration > 0) {
          times.push(duration)
        }
      }
      const num = Math.floor(Math.max(...times) - Math.min(...times))
      if (num >= 1) {
        console.log('该教程原音与伴奏时长超过' + num + '秒，请修改后使用')
        // Dialog.alert({
        //   message: '该教程原音与伴奏时长超过' + num + '秒，请修改后使用',
        // })
      }
      this.duration = Math.min(...times)

      if (this.duration > 0) {
        this.event?.emit('loadedmetadata', null, this.duration)
      }
    }
  }

  destroyed() {
    this.pause()
    this.event.removeAllListeners()
    this.audio = null
    // Object.values(this.audios).map(item => (item as HTMLAudioElement).remove())
    this.audios = {}
  }

  hasWaitng() {
    let status = false
    for (const audio of Object.values(this.audios)) {
      if ((audio as any).dataset.status === 'waiting') {
        status = true
        break
      }
    }
    return status
  }

  syncEvent() {
    let isEnded = false
    const play = (evt: Event) => {
      isEnded = false
      // console.log('实际延迟', new Date().getTime() - starttime)
      // console.log('开始触发play事件', new Date().getTime())
      this.event.emit('play', evt)
      if ((evt.target as HTMLAudioElement)?.src === this.audioList[0]) {
        playStartTime = new Date().getTime()
      }
      // this.play()
    }
    const pause = async (evt: Event) => {
      await this.pause()
      this.event.emit('pause', evt)
      if ((evt.target as HTMLAudioElement)?.src === this.audioList[0]) {
        const playTime = new Date().getTime() - playStartTime
        this.event.emit('updatePlayTime', playTime / 1000)
      }
    }
    const waiting = (evt: any) => {
      if (this.status === 'play') {
        evt.target.dataset.status = 'waiting'
      }
      this.event.emit('allWaiting')
    }
    const playing = (evt: any) => {
      evt.target.dataset.status = ''
      this.event.emit('allPlaying')
    }
    const ended = async (evt: Event) => {
      if (!isEnded) {
        isEnded = true
        await this.pause()
      }
      for (const key in this.audios) {
        if (Object.prototype.hasOwnProperty.call(this.audios, key)) {
          this.event.emit('ended', {
            target: this.audios[key],
          })
          // const audio = this.audios[key]
          // const endedEvent = new Event('ended')
          // audio.dispatchEvent(endedEvent)
        }
      }
      // this.event.emit('ended', evt)
    }
    // for (const audio of Object.values(this.audios)) {
    //   audio.removeEventListener('play', play)
    //   audio.removeEventListener('pause', pause)
    //   audio.removeEventListener('waiting', waiting)
    //   audio.removeEventListener('playing', playing)
    //   audio.removeEventListener('ended', ended)
    // }
    for (const audio of Object.values(this.audios)) {
      ;(audio as HTMLAudioElement).addEventListener('loadedmetadata', () => this.setDuration(this.audios))
      ;(audio as HTMLAudioElement).addEventListener('play', play)
      ;(audio as HTMLAudioElement).addEventListener('pause', pause)
      ;(audio as HTMLAudioElement).addEventListener('waiting', waiting)
      ;(audio as HTMLAudioElement).addEventListener('playing', playing)
      ;(audio as HTMLAudioElement).addEventListener('ended', ended)
    }
    // this.audio?.addEventListener('play', play)
    // this.audio?.addEventListener('pause', pause)
    // this.audio?.addEventListener('waiting', waiting)
    // this.audio?.addEventListener('playing', playing)
    // this.audio?.addEventListener('ended', ended)
  }

  getStatus() {
    return !this.audio ? 'init' : this.audio?.paused ? 'pause' : 'play'
  }

  play(delay?: number) {
    let plused = false
    if (this.getStatus() !== 'play') {
      return new Promise((resolve) => {
        setTimeout(() => {
          starttime = new Date().getTime()
          Object.values(this.audios).map(async (item: any, inedx: number) => {
            // console.log('play duration', item.duration)
            await item.play()
            if (!plused) {
              plused = true
              // console.log('starttime', starttime)
              // console.log('延迟时间', new Date().getTime() - starttime)
              // runtime.evaluatingFixTime += new Date().getTime() - starttime
            }
          })
          resolve(this.audios)
          // Promise.all(Object.values(this.audios).map(async (item: any) => await item.play()))
          //   .then(res => {
          //     this.status = this.getStatus()
          //     resolve(res)
          //     return res
          //   })
        }, delay || 100)
      })
    }
    this.status = this.getStatus()
    return Promise.resolve()
  }

  pause() {
    this.status = this.getStatus()
    return Promise.all(Object.values(this.audios).map(async (item: any) => await item.pause())).then((res) => {
      this.status = this.getStatus()
      return res
    })
  }

  setVolume = (none: boolean, cb: () => void) => {
    let timer = setInterval(() => {
      Object.values(this.audios).map((item: any) => {
        if (none) {
          item.volume -= 0.01
          if (item.volume <= 0.01) {
            item.volume = 0
            clearInterval(timer)
            cb && cb()
          }
        } else {
          item.volume += 0.01
          if (item.volume >= 1) {
            item.volume = 1
            clearInterval(timer)
            cb && cb()
          }
        }
        console.log(item.volume)
      })
    }, 16.7)
  }

  setMute(muted: boolean, url?: string) {
    if (url) {
      if (this.audios[url]) {
        // this.setVolume(muted, () => this.audios[url].muted = muted)
        this.audios[url].muted = muted
        // this.audios[url].volume = (muted ? 0 : 1)
      }
    } else {
      this.muted = muted
      // this.setVolume(muted, () => {
      Object.values(this.audios).map((item: any) => (item.muted = muted))
      // })
    }
  }

  setSpeed(speed: number, url?: string) {
    if (url) {
      if (this.audios[url]) {
        this.audios[url].playbackRate = speed
        // this.audios[url].speed = speed
      }
    } else {
      this.speed = speed
      // this.group.speed = speed
      Object.values(this.audios).map((item: any) => {
        item.playbackRate = speed
        // console.log(item.getSourceNode().playbackRate.value = speed)
        return item
      })
    }
  }

  setCurrentTime(time: number) {
    this.currentTime = time
    // if (this.status === 'play') {
    //   this.pause()
    //   this.play()
    // }
    Object.values(this.audios).map((item: any) => (item.currentTime = time))
  }

  toggleMute(url?: string) {
    if (url) {
      if (this.audios[url]) {
        this.audios[url].muted = !this.audios[url].muted
      }
    } else {
      Object.values(this.audios).map((item: any) => (item.muted = !this.muted))
      this.muted = !this.muted
    }
  }

  async togglePlay(delay?: number) {
    if (this.getStatus() === 'pause') {
      return await this.play(delay)
    } else if (this.getStatus() === 'play') {
      this.setMute(true)
      return await this.pause()
    }
  }
}

export interface IAudios {
  [key: string]: HTMLAudioElement
}

export interface IMultipleAudio {
  audios: IAudios
  audio: null | HTMLAudioElement
  status: string
  muted: boolean
  speed: number
  length: number
  event: EventEmitterType
  play(): void
  pause(): void
  setMute(muted: boolean, url?: string): void
  setSpeed(speed: number, url?: string): void
  togglePlay(): void
  toggleMute(): void
  setCurrentTime(time: number): void
  destroyed: () => void
}
