<template>
  <div class="video-wrapper" @click="onClick">
    <video
      ref="videoEl"
      class="video"
      :src="currentSrc"
      :poster="poster"
      :muted="isMuted"
      loop
      playsinline
      webkit-playsinline
      x5-playsinline
      x5-video-player-type="h5-page"
      preload="auto"
      @error="onVideoError"
      @loadeddata="onLoaded"
    />
    <Icon v-if="!isPlaying && !loadFailed" icon="fluent:play-28-filled" class="pause-icon" />
    <p v-if="loadFailed" class="error-tip">视频加载失败，请检查网络</p>
    <div class="float">
      <div class="normal" :style="{ opacity: state.isMove ? 0 : 1 }">
        <template v-if="!state.commentVisible">
          <ItemToolbar :item="localItem" @update:item="onItemUpdate" />
          <ItemDesc />
        </template>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { Icon } from '@iconify/vue'
import { computed, onMounted, onUnmounted, provide, reactive, ref, watch } from 'vue'
import bus, { EVENT_KEY } from '@/utils/bus'
import { SlideItemPlayStatus } from '@/utils/const_var'
import { _checkImgUrl } from '@/utils/index'
import { _css } from '@/utils/dom'
import ItemToolbar from './ItemToolbar.vue'
import ItemDesc from './ItemDesc.vue'

const props = defineProps({
  item: { type: Object, required: true },
  index: { type: Number, default: 0 },
  position: {
    type: Object,
    default: () => ({ uniqueId: 'home', index: 0 }),
  },
  isPlay: { type: Boolean, default: false },
})

const emit = defineEmits<{ 'update:item': [unknown] }>()

const videoEl = ref<HTMLVideoElement | null>(null)
const isMuted = ref(
  typeof window !== 'undefined' ? (window as Window).isMuted !== false : true,
)
const urlIndex = ref(0)
const loadFailed = ref(false)

const localItem = reactive({ ...(props.item as object) })

const state = reactive({
  status: props.isPlay ? SlideItemPlayStatus.Play : SlideItemPlayStatus.Pause,
  commentVisible: false,
  isMove: false,
})

const COMMENT_VIDEO_HEIGHT = 'calc(var(--vh, 1vh) * 30)'

function getAwemeId() {
  return (localItem as { aweme_id?: string }).aweme_id
}

function setVideoHeight(height: string, duration = '300ms') {
  const el = videoEl.value
  if (!el) return
  _css(el, 'transition-duration', duration)
  _css(el, 'height', height)
}

function onDialogMove(val?: unknown) {
  const { tag, e } = (val as { tag?: string; e?: number }) || {}
  if (!state.commentVisible || tag !== 'comment' || e === undefined) return
  state.isMove = true
  setVideoHeight(`calc(var(--vh, 1vh) * 30 + ${e}px)`, '0ms')
}

function onDialogEnd(val?: unknown) {
  const { tag, isClose } = (val as { tag?: string; isClose?: boolean }) || {}
  if (!state.commentVisible || tag !== 'comment') return
  state.isMove = false
  if (isClose) {
    state.commentVisible = false
    setVideoHeight('100%')
  } else {
    setVideoHeight(COMMENT_VIDEO_HEIGHT)
  }
}

function onOpenComments(id?: unknown) {
  const awemeId = id as string | undefined
  if (awemeId && awemeId !== getAwemeId()) return
  state.commentVisible = true
  setVideoHeight(COMMENT_VIDEO_HEIGHT)
}

function onCloseComments() {
  if (!state.commentVisible) return
  state.commentVisible = false
  state.isMove = false
  setVideoHeight('100%')
}

const playUrls = computed(() => {
  const video = (localItem as { video?: { play_addr?: { url_list?: string[] } } }).video
  return video?.play_addr?.url_list?.filter(Boolean) || []
})

const currentSrc = computed(() => playUrls.value[urlIndex.value] || '')

const poster = computed(() => {
  const video = (localItem as { video?: { cover?: { url_list?: string[] }; poster?: string } }).video
  const raw = video?.poster || video?.cover?.url_list?.[0]
  return _checkImgUrl(raw)
})

const isPlaying = computed(() => state.status === SlideItemPlayStatus.Play)

provide('item', localItem)
provide('position', computed(() => props.position))
provide('isPlaying', isPlaying)
provide('isMuted', isMuted)

function onItemUpdate(val: unknown) {
  Object.assign(localItem, val as object)
  emit('update:item', val)
}

