<script lang="ts" setup>
import type { PropType, Ref } from 'vue'
import { useToast } from 'vue-toast-notification'
import SubscribeEditForm from '../form/SubscribeEditForm.vue'
import { formatSeason } from '@/@core/utils/formatters'
import api from '@/api'
import { doneNProgress, startNProgress } from '@/api/nprogress'
import type { MediaInfo, NotExistMediaInfo, Subscribe, TmdbSeason } from '@/api/types'
import router from '@/router'
import noImage from '@images/no-image.jpeg'

// 输入参数
const props = defineProps({
  media: Object as PropType<MediaInfo>,
  width: String,
  height: String,
})

// 提示框
const $toast = useToast()

// 图片加载状态
const isImageLoaded = ref(false)

// 图片加载失败
const imageLoadError = ref(false)

// TMDB识别标志
const tmdbFlag = ref(true)

// 当前订阅状态
const isSubscribed = ref(false)

// 本地存在状态
const isExists = ref(false)

// 各季缺失状态：0-已存在 1-部分缺失 2-全部缺失，没有数据也是已存在
const seasonsNotExisted = ref<{ [key: number]: number }>({})

// 订阅季弹窗
const subscribeSeasonDialog = ref(false)

// 订阅编辑弹窗
const subscribeEditDialog = ref(false)

// 订阅ID
const subscribeId = ref(0)

// 季详情
const seasonInfos = ref<TmdbSeason[]>([])

// 选中的订阅季
const seasonsSelected = ref<TmdbSeason[]>([])

// 订阅弹窗选择的多季
function subscribeSeasons() {
  subscribeSeasonDialog.value = false
  seasonsSelected.value.forEach((season) => {
    addSubscribe(season.season_number)
  })
}

// 角标颜色
function getChipColor(type: string) {
  if (type === '电影')
    return 'border-blue-500 bg-blue-600'
  else if (type === '电视剧')
    return ' bg-indigo-500 border-indigo-600'
  else
    return 'border-purple-600 bg-purple-600'
}

// 添加订阅处理

async function handleAddSubscribe() {
  if (props.media?.type === '电视剧' && props.media?.tmdb_id) {
    // TMDB电视剧
    // 查询TMDB所有季信息
    await getMediaSeasons()
    if (!seasonInfos.value) {
      $toast.error(`${props.media?.title} 查询剧集信息失败！`)
      return
    }

    // 检查各季的缺失状态
    await checkSeasonsNotExists()
    if (!tmdbFlag.value)
      return

    if (seasonInfos.value.length === 1) {
      // 添加订阅
      addSubscribe(1)
    }
    else {
      // 弹出季选择列表，支持多选
      seasonsSelected.value = []
      subscribeSeasonDialog.value = true
    }
  }
  else if (props.media?.type === '电视剧') {
    // 豆瓣电视剧，只会有一季
    const season = props.media?.season ?? 1
    // 添加订阅
    addSubscribe(season)
  }
  else {
    // 电影
    addSubscribe()
  }
}

// 调用API添加订阅，电视剧的话需要指定季
async function addSubscribe(season = 0) {
  // 开始处理
  startNProgress()
  try {
    // 是否洗版
    let best_version = isExists.value ? 1 : 0
    if (season && props.media?.tmdb_id)
      // 全部存在时洗版
      best_version = !seasonsNotExisted.value[season] ? 1 : 0
    // 请求API
    const result: { [key: string]: any } = await api.post('subscribe/', {
      name: props.media?.title,
      type: props.media?.type,
      year: props.media?.year,
      tmdbid: props.media?.tmdb_id,
      doubanid: props.media?.douban_id,
      season,
      best_version,
    })

    // 订阅状态
    if (result.success) {
      // 订阅成功
      isSubscribed.value = true
    }

    // 提示
    showSubscribeAddToast(
      result.success,
      props.media?.title ?? '',
      season,
      result.message,
      best_version,
    )

    // 弹出订阅编辑弹窗
    if (result.success && seasonsSelected.value.length <= 1) {
      subscribeId.value = result.data.id
      subscribeEditDialog.value = true
    }
  }
  catch (error) {
    console.error(error)
  }
  doneNProgress()
}

