<!-- src/views/Ranking/Overall/Ranking.vue -->
<template>
  <div
    :class="['fullPage', 'scrollY']"
    :style="{ backgroundColor: currentRankingConfig.backgroundColor }"
  >
    <div v-if="isLoading" class="loading-container">
      <LoadingContent>{{ t('loading') }}...</LoadingContent>
    </div>

    <div v-else style="position: relative; z-index: 4">
      <BackgroundLayer
        :useCanvas="true"
        :backgroundImages="[currentBgImage]"
        :layerStyle="{ zIndex: -1 }"
      />

      <div ref="sentinel"></div>

      <div
        ref="stickyHeader"
        style="
          position: sticky;
          top: 0;
          z-index: 999;
          border-radius: 0 0 12px 12px;
          transition: all 0.3s;
          padding-bottom: 1vw;
        "
      >
        <div style="height: 13vw"></div>

        <div style="padding: 4px 8%">
          <div style="display: flex; justify-content: space-around; position: relative">
            <div
              v-for="tab in rankingTabs"
              :key="tab.value"
              class="tab"
              :style="{ color: rankingType === tab.value ? tab.activeColor : '#fff' }"
              @click="changeType(tab.value)"
            >
              {{ t(tab.labelKey) }}
            </div>

            <img
              v-smart-img
              src="../../../assets/icon/Aslan/arrowBack.png"
              alt=""
              style="position: absolute; left: -4%; top: 45%; transform: translateY(-50%)"
              width="6%"
              @click="closePage"
            />
          </div>
        </div>

        <div
          style="
            margin: 1vw 6vw 4vw;
            padding: 4px;
            border-radius: 32px;
            border: 1px solid #b2a67d;
            backdrop-filter: blur(32px);
            display: grid;
            grid-template-columns: repeat(3, 1fr);
          "
          :style="{ background: currentRankingConfig.timeBackground }"
        >
          <div
            v-for="tab in timeTabs"
            :key="tab.value"
            class="tabItem"
            :style="selectedTimeTab === tab.value ? selectedTimeTabStyle : undefined"
          >
            <div
              class="tag"
              :class="{ tagActive: selectedTimeTab === tab.value }"
              @click="changeDateType(tab.value)"
            >
              {{ t(tab.labelKey) }}
            </div>
          </div>
        </div>
      </div>

      <div>
        <div style="margin: 0 10px">
          <div style="display: flex; justify-content: center">
            <TopUser
              :Type="rankingType"
              :rank="1"
              :isTopOne="true"
              :BorderImgUrl="topBorderUrls[0]"
              :avatarUrl="topUsers[0].avatar || ''"
              :name="topUsers[0].nickname || ''"
              :distributionValue="topUsers[0].quantityFormat || ''"
              style="width: 85%"
              @click="gotoAppPage(topUsers[0].id)"
            />
          </div>

          <div style="display: flex; justify-content: space-between; margin-top: 0">
            <TopUser
              v-for="user in sideTopUsers"
              :key="user.key"
              :Type="rankingType"
              :rank="user.rank"
              :BorderImgUrl="topBorderUrls[user.rank - 1]"
              :avatarUrl="user.avatar || ''"
              :name="user.nickname || ''"
              :distributionValue="user.quantityFormat || ''"
              style="width: 43%"
              @click="gotoAppPage(user.id)"
            />
          </div>
        </div>

        <div style="padding: 3vw 3vw 0; display: flex; flex-direction: column; gap: 2vw">
          <itemCenter
            v-for="(userInfo, index) in rankingFromTop4"
            :key="userInfo.id"
            style="min-height: 19vw"
            :imgUrl="currentRankingConfig.rankingItemBg"
            :contentStyle="`padding: 3vw;justify-content: space-between;gap: 8px;`"
            :lazy="true"
            :immediate="index < 10"
            @click="gotoAppPage(userInfo.id)"
          >
            <div
              style="
                flex: 1;
                min-width: 0;
                align-self: stretch;
                display: flex;
                justify-content: space-around;
                align-items: center;
                gap: 8px;
              "
            >
              <div
                style="
                  width: auto;
                  min-width: 0;
                  align-self: stretch;
                  display: flex;
                  justify-content: center;
                  align-items: center;
                "
              >
                <div style="font-size: 1.2em; font-weight: 700">{{ index + 4 }}</div>
              </div>

              <img
                :src="userInfo.avatar || ''"
                alt=""
                style="
                  display: block;
                  width: 13vw;
                  aspect-ratio: 1/1;
                  border-radius: 50%;
                  object-fit: cover;
                "
                @error="handleAvatarImageError"
              />

              <div
                style="
                  flex: 1;
                  min-width: 0;
                  padding: 0 4px;
                  font-weight: 700;
                  overflow: hidden;
                  white-space: nowrap;
                  text-overflow: ellipsis;
                "
              >
                {{ userInfo.nickname }}
              </div>
            </div>

            <div
              style="
                width: auto;
                min-width: 0;
                align-self: stretch;
                display: flex;
                align-items: center;
              "
            >
              <img
                v-smart-img
                src="/src/assets/icon/Aslan/coin.png"
                alt=""
                style="display: block; width: 1.3em; aspect-ratio: 1/1"
              />
              <div style="font-weight: 500; color: rgba(248, 182, 45, 1)">
                {{ userInfo.quantityFormat }}
              </div>
            </div>
          </itemCenter>

          <div ref="RankingLoadmore" v-show="showRankingLoading"></div>
        </div>
      </div>
    </div>

    <div v-if="!isLoading" style="height: 22vw"></div>

    <itemCenter
      v-if="!isLoading"
      style="min-height: 15vw; position: fixed; bottom: 0; z-index: 999"
      :imgUrl="currentRankingConfig.myRankingBg"
      :contentStyle="`padding:2vw 4vw 0;justify-content: space-between;gap: 8px;`"
    >
      <div
        style="
          flex: 1;
          min-width: 0;
          align-self: stretch;
          display: flex;
          justify-content: space-around;
          align-items: center;
          gap: 8px;
        "
      >
        <div
          style="
            width: auto;
            min-width: 0;
            align-self: stretch;
            display: flex;
            justify-content: center;
            align-items: center;
          "
        >
          <div style="font-size: 1.2em; font-weight: 700">{{ myRanking.rank || 999 }}</div>
        </div>

        <img
          :src="myRanking.avatar || ''"
          alt=""
          style="
            display: block;
            width: 15vw;
            aspect-ratio: 1/1;
            border-radius: 50%;
            object-fit: cover;
          "
          @error="handleAvatarImageError"
        />

        <div
          style="
            flex: 1;
            min-width: 0;
            font-weight: 700;
            padding: 0 4px;
            overflow: hidden;
            white-space: nowrap;
            text-overflow: ellipsis;
          "
        >
          {{ myRanking.nickname || '' }}
        </div>
      </div>

      <div
        style="width: auto; min-width: 0; align-self: stretch; display: flex; align-items: center"
      >
        <img
          v-smart-img
          src="/src/assets/icon/Aslan/coin.png"
          alt=""
          style="display: block; width: 1.3em; aspect-ratio: 1/1"
        />
        <div style="font-weight: 500; color: rgba(248, 182, 45, 1)">
          {{ myRanking.quantityFormat || 0 }}
        </div>
      </div>
    </itemCenter>
  </div>