watch(
  () => props.item,
  (v) => {
    Object.assign(localItem, v as object)
    urlIndex.value = 0
    loadFailed.value = false
  },
  { deep: true },
)

function onVideoError() {
  if (urlIndex.value < playUrls.value.length - 1) {
    urlIndex.value += 1
    loadFailed.value = false
    const el = videoEl.value
    if (el) {
      el.load()
      if (props.isPlay) play()
    }
    return
  }
  loadFailed.value = true
  state.status = SlideItemPlayStatus.Pause
}

function onLoaded() {
  loadFailed.value = false
}

function unmute() {
  isMuted.value = false
  ;(window as Window).isMuted = false
  const el = videoEl.value
  if (el) {
    el.muted = false
    el.volume = 1
  }
}

function play() {
  const el = videoEl.value
  if (!el || !currentSrc.value || loadFailed.value) return
  state.status = SlideItemPlayStatus.Play
  if (!isMuted.value) {
    el.muted = false
    el.volume = 1
  }
  el.play().catch(() => {
    state.status = SlideItemPlayStatus.Pause
  })
}

function pause() {
  const el = videoEl.value
  if (!el) return
  state.status = SlideItemPlayStatus.Pause
  el.pause()
}

function onClick() {
  unmute()
  bus.emit(EVENT_KEY.SINGLE_CLICK_BROADCAST, {
    uniqueId: props.position.uniqueId,
    index: props.position.index,
    type: EVENT_KEY.ITEM_TOGGLE,
  })
}

function onBusClick(val?: unknown) {
  const payload = val as { uniqueId?: string; index?: number; type?: string } | undefined
  if (!payload) return
  const { uniqueId, index, type } = payload
  if (props.position.uniqueId !== uniqueId || props.position.index !== index) return

  if (type === EVENT_KEY.ITEM_TOGGLE) {
    if (state.status === SlideItemPlayStatus.Play) pause()
    else play()
  }
  if (type === EVENT_KEY.ITEM_STOP) {
    const el = videoEl.value
    if (el) el.currentTime = 0
    pause()
  }
  if (type === EVENT_KEY.ITEM_PLAY) {
    const el = videoEl.value
    if (el) el.currentTime = 0
    play()
  }
}

watch(
  () => props.isPlay,
  (v) => {
    if (v) play()
    else pause()
  },
)

function onRemoveMuted() {
  unmute()
}

onMounted(() => {
  bus.on(EVENT_KEY.SINGLE_CLICK_BROADCAST, onBusClick)
  bus.on(EVENT_KEY.REMOVE_MUTED, onRemoveMuted)
  bus.on(EVENT_KEY.DIALOG_MOVE, onDialogMove)
  bus.on(EVENT_KEY.DIALOG_END, onDialogEnd)
  bus.on(EVENT_KEY.OPEN_COMMENTS, onOpenComments)
  bus.on(EVENT_KEY.CLOSE_COMMENTS, onCloseComments)
  if ((window as Window).isMuted === false) unmute()
  if (props.isPlay) play()
})

onUnmounted(() => {
  bus.off(EVENT_KEY.SINGLE_CLICK_BROADCAST, onBusClick)
  bus.off(EVENT_KEY.REMOVE_MUTED, onRemoveMuted)
  bus.off(EVENT_KEY.DIALOG_MOVE, onDialogMove)
  bus.off(EVENT_KEY.DIALOG_END, onDialogEnd)
  bus.off(EVENT_KEY.OPEN_COMMENTS, onOpenComments)
  bus.off(EVENT_KEY.CLOSE_COMMENTS, onCloseComments)
  pause()
  setVideoHeight('100%', '0ms')
})
</script>

<style scoped lang="less">
.video-wrapper {
  position: relative;
  font-size: 14rem;
  width: 100%;
  height: 100%;
  text-align: center;
  background: #000;
  overflow: hidden;

  .video {
    max-width: 100%;
    height: 100%;
    vertical-align: top;
    transition:
      height 0.3s,
      margin-top 0.3s;
  }

  .pause-icon {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    font-size: 60rem;
    color: rgba(255, 255, 255, 0.8);
    pointer-events: none;
    z-index: 1;
  }

  .error-tip {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    color: rgba(255, 255, 255, 0.7);
    font-size: 14rem;
    z-index: 1;
  }

  .float {
    position: absolute;
    left: 0;
    top: 0;
    height: 100%;
    width: 100%;
    z-index: 2;
    pointer-events: none;

    .normal {
      position: absolute;
      bottom: 0;
      width: 100%;
      transition: all 0.3s;
      pointer-events: none;
    }
  }
}
</style>
