<script setup lang="ts">
import type { CourseChapterType, CourseResourceType, VideoRecordType, PlayItemType } from '../types'
import type { VideoJsPlayer, VideoJsPlayerOptions } from 'video.js'
import { throttle } from 'lodash'
import { useStorage, useElementVisibility } from '@vueuse/core'
import { Swiper, SwiperSlide } from 'swiper/vue'
import { Navigation } from 'swiper'
import 'swiper/css'
import 'swiper/css/navigation'
import { Close } from '@element-plus/icons-vue'
import AppVideoPlayer from '@/components/base/AppVideoPlayer.vue'
import { getCoursePlayInfo, getVideoRecords, uploadVideoRecords } from '../api'
import { useLog } from '@/composables/useLog'
import { useDevice } from '@/composables/useDevice'
const { mobile } = useDevice()
const log = useLog({ hasKey: true })

interface Props {
  chapterList: CourseChapterType[]
}

const props = defineProps<Props>()
const emit = defineEmits<{
  (e: 'updateResource', resource: CourseResourceType | undefined): void
  (e: 'timeupdate', time: number): void
}>()

const options = $ref<VideoJsPlayerOptions>()

const route = useRoute()
let courseId = $ref<string>('')
let chapterId = $ref<string>('')
let sectionId = $ref<string>('')
let resourceId = $ref<string>('')
let semesterId = $ref<string>('')
watchEffect(() => {
  courseId = route.query.course_id as string
  chapterId = route.query.chapter_id as string
  sectionId = route.query.section_id as string
  resourceId = route.query.resource_id as string
  semesterId = route.query.semester_id as string
})

const playerWrapperRef = ref(null)
const playerIsVisible = useElementVisibility(playerWrapperRef)
let miniPlayerClosed = $ref(false)
watchEffect(() => {
  if (playerIsVisible.value) {
    miniPlayerClosed = false
  }
})
/**
 * 视频播放器相关
 */
let src = $ref<{ src: string; type: string }>()
function changeSrc(data: PlayItemType) {
  // src = { src: data.PlayURL, type: 'application/x-mpegURL' }
  src = { src: data.PlayURL, type: 'video/mp4' }
}
// 跳过片头
const isSkip = $ref(useStorage('isSkip', false))
const skipTime = $ref<number>(6)
// 连续播放
const isAutoPlayNext = $ref(useStorage('isAutoPlayNext', false))

// 播放器ready
let isReady = $ref<boolean>(false)
let videoJsPlayer = $ref<VideoJsPlayer | null>(inject('videoJsPlayer'))
const onReady = (player: VideoJsPlayer) => {
  isReady = true
  videoJsPlayer = player
}

// 当前章
const chapter = $computed(() => {
  return props.chapterList.find(item => item.id === chapterId)
})
// 当前节
const section = $computed(() => {
  return chapter?.sections.find(item => item.id === sectionId)
})

// 当前节下的视频列表
const sectionVideoList = $computed<CourseResourceType[]>(() => {
  const list = section?.resources ?? []
  return list.filter(item => item.resource_type === 2)
})

watchEffect(() => {
  if (sectionVideoList.length) {
    const found = sectionVideoList.find(item => item.resource_id === resourceId)
    resourceId = found ? resourceId : sectionVideoList[0]?.resource_id
  }
})

// 当前资源
const resource = $computed(() => {
  return section?.resources.find(item => item.resource_id === resourceId && item.resource_type === 2)
})
watchEffect(() => {
  emit('updateResource', resource)
})
// 切换视频资源
function changeResource(data: CourseResourceType) {
  throttledFn && throttledFn.flush()
  resourceId = data.resource_id
  videoJsPlayer?.pause()
}

// 进度信息
const progress = reactive<VideoRecordType>({
  cumulative_playing_time: '',
  current_playing_time: 0,
  max_playing_time: 0,
  valid_playing_time: 0,
  watchedTimePoint: []
})

let playList = $ref<PlayItemType[]>([])
const currentPlayList = $computed<any>(() => {
  if (resource?.info.video_type === 2) {
    return [{ PlayURL: resource?.info.video_play_address }]
  }
  return playList
    .filter(item => item.StreamType === 'video' && item.Format === 'mp4')
    .map(item => {
      const DefinitionMap: Record<string, string> = { FD: '流畅', LD: '标清', SD: '高清', HD: '超清' }
      const DefinitionName = DefinitionMap[item.Definition] || item.Definition
      return { ...item, DefinitionName }
    })
})