</template>

<script setup>
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
import { useRoute } from 'vue-router'
import { useI18n } from 'vue-i18n'
import { isInApp, closePage, viewUserInfo, gotoRoom } from '@/utils/appBridge.js'
import { connectToApp } from '@/utils/appConnector.js'
import { preloadImages } from '@/utils/image/imagePreloader'
import { getWebpUrl } from '@/config/imagePaths.js'
import { toCssBackgroundImage } from '@/utils/protectedAssets.js'
import { handleAvatarImageError } from '@/utils/image/imageHandler.js'
import { useThrottle } from '@/utils/useDebounce'

import { getWealthRanking, getCharmRanking, getRoomRanking, getMyRanking } from '@/api/ranking'

import BackgroundLayer from '@/components/BackgroundLayer.vue'
import itemCenter from '@/components/itemCenter.vue'
import TopUser from './components/topUser.vue'

const webpUrl = (filename) => getWebpUrl('Ranking/Overall/webp/', filename)

const RANKING_CONFIG = {
  Wealth: {
    timeBackground:
      'linear-gradient(97deg, rgba(21, 50, 22, 0.10) 12.03%, rgba(33, 99, 40, 0.10) 100.37%), linear-gradient(90deg, #0D0D0D 0%, #232323 100%)',
    labelKey: 'ranking_wealth',
    activeColor: '#60FF83',
    backgroundColor: '#242A2A',
    backgroundImage: webpUrl('bgWealth'),
    paramsType: 'GIFTS_SEND',
    topBorders: [webpUrl('top1-wealth'), webpUrl('top2-wealth'), webpUrl('top3-wealth')],
    rankingItemBg: webpUrl('RankingItem-wealth'),
    myRankingBg: webpUrl('myRankingBg-wealth'),
  },
  Room: {
    timeBackground: 'linear-gradient(97deg, #211532 12.03%, #3C2163 100.37%)',
    labelKey: 'ranking_room',
    activeColor: '#AA60FF',
    backgroundColor: '#2B2022',
    backgroundImage: webpUrl('bgRoom'),
    paramsType: 'ROOM_GIFTS',
    topBorders: [webpUrl('top1-room'), webpUrl('top2-room'), webpUrl('top3-room')],
    rankingItemBg: webpUrl('RankingItem-room'),
    myRankingBg: webpUrl('myRankingBg-room'),
  },
  Charm: {
    timeBackground: 'linear-gradient(97deg, #322215 12.03%, #633421 100.37%)',
    labelKey: 'ranking_charm',
    activeColor: '#FF9A60',
    backgroundColor: '#3E1308',
    backgroundImage: webpUrl('bgCharm'),
    paramsType: 'GIFTS_RECEIVED',
    topBorders: [webpUrl('top1-charm'), webpUrl('top2-charm'), webpUrl('top3-charm')],
    rankingItemBg: webpUrl('RankingItem-charm'),
    myRankingBg: webpUrl('myRankingBg-charm'),
  },
}

