<template>
  <div>
    <video id="video-player" class="video-js vjs-defaultskin"></video>
  </div>
</template>
<script lang="ts">
// Importing video-js
import videojs from 'video.js'
import qualityLevels from 'videojs-contrib-quality-levels'
import { createQualitySelector } from '@/assets/qualitySelector'
import { getTimeFromQuery } from '@/mixins'
import 'video.js/dist/video-js.css'

videojs.registerPlugin('qualityLevels', qualityLevels)

export default {
  props: {
    options: {
      type: Object,
      default() {
        return {}
      }
    }
  },
  emits: ['PlayerTimeUpdate'],
  setup() {
    let player: any
    let lastProgressAt = 0
    let lastKnownTime = 0
    let recoverTimer: ReturnType<typeof setInterval> | null = null
    let lastRecoverAt = 0

    return {
      player,
      lastProgressAt,
      lastKnownTime,
      recoverTimer,
      lastRecoverAt
    }
  },
  // initializing the video player
  // when the component is being mounted
  mounted() {
    const emit = this.$emit
    const isLiveStreamSource = () =>
      String(this.player?.currentSource()?.src || '').includes('/proxy/stream/')

    const tryResumePlayback = () => {
      if (!this.player || this.player.isDisposed()) {
        return
      }

      const now = Date.now()
      if (now - this.lastRecoverAt < 2500) {
        return
      }
      this.lastRecoverAt = now

      const source = this.player.currentSource()
      if (!source?.src) {
        return
      }

      const isLive = isLiveStreamSource()
      const savedTime = Number(this.player.currentTime() || 0)

      // Soft recovery first: if playback is paused/stuck, request play.
      this.player.play().catch(() => {
        // Hard recovery: reload source and restore position (for VOD/clip).
        this.player.src(source)
        this.player.one('loadedmetadata', () => {
          if (!isLive && savedTime > 0) {
            this.player.currentTime(savedTime)
          } else if (isLive && this.player.liveTracker?.seekToLiveEdge) {
            this.player.liveTracker.seekToLiveEdge()
          }

          this.player.play().catch(() => {
            // If autoplay is blocked, keep player ready; user can click play.
          })
        })
      })
    }

    const playerOptions = {
      ...this.options,
      html5: {
        ...(this.options.html5 || {}),
        vhs: {
          // Lower initial bitrate pressure and let ABR adapt quickly.
          enableLowInitialPlaylist: true,
          limitRenditionByPlayerDimensions: true,
          ...(this.options.html5?.vhs || {})
        }
      }
    }

    this.player = videojs('video-player', playerOptions, () => {
      createQualitySelector(this.player)

      if (this.$route.query['t']) {
        const timeQuery = this.$route.query['t'].toString() || ''
        const time = getTimeFromQuery(timeQuery)
        this.player.currentTime(time)
      }

      this.player.on('timeupdate', () => {
        const currentTime = Number(this.player.currentTime() || 0)
        if (currentTime !== this.lastKnownTime) {
          this.lastKnownTime = currentTime
          this.lastProgressAt = Date.now()
        }

        emit('PlayerTimeUpdate', currentTime)
      })

      this.player.on('waiting', tryResumePlayback)
      this.player.on('stalled', tryResumePlayback)
      this.player.on('error', tryResumePlayback)

      // On live streams, ad transitions/discontinuities can fire ended.
      this.player.on('ended', () => {
        if (isLiveStreamSource()) {
          tryResumePlayback()
        }
      })

      // Watchdog: if live playback stops progressing for several seconds,
      // trigger recovery even when no explicit media event fires.
      this.lastProgressAt = Date.now()
      this.recoverTimer = setInterval(() => {
        if (!this.player || this.player.isDisposed()) {
          return
        }

        if (!isLiveStreamSource()) {
          return
        }

        const stalledFor = Date.now() - this.lastProgressAt
        if (stalledFor > 8000) {
          tryResumePlayback()
        }
      }, 3000)
    })
  },
  unmounted() {
    if (this.recoverTimer) {
      clearInterval(this.recoverTimer)
      this.recoverTimer = null
    }
    this.player.dispose()
  }
}
</script>