// 获取视频信息
async function fetchInfo() {
  if (!resource?.info.source_id) throw '资源不存在'
  const res = await getCoursePlayInfo({ source_id: resource.info.source_id })
  playList = res.data.playing_list
}
// 获取视频记录
async function fetchVideoRecords() {
  if (!resource) throw '资源不存在'
  const res = await getVideoRecords({
    chapter_id: chapterId,
    course_id: courseId,
    section_id: sectionId,
    semester_id: semesterId,
    video_id: resourceId
  })
  const { detail = {} } = res.data
  progress.current_playing_time = detail.current_playing_time ? parseFloat(detail.current_playing_time) : 0
  progress.max_playing_time = detail.max_playing_time ? parseFloat(detail.max_playing_time) : 0
  progress.valid_playing_time = detail.valid_playing_time ? parseFloat(detail.valid_playing_time) : 0
  progress.watchedTimePoint = []
  console.log('info', progress.current_playing_time)
  // 日志相关
  pageLoadedTime = Math.floor(Date.now() / 1000)
  log.getKey()
}
watchEffect(async () => {
  if (!resource) return
  await fetchVideoRecords()
  try {
    await fetchInfo()
  } catch (error) {
    console.log(error)
  }
  console.log('watch', progress.current_playing_time)
  changeSrc(currentPlayList[0])
})

function setVideoInfo() {
  if (!videoJsPlayer) return
  // 上次播放结束，设置进度为0
  if (videoJsPlayer.duration() - progress.current_playing_time < 1) {
    progress.current_playing_time = 0
  }
  // 统计，增加默认时间10秒
  progress.valid_playing_time = progress.valid_playing_time || 10
  // 跳过片头
  if (isSkip) {
    // 跳过片头
    if (progress.current_playing_time < skipTime) {
      progress.current_playing_time = skipTime
    }
    // 有效时间增加跳过片头的时间
    if (!progress.valid_playing_time) {
      progress.valid_playing_time = skipTime + 10
    }
  }
  // 初始化跳转到上次观看的时间点
  videoJsPlayer.currentTime(progress.current_playing_time)
  console.log('seek', progress.current_playing_time)
  videoJsPlayer.play()
}
const throttledFn = throttle(
  () => {
    uploadVideoRecords({
      chapter_id: chapterId,
      course_id: courseId,
      section_id: sectionId,
      semester_id: semesterId,
      video_id: resourceId,
      valid_playing_time: progress.valid_playing_time,
      current_playing_time: progress.current_playing_time,
      max_playing_time: progress.max_playing_time,
      cumulative_playing_time: progress.watchedTimePoint.join(',') || progress.current_playing_time.toString()
    })
    // 清空已经上传过的观看时间点
    progress.watchedTimePoint = []
    // 日志上送
    logUpload()
  },
  5000
  // { leading: false }
)

// 页面初始化完成的时间
let pageLoadedTime = Math.floor(Date.now() / 1000)
// 日志上送
function logUpload() {
  if (!videoJsPlayer) return
  // 日志上送
  log.upload({
    event: 'video_event',
    action: 'stu_watch_action',
    data: {
      course_id: courseId,
      resource_id: resourceId,
      page_dwell_time: Math.floor(Date.now() / 1000) - pageLoadedTime,
      resource_progress: Math.min(Math.floor((progress.valid_playing_time / videoJsPlayer?.duration()) * 100), 100)
    }
  })
}

// 视频加载完成
function onLoadedData() {
  console.log('onLoadedData')
  setVideoInfo()
}

// 进度更新
function onTimeUpdate() {
  const time = Math.floor(videoJsPlayer?.currentTime() ?? 0)
  if (!time || videoJsPlayer?.paused()) return
  emit('timeupdate', time)
  console.log('onTimeUpdate', time)
  // 更新观看累计时长
  if (time !== progress.current_playing_time) {
    progress.valid_playing_time++
  }
  // 更新当前播放时间
  progress.current_playing_time = time
  // 观看的最大点
  progress.max_playing_time = Math.max(time, progress.max_playing_time)
  // 观看时间点
  const hasTimePoint = progress.watchedTimePoint.includes(time)
  !hasTimePoint && progress.watchedTimePoint.push(time)

  throttledFn()
}
let nextTimer = 0
// 播放结束
function onEnded() {
  console.log('onEnd')
  throttledFn && throttledFn.flush()
  // 3秒后自动播放下一个视频
  if (isAutoPlayNext) {
    nextTimer && window.clearTimeout(nextTimer)
    nextTimer = window.setTimeout(() => {
      const currentIndex = sectionVideoList.findIndex(item => item.resource_id === resourceId)
      const next = sectionVideoList[currentIndex + 1]
      next && changeResource(next)
    }, 1000 * 3)
  }
}
function onSeeked() {
  console.log('onSeeked')
  throttledFn && throttledFn.flush()
}
onUnmounted(() => {
  throttledFn && throttledFn.cancel()
})
</script>