// 弹出添加订阅提示
function showSubscribeAddToast(result: boolean,
  title: string,
  season: number,
  message: string,
  best_version: number) {
  if (season)
    title = `${title} ${formatSeason(season.toString())}`

  let subname = '订阅'
  if (best_version > 0)
    subname = '洗版订阅'

  if (result && seasonsSelected.value.length > 1)
    $toast.success(`${title} 添加${subname}成功！`)
  else if (!result)
    $toast.error(`${title} 添加${subname}失败：${message}！`)
}

// 调用API取消订阅
async function removeSubscribe() {
  // 开始处理
  startNProgress()
  try {
    const mediaid = props.media?.tmdb_id
      ? `tmdb:${props.media?.tmdb_id}`
      : `douban:${props.media?.douban_id}`

    const result: { [key: string]: any } = await api.delete(
      `subscribe/media/${mediaid}`,
      {
        params: {
          season: props.media?.season,
        },
      },
    )

    if (result.success) {
      isSubscribed.value = false
      $toast.success(`${props.media?.title} 已取消订阅！`)
    }
    else {
      $toast.error(`${props.media?.title} 取消订阅失败：${result.message}！`)
    }
  }
  catch (error) {
    console.error(error)
  }
  doneNProgress()
}

// 查询当前媒体是否已订阅
async function handleCheckSubscribe() {
  try {
    const result = await checkSubscribe(props.media?.season)
    if (result)
      isSubscribed.value = true
  }
  catch (error) {
    console.error(error)
  }
}

// 查询当前媒体是否已存在
async function handleCheckExists() {
  try {
    const result: { [key: string]: any } = await api.get('media/exists', {
      params: {
        tmdbid: props.media?.tmdb_id,
        title: props.media?.title,
        year: props.media?.year,
        season: props.media?.season,
        mtype: props.media?.type,
      },
    })

    if (result.success)
      isExists.value = true
  }
  catch (error) {
    console.error(error)
  }
}

// 调用API检查是否已订阅，电视剧需要指定季
async function checkSubscribe(season = 0) {
  try {
    const mediaid = props.media?.tmdb_id
      ? `tmdb:${props.media?.tmdb_id}`
      : `douban:${props.media?.douban_id}`

    const result: Subscribe = await api.get(`subscribe/media/${mediaid}`, {
      params: {
        season,
      },
    })

    return result.id || null
  }
  catch (error) {
    console.error(error)
  }

  return null
}

// 检查所有季的缺失状态
async function checkSeasonsNotExists() {
  // 开始处理
  startNProgress()
  try {
    const result: NotExistMediaInfo[] = await api.post('download/notexists', props.media)
    if (result) {
      result.forEach((item) => {
        // 0-已存在 1-部分缺失 2-全部缺失
        let state = 0
        if (item.episodes.length === 0)
          state = 2
        else if (item.episodes.length < item.total_episode)
          state = 1

        seasonsNotExisted.value[item.season] = state
      })
    }
  }
  catch (error) {
    $toast.error(`${props.media?.title}无法识别TMDB媒体信息！`)
    tmdbFlag.value = false
  }

  // 处理完成
  doneNProgress()
}

// 查询TMDB的所有季信息
async function getMediaSeasons() {
  try {
    seasonInfos.value = await api.get(`tmdb/seasons/${props.media?.tmdb_id}`)
  }
  catch (error) {
    console.error(error)
  }
}

// 爱心订阅按钮响应
function handleSubscribe() {
  if (isSubscribed.value)
    removeSubscribe()
  else
    handleAddSubscribe()
}