const TIME_TABS = [
  { value: 'Daily', labelKey: 'ranking_daily', paramsDateType: 'DAY', responseKey: 'daily' },
  { value: 'Weekly', labelKey: 'ranking_weekly', paramsDateType: 'WEEK', responseKey: 'weekly' },
  {
    value: 'Monthly',
    labelKey: 'ranking_monthly',
    paramsDateType: 'MONTH',
    responseKey: 'monthly',
  },
]

const RANKING_API_MAP = {
  Wealth: getWealthRanking,
  Charm: getCharmRanking,
  Room: getRoomRanking,
}

const DEFAULT_TOP_USER = {
  id: '',
  avatar: '',
  nickname: '',
  quantityFormat: '',
}

const route = useRoute()
const { t } = useI18n()

const normalizeRankingType = (value) => {
  return Object.hasOwn(RANKING_CONFIG, value) ? value : 'Wealth'
}

const rankingType = ref(normalizeRankingType(route.query.first))
const selectedTimeTab = ref('Daily')
const totalRanking = ref({})
const showRanking = ref([])
const myRanking = ref({})
const isInAppEnvironment = ref(false)
const isLoading = ref(true)
const showRankingLoading = ref(false)
const loadedCount = ref(10)
const paramsDateType = ref('DAY')

const sentinel = ref(null)
const stickyHeader = ref(null)
const RankingLoadmore = ref(null)

const rankingTabs = Object.entries(RANKING_CONFIG).map(([value, config]) => ({
  value,
  labelKey: config.labelKey,
  activeColor: config.activeColor,
}))

const timeTabs = TIME_TABS
const tabSelectedBg = webpUrl('tabSelectedBg')

const currentRankingConfig = computed(() => {
  return RANKING_CONFIG[rankingType.value]
})

const currentBgImage = computed(() => {
  return currentRankingConfig.value.backgroundImage
})

const topBorderUrls = computed(() => {
  return currentRankingConfig.value.topBorders
})

const selectedTimeTabStyle = computed(() => {
  return {
    backgroundImage: toCssBackgroundImage(tabSelectedBg),
    backgroundSize: '100% 100%',
    backgroundRepeat: 'no-repeat',
    backgroundPosition: 'center',
  }
})

const rankingTop3 = computed(() => {
  const topUsers = showRanking.value.slice(0, 3)

  while (topUsers.length < 3) {
    topUsers.push({ ...DEFAULT_TOP_USER })
  }

  return topUsers
})

const topUsers = computed(() => {
  return rankingTop3.value
})

const sideTopUsers = computed(() => {
  return rankingTop3.value.slice(1, 3).map((user, index) => ({
    ...user,
    rank: index + 2,
    key: user.id || `top-${index + 2}`,
  }))
})

const rankingFromTop4 = computed(() => {
  return showRanking.value.slice(3, loadedCount.value + 3)
})

const remainingRankingCount = computed(() => {
  return Math.max(showRanking.value.length - 3, 0)
})

const currentTimeTabConfig = computed(() => {
  return TIME_TABS.find((tab) => tab.value === selectedTimeTab.value) || TIME_TABS[0]
})

const preloadCriticalImages = async () => {
  const criticalImages = [
    tabSelectedBg,
    ...Object.values(RANKING_CONFIG).flatMap((config) => [
      config.backgroundImage,
      ...config.topBorders,
      config.rankingItemBg,
      config.myRankingBg,
    ]),
  ]

  await preloadImages(criticalImages)
}

const resetLoadedState = () => {
  loadedCount.value = 10
  showRankingLoading.value = false
}

const syncLoadMoreState = () => {
  showRankingLoading.value = remainingRankingCount.value > loadedCount.value
}

const updateTimeRanking = () => {
  const rankingList = totalRanking.value[currentTimeTabConfig.value.responseKey] || []
  showRanking.value = rankingList
  resetLoadedState()
  syncLoadMoreState()
}