<template>
  <div class="course-player-wrapper">
    <div class="course-player-box" ref="playerWrapperRef" :style="resource ? `height: 510px` : ''">
      <div class="player-box" :class="{ 'is-pinned': !playerIsVisible && !miniPlayerClosed && !mobile }">
        <AppVideoPlayer
          :options="options"
          :src="src"
          @ready="onReady"
          @loadeddata="onLoadedData"
          @timeupdate="onTimeUpdate"
          @seeked="onSeeked"
          @ended="onEnded"
          style="width: 100%; height: 100%"
          v-if="src"></AppVideoPlayer>
        <el-icon class="mini-player-close" @click="miniPlayerClosed = true">
          <Close></Close>
        </el-icon>
      </div>
    </div>
    <!-- 设置 -->
    <teleport to=".vjs-control-bar" v-if="isReady">
      <el-popover trigger="hover" effect="dark" placement="top" :teleported="false" :show-arrow="false" width="40px">
        <template #reference>
          <button class="vjs-hd-control vjs-control vjs-button" type="button">
            <span class="vjs-icon-hd"></span>
          </button>
        </template>
        <ul class="video-definition">
          <li v-for="(item, index) in currentPlayList" :key="index" :class="{ 'is-active': item.PlayURL === src.src }" @click="changeSrc(item)">
            {{ item.DefinitionName }}
          </li>
        </ul>
      </el-popover>
      <el-popover trigger="hover" effect="dark" placement="top" :teleported="false" :show-arrow="false" width="140px">
        <template #reference>
          <button class="vjs-cog-control vjs-control vjs-button" type="button">
            <span class="vjs-icon-cog"></span>
          </button>
        </template>
        <ul class="video-actions">
          <li>始终跳过片头 <el-switch size="small" v-model="isSkip"></el-switch></li>
          <li>连续播放 <el-switch size="small" v-model="isAutoPlayNext"></el-switch></li>
        </ul>
      </el-popover>
    </teleport>
    <swiper :slidesPerView="'auto'" :spaceBetween="30" navigation :modules="[Navigation]">
      <swiper-slide
        v-for="item in sectionVideoList"
        :key="item.id"
        class="video-item"
        :class="{ 'is-active': item.resource_id === resourceId }"
        @click="changeResource(item)">
        <div class="video-item-pic">
          <img :src="item.info?.cover" />
        </div>
        <p>{{ item.info.name }}</p>
      </swiper-slide>
    </swiper>
  </div>
</template>

<style lang="scss" scoped>
.course-player-wrapper {
  margin-bottom: 0.4rem;
  border-bottom: 1px solid #e2e2e2;
}
.player-box {
  position: relative;
  width: 100%;
  height: 100%;
  &.is-pinned {
    position: fixed;
    bottom: 40px;
    right: 40px;
    width: 400px;
    height: 220px;
    z-index: 9999;

    .vjs-hd-control,
    .vjs-cog-control,
    :deep(.vjs-fullscreen-control) {
      display: none;
    }
    .mini-player-close {
      display: block;
    }
  }
}
.mini-player-close {
  display: none;
  position: absolute;
  right: 5px;
  top: 5px;
  font-size: 20px;
  color: #fff;
  z-index: 1000;
  cursor: pointer;
}
.vjs-icon-hd,
.vjs-icon-cog {
  font-size: 1.8em;
}
:deep(.vjs-fullscreen-control) {
  order: 1;
}
.video-item {
  position: relative;
  margin: 0.2rem 0;
  width: 2.38rem;
  cursor: pointer;
  box-sizing: border-box;

  p {
    padding: 0 8px;
    font-size: 0.14rem;
    line-height: 30px;
    color: #333;
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
  }
  &:hover,
  &.is-active {
    .video-item-pic {
      border: 2px solid var(--main-color);
      box-shadow: 0px 3px 10px rgb(0 0 0 / 12%);
    }
    img {
      transform: scale(1.05);
    }
    p {
      color: var(--main-color);
    }
  }
  &.is-active {
    p {
      padding-left: 20px;
      background: url('@/assets/images/icon_play.png') no-repeat left center;
      background-size: 14px;
    }
  }
}
.video-item-pic {
  width: 100%;
  height: 1.48rem;
  border-radius: 10px;
  box-sizing: border-box;
  overflow: hidden;
  img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: all 0.25s ease-in-out;
  }
}

.video-definition {
  font-size: 12px;
  li {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0.2em 0;
    font-size: 1.2em;
    line-height: 1.4em;
    &:hover {
      background: rgba(115, 133, 159, 0.5);
    }
    &.is-active {
      color: #2b333f;
      background-color: #fff;
    }
  }
}
.video-actions {
  font-size: 12px;
  li {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.2em 5px;
    font-size: 1.2em;
    line-height: 1.4em;
  }
}
:deep(.el-popover) {
  min-width: auto;
  background-color: rgba(43, 51, 63, 0.7);
  border: 0;
  border-radius: 0;
  padding: 0;
  margin-bottom: -12px !important;
}
:deep(.swiper-button-next),
:deep(.swiper-button-prev) {
  margin-top: -30px;
  --swiper-navigation-size: 14px;
  width: 0.34rem;
  height: 0.34rem;
  color: #fff;
  background: #303133;
  border-radius: 50%;
}
</style>