// 计算存在状态的颜色
function getExistColor(season: number) {
  const state = seasonsNotExisted.value[season]
  if (!state)
    return 'success'

  if (state === 1)
    return 'warning'
  else if (state === 2)
    return 'error'
  else
    return 'success'
}

// 计算存在状态的文本
function getExistText(season: number) {
  const state = seasonsNotExisted.value[season]
  if (!state)
    return '已存在'

  if (state === 1)
    return '部分缺失'
  else if (state === 2)
    return '缺失'
  else
    return '已存在'
}

// 打开详情页
function goMediaDetail() {
  router.push({
    path: '/media',
    query: {
      mediaid: `${
        props.media?.tmdb_id
          ? `tmdb:${props.media?.tmdb_id}`
          : `douban:${props.media?.douban_id}`
      }`,
      type: props.media?.type,
    },
  })
}

// 开始搜索
function handleSearch() {
  router.push({
    path: '/resource',
    query: {
      keyword: `${
        props.media?.tmdb_id
          ? `tmdb:${props.media?.tmdb_id}`
          : `douban:${props.media?.douban_id}`
      }`,
      type: props.media?.type,
      area: 'title',
    },
  })
}

// 装载时检查是否已订阅
onBeforeMount(() => {
  handleCheckSubscribe()
  handleCheckExists()
})

// 计算图片地址
const getImgUrl: Ref<string> = computed(() => {
  if (imageLoadError.value)
    return noImage
  const url = props.media?.poster_path?.replace('original', 'w500') ?? noImage
  // 如果地址中包含douban则使用中转代理
  if (url.includes('doubanio.com'))
    return `${import.meta.env.VITE_API_BASE_URL}douban/img/${encodeURIComponent(url)}`

  return url
})

// 拼装季图片地址
function getSeasonPoster(posterPath: string) {
  if (!posterPath)
    return ''
  return `https://image.tmdb.org/t/p/w500${posterPath}`
}

// 将yyyy-mm-dd转换为yyyy年mm月dd日
function formatAirDate(airDate: string) {
  if (!airDate)
    return ''
  const date = new Date(airDate)
  return `${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日`
}
// 从yyyy-mm-dd中提取年份
function getYear(airDate: string) {
  if (!airDate)
    return ''
  const date = new Date(airDate)
  return date.getFullYear()
}
</script>