const fetchRanking = async () => {
  const requestRanking = RANKING_API_MAP[rankingType.value]
  const response = await requestRanking()

  if (response.status && response.body) {
    totalRanking.value = response.body
    updateTimeRanking()
  } else {
    totalRanking.value = {}
    showRanking.value = []
    syncLoadMoreState()
  }
}

const fetchMyRanking = async () => {
  const response = await getMyRanking(currentRankingConfig.value.paramsType, paramsDateType.value)

  myRanking.value = response.status && response.body ? response.body : {}
}

const initData = async () => {
  await Promise.all([fetchRanking(), fetchMyRanking()])
}

const gotoAppPage = (id) => {
  if (!id || !isInAppEnvironment.value) {
    return
  }

  if (rankingType.value === 'Room') {
    gotoRoom(id)
    return
  }

  viewUserInfo(id)
}

const loadMoreRanking = () => {
  if (!showRankingLoading.value) {
    return
  }

  loadedCount.value = Math.min(loadedCount.value + 10, remainingRankingCount.value)
  syncLoadMoreState()
}

const stickyObserver = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (!stickyHeader.value) {
      return
    }

    stickyHeader.value.classList.toggle('stuck', !entry.isIntersecting)
  })
})

const loadMoreObserver = new IntersectionObserver(
  (entries) => {
    entries.forEach((entry) => {
      if (entry.isIntersecting) {
        loadMoreRanking()
      }
    })
  },
  {
    rootMargin: '100px',
  },
)

const registerViewportObservers = async () => {
  await nextTick()

  stickyObserver.disconnect()
  loadMoreObserver.disconnect()

  if (sentinel.value) {
    stickyObserver.observe(sentinel.value)
  }

  if (RankingLoadmore.value && showRankingLoading.value) {
    loadMoreObserver.observe(RankingLoadmore.value)
  }
}

const completePreloading = async () => {
  try {
    await Promise.all([initData(), preloadCriticalImages()])
  } catch (error) {
    console.error('preload ranking overall assets failed:', error)
  } finally {
    isLoading.value = false
  }
}

const connectToAppHandler = async () => {
  await connectToApp(() => {
    completePreloading()
  })
}

const handleChangeType = async (type) => {
  if (rankingType.value === type) {
    return
  }

  rankingType.value = type
  selectedTimeTab.value = 'Daily'
  paramsDateType.value = 'DAY'
  totalRanking.value = {}
  showRanking.value = []
  myRanking.value = {}
  resetLoadedState()

  await Promise.all([fetchRanking(), fetchMyRanking()])
}

const handleChangeDateType = async (tab) => {
  if (selectedTimeTab.value === tab) {
    return
  }

  const timeTab = TIME_TABS.find((item) => item.value === tab)

  if (!timeTab) {
    return
  }

  selectedTimeTab.value = timeTab.value
  paramsDateType.value = timeTab.paramsDateType
  showRanking.value = []
  myRanking.value = {}
  updateTimeRanking()
  await fetchMyRanking()
}

const changeType = useThrottle(handleChangeType, 1000)
const changeDateType = useThrottle(handleChangeDateType, 500)

onMounted(() => {
  connectToAppHandler()
  isInAppEnvironment.value = isInApp()
})

watch(isLoading, async (newValue) => {
  if (!newValue) {
    await registerViewportObservers()
  }
})

watch(showRankingLoading, async () => {
  if (!isLoading.value) {
    await registerViewportObservers()
  }
})

onUnmounted(() => {
  stickyObserver.disconnect()
  loadMoreObserver.disconnect()
})
</script>

<style lang="scss" scoped>
* {
  color: #fff;
}

.fullPage {
  width: 100vw;
  height: 100vh;
  overflow-y: auto;
  position: relative;
  background: #000;
}

.scrollY::-webkit-scrollbar {
  display: none;
}

.tab {
  font-weight: 700;
  font-size: 1em;
}

.tabItem {
  display: flex;
  justify-content: center;
  align-items: center;
}

.tag {
  font-weight: 510;
  padding: 4px 10px;
}

.tagActive {
  color: black;
  display: flex;
  justify-content: center;
  align-items: center;
}

.stuck {
  background-color: rgba(255, 255, 255, 0.1);
  backdrop-filter: blur(32px);
}

.loading-container {
  width: 100vw;
  height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  color: #666;
  overflow: hidden;
  position: relative;
}

@media screen and (max-width: 360px) {
  * {
    font-size: 10px;
  }
}

@media screen and (min-width: 360px) {
  * {
    font-size: 14px;
  }
}

@media screen and (min-width: 768px) {
  * {
    font-size: 24px;
  }
}

@media screen and (min-width: 1024px) {
  * {
    font-size: 32px;
  }
}
</style>