<template>
  <VHover v-bind="props">
    <template #default="hover">
      <VCard
        v-bind="hover.props"
        :height="props.height"
        :width="props.width"
        class="outline-none shadow ring-gray-500 rounded-lg"
        :class="{
          'transition transform-cpu duration-300 scale-105 shadow-lg': hover.isHovering,
          'ring-1': isImageLoaded,
        }"
      >
        <VImg
          aspect-ratio="2/3"
          :src="getImgUrl"
          class="object-cover aspect-w-2 aspect-h-3"
          :class="hover.isHovering ? 'on-hover' : ''"
          cover
          @load="isImageLoaded = true"
          @error="imageLoadError = true"
        >
          <template #placeholder>
            <div class="w-full h-full">
              <VSkeletonLoader class="object-cover aspect-w-2 aspect-h-3" />
            </div>
          </template>
          <!-- 类型角标 -->
          <VChip
            v-show="isImageLoaded"
            variant="elevated"
            size="small"
            :class="getChipColor(props.media?.type || '')"
            class="absolute left-2 top-2 bg-opacity-80 shadow-md text-white font-bold"
          >
            {{ props.media?.type }}
          </VChip>
          <!-- 本地存在标识 -->
          <ExistIcon v-if="isExists" />
          <!-- 评分角标 -->
          <VChip
            v-if="isImageLoaded && props.media?.vote_average && !isExists"
            variant="elevated"
            size="small"
            :class="getChipColor('rating')"
            class="absolute right-2 top-2 bg-opacity-80 shadow-md text-white font-bold"
          >
            {{ props.media?.vote_average }}
          </VChip>
          <!-- 详情 -->
          <VCardText
            v-show="hover.isHovering || imageLoadError"
            class="w-full flex flex-col flex-wrap justify-end align-left text-white absolute bottom-0 cursor-pointer pa-2"
            @click.stop="goMediaDetail"
          >
            <span class="font-bold">{{ props.media?.year }}</span>
            <h1 class="mb-1 text-white font-extrabold text-xl line-clamp-2 overflow-hidden text-ellipsis ...">
              {{ props.media?.title }}
            </h1>
            <p class="leading-4 line-clamp-4 overflow-hidden text-ellipsis ...">
              {{ props.media?.overview }}
            </p>
            <div class="flex align-center justify-between">
              <IconBtn
                icon="mdi-magnify"
                color="white"
                @click.stop="handleSearch"
              />
              <IconBtn
                icon="mdi-heart"
                :color="isSubscribed ? 'error' : 'white'"
                @click.stop="handleSubscribe"
              />
            </div>
          </VCardText>
        </VImg>
      </VCard>
    </template>
  </VHover>
  <!-- 订阅季弹窗 -->
  <VBottomSheet
    v-model="subscribeSeasonDialog"
    inset
    scrollable
  >
    <VCard class="rounded-t">
      <DialogCloseBtn @click="subscribeSeasonDialog = false" />
      <VCardTitle class="pe-10">
        订阅 - {{ props.media?.title }}
      </VCardTitle>
      <VCardText>
        <VList
          v-model:selected="seasonsSelected"
          lines="three"
          select-strategy="classic"
        >
          <VListItem
            v-for="(item, i) in seasonInfos" :key="i"
            :value="item"
          >
            <template #prepend>
              <VImg
                height="90"
                width="60"
                :src="getSeasonPoster(item.poster_path || '')"
                aspect-ratio="2/3"
                class="object-cover rounded shadow ring-gray-500 me-3"
                cover
              >
                <template #placeholder>
                  <div class="w-full h-full">
                    <VSkeletonLoader class="object-cover aspect-w-2 aspect-h-3" />
                  </div>
                </template>
              </VImg>
            </template>
            <VListItemTitle>
              第 {{ item.season_number }} 季
            </VListItemTitle>
            <VListItemSubtitle class="mt-1 me-2">
              <VChip
                v-if="item.vote_average"
                color="primary"
                size="small"
                class="mb-1"
              >
                <VIcon icon="mdi-star" /> {{ item.vote_average }}
              </VChip>
              {{ getYear(item.air_date || '') }} • {{ item.episode_count }} 集
            </VListItemSubtitle>
            <VListItemSubtitle>
              《{{ media?.title }}》第 {{ item.season_number }} 季于 {{ formatAirDate(item.air_date || '') }} 首播。
            </VListItemSubtitle>
            <VListItemSubtitle>
              <VChip
                v-if="seasonsNotExisted"
                class="mt-2"
                size="small"
                :color="getExistColor(item.season_number || 0)"
              >
                {{ getExistText(item.season_number || 0) }}
              </VChip>
            </VListItemSubtitle>
            <template #append="{ isSelected }">
              <VListItemAction start>
                <VSwitch :model-value="isSelected" />
              </VListItemAction>
            </template>
          </VListItem>
        </VList>
      </VCardText>
      <div class="my-2 text-center">
        <VBtn
          :disabled="seasonsSelected.length === 0"
          width="30%"
          @click="subscribeSeasons"
        >
          {{ seasonsSelected.length === 0 ? '请选择订阅季' : '提交订阅' }}
        </VBtn>
      </div>
    </VCard>
  </VBottomSheet>
  <!-- 订阅编辑弹窗 -->
  <SubscribeEditForm
    v-model="subscribeEditDialog"
    :subid="subscribeId"
    @close="subscribeEditDialog = false"
    @save="subscribeEditDialog = false"
    @remove="() => { subscribeEditDialog = false; handleCheckSubscribe(); }"
  />
</template>

<style lang="scss">
.on-hover img {
  @apply brightness-50;
}
</style>
