﻿<template>
  <div class="fullPage">
    <!-- 页面同步预加载：首屏关键图片加载完成前展示 loading -->
    <div
      v-if="isLoading"
      style="display: flex; min-height: 100vh; align-items: center; justify-content: center"
    >
      <LoadingContent>{{ t('loading') }}...</LoadingContent>
    </div>

    <div
      v-else
      class="scroll"
      :dir="isRtlLayout ? 'rtl' : 'ltr'"
      style="min-height: 100vh; position: relative; overflow-x: hidden"
    >
      <!-- 页面主背景：背景图内包含活动标题和顶部视觉信息 -->
      <BackgroundLayer :backgroundImages="[imageUrl('bg')]" />

      <main
        style="
          position: relative;
          z-index: 1;
          width: min(100vw, 480px);
          min-height: 100vh;
          margin: 0 auto;
          padding: 100vw 0 12vw;
        "
      >
        <!-- 规则入口：默认贴右，阿语 RTL 时通过样式贴左 -->
        <section class="rule-row">
          <itemCenter
            :imgUrl="imageUrl('ruleBtBg')"
            style="width: 20vw; flex: 0 0 20vw"
            contentStyle="padding: 4vw 0 4vw 3vw;"
            @click="openRulePopup"
          >
            <span
              style="
                color: #fff8d7;
                font-size: 1em;
                font-weight: 800;
                line-height: 1;
                text-align: center;
                text-shadow: 0 1px 2px rgba(63, 20, 57, 0.5);
                white-space: nowrap;
              "
            >
              {{ t('rule') }}
            </span>
          </itemCenter>
        </section>

        <!-- 倒计时：天/时/分/秒分别用 timeBg 承载，时分秒之间显示英文冒号 -->
        <section style="direction: ltr; display: flex; justify-content: center; margin-bottom: 4vw">
          <div
            style="
              width: min(95vw, 440px);
              display: flex;
              align-items: center;
              justify-content: space-between;
              gap: 1vw;
              padding: 0 2vw;
              overflow: hidden;
            "
          >
            <itemCenter
              class="countdown-cell"
              :imgUrl="imageUrl('timeBg')"
              contentStyle="padding: 1.8vw 1vw;"
            >
              <span class="countdown-value">{{ Days }}D</span>
            </itemCenter>
            <span class="countdown-colon"></span>
            <itemCenter
              class="countdown-cell"
              :imgUrl="imageUrl('timeBg')"
              contentStyle="padding: 1.8vw 1vw;"
            >
              <span class="countdown-value">{{ Hours }}</span>
            </itemCenter>
            <span class="countdown-colon">:</span>
            <itemCenter
              class="countdown-cell"
              :imgUrl="imageUrl('timeBg')"
              contentStyle="padding: 1.8vw 1vw;"
            >
              <span class="countdown-value">{{ Minutes }}</span>
            </itemCenter>
            <span class="countdown-colon">:</span>
            <itemCenter
              class="countdown-cell"
              :imgUrl="imageUrl('timeBg')"
              contentStyle="padding: 1.8vw 1vw;"
            >
              <span class="countdown-value">{{ Seconds }}</span>
            </itemCenter>
          </div>
        </section>

        <!-- 一级标签：CollectCards / Personal Rank / Room Rank -->
        <section
          style="
            display: grid;
            grid-template-columns: repeat(3, minmax(0, 1fr));
            align-items: stretch;
            gap: 2vw;
            margin: 0 0 4vw;
          "
        >
          <button
            v-for="tab in visibleTopTabs"
            :key="tab.value"
            type="button"
            class="image-tab top-tab"
            :class="{ 'image-tab-active': activeTopTab === tab.value }"
            @click="changeTopTab(tab.value)"
          >
            <itemCenter
              class="tab-background"
              :imgUrl="imageUrl('tagBg')"
              contentStyle="padding: 4vw 4vw 0;"
            >
              <img
                v-show="activeTopTab === tab.value"
                v-smart-img.noFade
                :src="imageUrl('tagActiveBg')"
                class="tab-active-bg"
                alt=""
              />
              <span style="position: relative; z-index: 1; font-weight: 700">{{
                t(tab.label)
              }}</span>
            </itemCenter>
          </button>
        </section>

        <!-- 标签内容区：根据当前标签逐步展示对应业务模块 -->
        <section style="min-height: 86vw; padding: 0; margin: 0">
          <div v-if="currentTopSection">
            <!-- 二级标签：个人榜和房间榜下的 Reward / Ranking -->
            <div
              v-if="visibleChildTabs.length"
              style="
                display: grid;
                grid-template-columns: repeat(2, minmax(0, 1fr));
                gap: 3vw;
                padding: 0 5vw;
                margin-bottom: 4vw;
              "
            >
              <button
                v-for="tab in visibleChildTabs"
                :key="tab.value"
                type="button"
                class="image-tab child-tab"
                :class="{ 'image-tab-active': activeChildTab === tab.value }"
                @click="changeChildTab(tab.value)"
              >
                <itemCenter
                  class="tab-background"
                  :imgUrl="imageUrl('moduleBg')"
                  contentStyle="padding: 1vw 4vw 0;"
                >
                  <img
                    v-show="activeChildTab === tab.value"
                    v-smart-img.noFade
                    :src="imageUrl('moduleActiveBg')"
                    class="tab-active-bg"
                    alt=""
                  />
                  <span style="position: relative; z-index: 1; font-weight: 700">{{
                    t(tab.label)
                  }}</span>
                </itemCenter>
              </button>
            </div>

            <!-- 当前模块容器背景：container 大背景图统一用 itemCenter 自上而下渲染 -->
            <div
              v-if="activeContainerImages.length"
              style="
                display: flex;
                flex-direction: column;
                align-items: center;
                gap: 0;
                margin: 0;
                padding: 0;
              "
            >
              <template v-for="imageName in activeContainerImages" :key="imageName">
                <!-- 弹幕模块：中奖飘窗接口返回后横向展示一行 -->
                <div
                  v-if="imageName === 'barrage' && barrageRenderList.length"
                  style="
                    width: 100vw;
                    height: 8vw;
                    overflow: hidden;
                    margin: 0 0 4vw 0;
                    padding: 0;
                    direction: ltr;
                  "
                >
                  <div
                    class="barrage-track"
                    style="display: flex; align-items: center; gap: 3vw; height: 100%"
                  >
                    <itemCenter
                      v-for="(item, index) in barrageRenderList"
                      :key="`${item.account || item.nickName || index}-${item.drawTime || index}`"
                      :imgUrl="imageUrl('barrageBg')"
                      style="width: 45vw; height: 8vw; flex: 0 0 45vw; margin: 0; padding: 0"
                      contentStyle="padding: 0 2vw; display: flex; align-items: center; justify-content: flex-start; gap: 1vw; box-sizing: border-box;"
                    >
                      <img
                        v-smart-img.noFade
                        :src="item.avatar"
                        alt=""
                        style="
                          width: 6vw;
                          height: 6vw;
                          flex: 0 0 6vw;
                          border-radius: 50%;
                          object-fit: cover;
                          display: block;
                        "
                        @error="handleAvatarImageError"
                      />
                      <div
                        style="
                          flex: 1;
                          min-width: 0;
                          display: flex;
                          flex-direction: column;
                          justify-content: center;
                          gap: 0.5vw;
                        "
                      >
                        <div
                          style="
                            width: 100%;
                            color: #fff;
                            font-size: 1em;
                            font-weight: 700;
                            line-height: 1.1;
                            overflow: hidden;
                            text-overflow: ellipsis;
                            white-space: nowrap;
                          "
                        >
                          {{ item.nickName || item.account || '' }}
                        </div>
                        <div
                          style="
                            width: 100%;
                            color: #fff;
                            font-size: 1em;
                            font-weight: 600;
                            line-height: 1.1;
                            overflow: hidden;
                            text-overflow: ellipsis;
                            white-space: nowrap;
                          "
                        >
                          {{ t('gulben_barrage_get_a') }} {{ item.prizeName || '' }}
                        </div>
                      </div>
                    </itemCenter>
                  </div>
                </div>

                <!-- lotteryBg -->
                <itemCenter
                  v-else-if="imageName === 'lotteryBg'"
                  :imgUrl="imageUrl(imageName)"
                  style="width: 100vw; margin: 0; padding: 0"
                  contentStyle="padding: 8vw 5vw 0; align-items: stretch; justify-content: flex-start; box-sizing: border-box;"
                >
                  <itemCenter
                    :imgUrl="imageUrl('ruleBtBg')"
                    style="
                      position: absolute;
                      top: 3vw;
                      right: 0vw;
                      z-index: 2;
                      width: 20vw;
                      margin: 0;
                      padding: 0;
                      direction: ltr;
                    "
                    contentStyle="padding: 4vw 0 4vw 3vw;"
                    @click="openHistoryPopup"
                  >
                    <span
                      style="
                        color: #fff8d7;
                        font-size: 1em;
                        font-weight: 800;
                        line-height: 1;
                        text-align: center;
                        text-shadow: 0 1px 2px rgba(63, 20, 57, 0.5);
                        white-space: nowrap;
                      "
                    >
                      {{ t('history') }}
                    </span>
                  </itemCenter>

                  <!-- 抽奖区域：上层放左右切换按钮和奖品，下层放抽奖按钮 -->
                  <div
                    style="width: 100%; height: 100%; display: flex; flex-direction: column; gap: 0"
                  >
                    <!-- 抽奖区域上层：左 / 奖品 / 右三列 -->
                    <div style="display: flex; flex: 1; align-items: center; gap: 4vw">
                      <div
                        style="
                          width: 10vw;
                          flex: 0 0 10vw;
                          display: flex;
                          align-items: center;
                          justify-content: center;
                        "
                        @click="changeLotteryPoolMode(-1)"
                      >
                        <img
                          v-smart-img.noFade
                          :src="imageUrl('leftBt')"
                          alt=""
                          class="pool-arrow pool-arrow-prev"
                        />
                      </div>

                      <div style="flex: 1; min-width: 0">
                        <div
                          :class="['lottery-prize-list', activeLotteryPoolMode.listClass]"
                          style="
                            display: flex;
                            flex-direction: column;
                            justify-content: center;
                            align-items: center;
                            gap: var(--lottery-prize-gap);
                          "
                        >
                          <div
                            v-for="(row, rowIndex) in activeLotteryPrizeRows"
                            :key="`${activeLotteryPoolMode.name}-${rowIndex}`"
                            style="
                              width: 100%;
                              display: flex;
                              align-items: center;
                              justify-content: center;
                              gap: var(--lottery-prize-gap);
                            "
                          >
                            <itemCenter
                              v-for="prize in row"
                              :key="prize.renderKey"
                              :imgUrl="imageUrl('lotteryPrizeBg')"
                              :class="[
                                'lottery-prize-item',
                                activeLotteryPoolMode.itemClass,
                                { 'lottery-prize-item-active': prize.index === activeLotteryIndex },
                              ]"
                              contentStyle="padding: 1vw; flex-direction: column; gap: 0.5vw; align-items: center; justify-content: center; box-sizing: border-box;"
                            >
                              <img
                                v-show="prize.index === activeLotteryIndex"
                                v-smart-img.noFade
                                :src="imageUrl('lotteryPrizeActiveBg')"
                                class="lottery-prize-active-bg"
                                alt=""
                              />
                              <img
                                v-if="prize.prizeImage"
                                v-smart-img.noFade
                                :src="prize.prizeImage"
                                alt=""
                                style="
                                  position: relative;
                                  z-index: 1;
                                  width: 60%;
                                  display: block;
                                  object-fit: contain;
                                "
                                @error="handleRewardImageError"
                              />
                              <div
                                style="
                                  position: relative;
                                  z-index: 1;
                                  width: 100%;
                                  color: #000;
                                  font-size: 0.7em;
                                  font-weight: 700;
                                  line-height: 1.1;
                                  text-align: center;
                                  overflow-wrap: anywhere;
                                "
                              >
                                {{ getLotteryPrizeDisplayText(prize) }}
                              </div>
                            </itemCenter>
                          </div>
                        </div>
                      </div>

                      <div
                        style="
                          width: 10vw;
                          flex: 0 0 10vw;
                          display: flex;
                          align-items: center;
                          justify-content: center;
                        "
                        @click="changeLotteryPoolMode(1)"
                      >
                        <img
                          v-smart-img.noFade
                          :src="imageUrl('leftBt')"
                          alt=""
                          class="pool-arrow pool-arrow-next"
                        />
                      </div>
                    </div>

                    <!-- 抽奖区域下层：单抽和5连抽按钮，内部放当前奖池票券图标 -->
                    <div
                      style="
                        display: flex;
                        align-items: center;
                        justify-content: space-around;
                        gap: 0;
                        margin: 0;
                        padding: 0;
                      "
                    >
                      <itemCenter
                        :imgUrl="imageUrl('draw1Bt')"
                        :class="{ 'collect-action-disabled': !canLotteryRewardDraw(1) }"
                        style="width: 38vw; margin: 0; padding: 0"
                        contentStyle="padding: 0; align-items: flex-end;padding: 0vw"
                        @click="handleLotteryDraw(1)"
                      >
                        <img
                          v-smart-img.noFade
                          :src="imageUrl(activeLotteryTicketImage)"
                          alt=""
                          style="width: 10vw; display: block"
                        />
                      </itemCenter>
                      <itemCenter
                        :imgUrl="imageUrl('draw5BtBg')"
                        :class="{ 'collect-action-disabled': !canLotteryRewardDraw(5) }"
                        style="width: 38vw; margin: 0; padding: 0"
                        contentStyle="padding: 0; align-items: flex-end;padding: 0vw"
                        @click="handleLotteryDraw(5)"
                      >
                        <img
                          v-smart-img.noFade
                          :src="imageUrl(activeLotteryTicketImage)"
                          alt=""
                          style="width: 10vw; display: block"
                        />
                      </itemCenter>
                    </div>
                  </div>
                </itemCenter>

                <!-- collectGameBg -->
                <itemCenter
                  v-else-if="imageName === 'collectGameBg'"
                  :imgUrl="imageUrl(imageName)"
                  style="width: 100vw; margin: 0; padding: 0"
                  contentStyle="padding: 0; align-items: stretch; justify-content: flex-start; box-sizing: border-box;"
                >
                  <!-- 集卡玩法区域：票券、提示、标题、奖池和操作按钮五层结构 -->
                  <div
                    style="
                      width: 100%;
                      height: 100%;
                      display: flex;
                      flex-direction: column;
                      justify-content: space-between;

                      margin: 0;
                      padding: 0;
                    "
                  >
                    <!-- 第一层：三种票券数量 -->
                    <div
                      style="
                        display: flex;
                        align-items: center;
                        justify-content: center;
                        gap: 2vw;
                        padding: 15vw 9vw 15vw;
                        margin: 0;
                      "
                    >
                      <div
                        v-for="(ticket, index) in collectTicketItems"
                        :key="ticket.type"
                        style="
                          position: relative;
                          flex: 1;
                          min-width: 0;
                          height: 100%;
                          display: flex;
                          align-items: center;
                          justify-content: center;
                          gap: 0.5vw;
                        "
                      >
                        <div style="width: 3vw; height: 100%">
                          <img
                            v-smart-img.noFade
                            :src="imageUrl('helpBt')"
                            alt=""
                            style="width: 100%; display: block"
                          />
                        </div>

                        <img
                          v-smart-img.noFade
                          :src="imageUrl(ticket.image)"
                          alt=""
                          style="width: 13vw; display: block"
                        />
                        <span style="color: #fff; font-size: 1.7em; font-weight: 600">
                          :{{ getTicketRemainingCount(ticket.type) }}
                        </span>
                      </div>
                    </div>

                    <!-- 第二层：玩法提示文案 -->
                    <div
                      style="
                        color: #d2d2d2;
                        font-size: 0.7em;
                        font-weight: 600;
                        text-align: center;
                        padding: 0;
                        margin: 0;
                      "
                    >
                      {{ t('gulben_collect_tip') }}
                    </div>

                    <!-- 第三层：当前奖池标题 -->
                    <div
                      style="
                        height: 9vw;
                        display: flex;
                        align-items: center;
                        justify-content: center;
                        padding: 0;
                        margin: 0;
                      "
                    >
                      <img
                        v-smart-img.noFade
                        :src="imageUrl(activeCollectGameTitle)"
                        alt=""
                        style="height: 6.5vw; width: auto; display: block"
                      />
                    </div>

                    <!-- 第四层：当前奖池卡牌 -->
                    <div
                      style="
                        height: 25vw;
                        display: flex;
                        align-items: center;
                        gap: 0;
                        padding: 0;
                        margin: 0;
                      "
                    >
                      <div
                        style="
                          width: 10vw;
                          flex: 0 0 10vw;
                          display: flex;
                          align-items: center;
                          justify-content: center;
                        "
                        @click="changeCollectGameMode(-1)"
                      >
                        <img
                          v-smart-img.noFade
                          :src="imageUrl('leftBt')"
                          alt=""
                          class="pool-arrow pool-arrow-prev"
                        />
                      </div>

                      <div
                        style="
                          flex: 1;
                          min-width: 0;
                          height: 24vw;
                          display: flex;
                          align-items: center;
                          justify-content: space-around;
                          gap: 0;
                        "
                      >
                        <template v-for="card in activeCollectCards" :key="card.cardTemplateId">
                          <img
                            v-if="card.isCardBack"
                            v-smart-img.noFade
                            :src="imageUrl('cardBackBg')"
                            alt=""
                            style="height: 100%; width: auto; display: block"
                          />
                          <itemCenter
                            v-else
                            :imgUrl="imageUrl(card.imageName)"
                            style="
                              height: 100%;
                              width: auto;
                              aspect-ratio: 159 / 261;
                              flex: 0 0 auto;
                            "
                            contentStyle="padding: 0;"
                          >
                            <div
                              style="
                                position: absolute;
                                top: 0.8vw;
                                right: 0.8vw;
                                border-radius: 33px 33px 33px 34px;
                                background: rgba(0, 0, 0, 0.1);
                                backdrop-filter: blur(2px);
                                -webkit-backdrop-filter: blur(2px);
                                padding: 4px;
                                color: #fff;
                                font-size: 1em;
                                font-weight: 600;
                                line-height: 1;
                              "
                            >
                              {{ card.quantity }}
                            </div>
                          </itemCenter>
                        </template>
                      </div>

                      <div
                        style="
                          width: 10vw;
                          flex: 0 0 10vw;
                          display: flex;
                          align-items: center;
                          justify-content: center;
                        "
                        @click="changeCollectGameMode(1)"
                      >
                        <img
                          v-smart-img.noFade
                          :src="imageUrl('leftBt')"
                          alt=""
                          class="pool-arrow pool-arrow-next"
                        />
                      </div>
                    </div>

                    <!-- 第五层：当前奖池操作按钮 -->
                    <div
                      style="
                        height: 25vw;
                        display: flex;
                        align-items: center;
                        justify-content: space-around;
                        gap: 0;
                        padding: 0;
                        margin: 0;
                      "
                    >
                      <itemCenter
                        v-if="activeCollectGameMode.name === 'gameR'"
                        :imgUrl="imageUrl('GameDrawBtBg')"
                        style="width: 40vw; margin: 0; padding: 0"
                        contentStyle="padding: 10vw 6vw 5vw; flex-direction: column; gap: 0.5vw; align-items: center; justify-content: center;"
                        @click="handleCardDraw()"
                      >
                        <div
                          style="
                            color: #fff;
                            font-size: 0.75em;
                            font-weight: 700;
                            line-height: 1.1;
                            text-align: center;
                            white-space: nowrap;
                          "
                        >
                          {{ t('gulben_remaining_tickets', { count: cardDrawTicketCount }) }}
                        </div>
                        <div
                          style="
                            color: #fff;
                            font-size: 0.75em;
                            font-weight: 700;
                            line-height: 1.1;
                            text-align: center;
                            white-space: nowrap;
                          "
                        >
                          {{ t('gulben_coins_cost', { count: 1000 }) }}
                        </div>
                      </itemCenter>
                      <itemCenter
                        v-if="activeCollectGameMode.name !== 'gameSSR'"
                        :imgUrl="imageUrl('ticketHandleBtBg')"
                        :class="{ 'collect-action-disabled': !canCombineActiveCards }"
                        style="width: 25vw; margin: 0; padding: 0"
                        contentStyle="padding: 0; align-items: center; justify-content: center;"
                        @click="handleCardCombine"
                      >
                        <div
                          style="
                            color: #fff;
                            font-size: 1em;
                            font-weight: 700;
                            line-height: 1.1;
                            text-align: center;
                            white-space: nowrap;
                          "
                        >
                          {{ t('gulben_combine_count', { count: 1 }) }}
                        </div>
                      </itemCenter>
                      <itemCenter
                        :imgUrl="imageUrl('ticketHandleBtBg')"
                        :class="{ 'collect-action-disabled': !canRedeemActiveCards }"
                        style="width: 25vw; margin: 0; padding: 0"
                        contentStyle="padding: 0; flex-direction: column; align-items: center; justify-content: center;"
                        @click="handleCardRedeem"
                      >
                        <div
                          style="
                            color: #fff;
                            font-size: 1em;
                            font-weight: 700;
                            line-height: 1.1;
                            text-align: center;
                            white-space: nowrap;
                          "
                        >
                          {{ t('gulben_redeem') }}
                        </div>
                        <div
                          style="
                            display: flex;
                            align-items: center;
                            justify-content: center;
                            gap: 0.5vw;
                            color: #fff;
                            font-size: 0.8em;
                            font-weight: 700;
                            line-height: 1;
                            white-space: nowrap;
                          "
                        >
                          <img
                            v-smart-img.noFade
                            :src="imageUrl(activeCollectTicketImage)"
                            alt=""
                            style="width: 6vw; display: block"
                          />
                          <span style="color: #fff; font-size: 1em; font-weight: 700"> *1 </span>
                        </div>
                      </itemCenter>
                    </div>
                  </div>
                </itemCenter>

                <!-- taskModule -->
                <div
                  v-else-if="imageName === 'taskModule'"
                  style="
                    width: 100vw;
                    display: flex;
                    flex-direction: column;
                    gap: 2vw;
                    margin: 0;
                    padding: 0 2vw;
                    box-sizing: border-box;
                  "
                >
                  <!-- 任务模块：复用 Season5 的任务列表字段，不展示任务类型图标和票券图标 -->
                  <itemCenter
                    v-for="task in taskList"
                    :key="task.taskCode || task.id"
                    :imgUrl="imageUrl('taskItemBg')"
                    style="width: 100%; margin: 0; padding: 0"
                    contentStyle="display: flex; padding: 7vw; justify-content: space-between; align-items: center;"
                  >
                    <div
                      style="flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1vw"
                    >
                      <div
                        style="color: #fff; font-size: 0.9em; font-weight: 590; line-height: 1.2"
                      >
                        {{ showTarget(task) }}
                      </div>
                      <div style="font-size: 0.9em; display: flex; align-items: center; gap: 1vw">
                        <span style="color: #fff; font-size: 1em; font-weight: 590">{{
                          t('Rewards:')
                        }}</span>
                        <img
                          v-smart-img.noFade
                          :src="imageUrl('Gameticket')"
                          :alt="t('ticket')"
                          width="20vw"
                          style="display: block"
                        />
                        <span style="color: #fff; font-size: 1em; font-weight: 590"
                          >*{{ task.rewardValue }}</span
                        >
                      </div>
                    </div>

                    <div style="width: 17vw; flex: 0 0 17vw">
                      <!-- 前往任务 -->
                      <itemCenter
                        v-if="task.taskStatus == 0"
                        style=""
                        :imgUrl="imageUrl('taskBtGo')"
                      >
                        <div style="font-size: 1em; font-weight: 600; white-space: nowrap">
                          {{ t('task_go') }}
                        </div>
                      </itemCenter>

                      <!-- 领取抽奖券 -->
                      <itemCenter
                        v-else-if="task.taskStatus === 1"
                        style=""
                        :imgUrl="imageUrl('taskBtReceive')"
                        @click="receiveTaskReward(task.taskCode)"
                      >
                        <div style="font-size: 1em; font-weight: 600; white-space: nowrap">
                          {{ t('task_receive') }}
                        </div>
                      </itemCenter>

                      <!-- 宸插畬鎴?-->
                      <itemCenter v-else style="" :imgUrl="imageUrl('taskBtReceived')">
                        <div style="font-size: 0.9em; font-weight: 600; white-space: nowrap">
                          {{ t('task_received') }}
                        </div>
                      </itemCenter>
                    </div>
                  </itemCenter>
                </div>

                <itemCenter
                  v-else
                  :imgUrl="imageUrl(imageName)"
                  style="width: 100vw; margin: 0; padding: 0"
                  contentStyle="padding: 0;"
                />
              </template>
            </div>

            <div
              v-else-if="isPersonalRewardSection"
              style="
                width: 100vw;
                margin: 0;
                padding: 0;
                box-sizing: border-box;
                display: flex;
                flex-direction: column;
                gap: 2vw;
              "
            >
              <!-- Personal Rank / Reward：三套奖励从上往下展示 -->
              <itemCenter
                v-for="(rewardTop, topIndex) in personalRewardTops"
                :key="rewardTop.rule?.id || topIndex"
                :imgUrl="imageUrl(`RewardTop${topIndex + 1}`)"
                style="width: 100vw; margin: 0; padding: 0"
                contentStyle="padding: 25.5vw 9vw 5vw; align-items: flex-start; justify-content: center; box-sizing: border-box;"
              >
                <div
                  style="
                    width: 100%;
                    display: flex;
                    align-items: center;
                    gap: 3.5vw;
                    direction: ltr;
                  "
                >
                  <!-- 左侧主奖励：每套奖励的第一个奖励大图展示 -->
                  <div
                    v-if="getRewardTopMainReward(rewardTop, topIndex)"
                    style="
                      width: 33vw;
                      flex: 0 0 33vw;
                      height: 100%;
                      display: flex;
                      flex-direction: column;
                      justify-content: flex-start;
                      align-items: center;
                      gap: 4vw;
                    "
                  >
                    <div
                      style="
                        width: 100%;
                        height: 68vw;
                        display: flex;
                        align-items: center;
                        justify-content: center;
                      "
                    >
                      <img
                        v-if="hasRewardCover(getRewardTopMainReward(rewardTop, topIndex))"
                        :src="getRewardTopMainReward(rewardTop, topIndex).cover || ''"
                        alt=""
                        style="width: 92%; display: block; object-fit: contain"
                        @error="
                          (event) =>
                            handleRewardImageError(
                              event,
                              getRewardTopMainReward(rewardTop, topIndex).type,
                            )
                        "
                      />
                      <div v-else aria-hidden="true" style="width: 92%; aspect-ratio: 1/1"></div>
                    </div>
                    <div
                      style="
                        color: #fff;
                        font-size: 1em;
                        font-weight: 700;
                        line-height: 1.1;
                        text-align: center;
                      "
                    >
                      {{ getRewardDisplayText(getRewardTopMainReward(rewardTop, topIndex)) }}
                    </div>
                  </div>

                  <!-- 右侧小奖励：前两行每行两个，第三行单个展示位居中 -->
                  <div
                    style="flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1.7vw"
                  >
                    <div
                      v-for="(row, rowIndex) in getRewardTopGridRows(rewardTop, topIndex)"
                      :key="`${topIndex}-${rowIndex}`"
                      style="display: flex; justify-content: center; gap: 2.7vw"
                    >
                      <div
                        v-if="rowIndex === 2 && row.length === 0"
                        aria-hidden="true"
                        style="width: calc((100% - 2vw) / 2); min-width: 0; visibility: hidden"
                      >
                        <div style="width: 100%; aspect-ratio: 1/1"></div>
                        <div style="font-size: 0.8em; line-height: 1.1">&nbsp;</div>
                      </div>
                      <div
                        v-for="(reward, rewardIndex) in row"
                        :key="getRewardRenderKey(reward, rewardIndex)"
                        style="
                          width: calc((100% - 2vw) / 2);
                          min-width: 0;
                          display: flex;
                          flex-direction: column;
                          align-items: center;
                          gap: 0.5vw;
                        "
                      >
                        <div
                          style="
                            width: 100%;
                            aspect-ratio: 1/1;
                            display: flex;
                            align-items: center;
                            justify-content: center;
                          "
                        >
                          <img
                            v-if="hasRewardCover(reward)"
                            :src="reward.cover || ''"
                            alt=""
                            style="width: 88%; display: block; object-fit: contain"
                            @error="(event) => handleRewardImageError(event, reward.type)"
                          />
                          <div
                            v-else
                            aria-hidden="true"
                            style="width: 88%; aspect-ratio: 1/1"
                          ></div>
                        </div>
                        <div
                          style="
                            width: 100%;
                            color: #fff;
                            font-size: 0.8em;
                            font-weight: 700;
                            line-height: 1.1;
                            text-align: center;
                            overflow: hidden;
                            white-space: nowrap;
                            text-overflow: ellipsis;
                          "
                        >
                          {{ getRewardDisplayText(reward) }}
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
              </itemCenter>
            </div>

            <div
              v-else-if="isRoomRewardSection"
              style="width: 100vw; margin: 0; padding: 0; box-sizing: border-box"
            >
              <!-- Room Rank / Reward：规则图作为背景，规则内容贴顶部展示 -->
              <itemCenter
                :imgUrl="imageUrl('ruleActivity')"
                style="width: 100vw; margin: 0; padding: 0"
                contentStyle="align-items: flex-start; justify-content: flex-start; flex-direction: column; gap: 1.5vw; padding: 28vw 8vw 0; box-sizing: border-box;"
              >
                <div class="room-rule-text" style="font-weight: 800">
                  {{ t('gulben_room_rule_title') }}
                </div>
                <div class="room-rule-text">
                  {{ t('gulben_room_rule_1') }}
                </div>
                <div class="room-rule-text">
                  {{ t('gulben_room_rule_2') }}
                </div>
                <div class="room-rule-text">
                  {{ t('gulben_room_rule_3') }}
                </div>
              </itemCenter>
            </div>

            <div
              v-else-if="isRankingSection"
              style="
                width: 100vw;
                margin: 0;
                padding: 0 0 12vw;
                box-sizing: border-box;
                display: flex;
                flex-direction: column;
                gap: 4vw;
              "
            >
              <!-- 活动礼物模块：参考 KingAndQueen 每周礼物结构，使用 giftBg 承载 -->
              <itemCenter
                v-if="isPersonalRankingSection"
                :imgUrl="imageUrl('giftBg')"
                style="width: 100%; margin: 0; padding: 0"
                contentStyle="padding: 0;flex-direction: column;align-items: center;"
              >
                <div
                  style="
                    width: 89vw;
                    display: flex;
                    align-items: flex-start;
                    gap: 4vw;
                    margin-top: 18vw;
                  "
                >
                  <div
                    v-for="(gift, index) in weeklyGifts"
                    :key="gift.giftId || index"
                    style="
                      flex: 1;
                      min-width: 0;
                      display: flex;
                      flex-direction: column;
                      align-items: center;
                      gap: 2vw;
                    "
                  >
                    <div
                      style="
                        width: 100%;
                        aspect-ratio: 1/1;
                        display: flex;
                        justify-content: center;
                        align-items: center;
                      "
                    >
                      <img
                        v-if="gift.giftPhoto"
                        :src="gift.giftPhoto"
                        alt=""
                        style="width: 70%; aspect-ratio: 1/1; object-fit: contain; display: block"
                      />
                      <div v-else style="width: 70%; aspect-ratio: 1/1"></div>
                    </div>

                    <div
                      style="
                        display: flex;
                        align-items: center;
                        justify-content: center;
                        gap: 0.4vw;
                      "
                    >
                      <img
                        v-smart-img.noFade
                        :src="coinIcon"
                        alt=""
                        style="display: block; width: 1.3em; aspect-ratio: 1/1"
                      />
                      <div
                        style="
                          color: #fff;
                          font-size: 1em;
                          font-weight: 590;
                          text-align: center;
                          white-space: nowrap;
                        "
                      >
                        {{ gift.giftCandy }}
                      </div>
                    </div>
                  </div>
                </div>
              </itemCenter>

              <!-- 排行榜前三名：使用 GulbenFestival 私有 TopUser 组件 -->
              <div
                style="
                  display: flex;
                  align-items: center;
                  justify-content: center;
                  gap: 2vw;
                  margin: 0;
                "
              >
                <div style="flex: 1; min-width: 0">
                  <TopUser
                    ranking="2"
                    :BorderImgUrl="imageUrl('top2')"
                    :avatarUrl="rankingTop3[1].userAvatar"
                    :name="rankingTop3[1].userName"
                    :distributionValue="rankingTop3[1].amount"
                    :valueIconUrl="coinIcon"
                    style="width: 100%"
                    @click="handleRankingClick(rankingTop3[1])"
                  />
                </div>
                <div style="flex: 1.5; min-width: 0">
                  <TopUser
                    ranking="1"
                    :BorderImgUrl="imageUrl('top1')"
                    :avatarUrl="rankingTop3[0].userAvatar"
                    :name="rankingTop3[0].userName"
                    :distributionValue="rankingTop3[0].amount"
                    :valueIconUrl="coinIcon"
                    style="width: 100%"
                    @click="handleRankingClick(rankingTop3[0])"
                  />
                </div>
                <div style="flex: 1; min-width: 0">
                  <TopUser
                    ranking="3"
                    :BorderImgUrl="imageUrl('top3')"
                    :avatarUrl="rankingTop3[2].userAvatar"
                    :name="rankingTop3[2].userName"
                    :distributionValue="rankingTop3[2].amount"
                    :valueIconUrl="coinIcon"
                    style="width: 100%"
                    @click="handleRankingClick(rankingTop3[2])"
                  />
                </div>
              </div>

              <!-- 第四名开始的排行榜 -->
              <div v-if="rankingListAfterTop3.length" style="position: relative; z-index: 2">
                <itemCenter
                  v-for="(listItem, index) in rankingListAfterTop3"
                  :key="listItem.userId || index"
                  :imgUrl="imageUrl('RankingItem')"
                  :contentStyle="`padding: 0 8vw`"
                  style="width: 100%; margin: 0 0 5vw; padding: 0"
                  @click="handleRankingClick(listItem)"
                >
                  <div style="flex: 1; min-width: 0; display: flex; align-items: center; gap: 2vw">
                    <div style="color: #fff; font-size: 1.5em; font-weight: 700">
                      {{ listItem.rank }}
                    </div>
                    <img
                      :src="listItem.userAvatar || ''"
                      alt=""
                      :class="['ranking-avatar', { 'ranking-avatar-square': isRoomRankingSection }]"
                      style="display: block; width: 3.5em; aspect-ratio: 1/1; object-fit: cover"
                    />
                    <div style="flex: 1; min-width: 0; display: flex; flex-direction: column">
                      <div
                        style="
                          color: #fff;
                          font-size: 1em;
                          font-weight: 860;
                          overflow: hidden;
                          white-space: nowrap;
                          text-overflow: ellipsis;
                        "
                      >
                        {{ listItem.userName }}
                      </div>
                      <div style="color: #fff; font-size: 1em; font-weight: 590">
                        {{ t('user_id_prefix') }} {{ listItem.specialAccount || listItem.account }}
                      </div>
                      <div
                        v-if="!isRoomRankingSection"
                        style="display: flex; align-items: center; gap: 1vw; margin-top: 0.5vw"
                      >
                        <img
                          v-if="getVipLevelImageName(listItem.vipLevelName)"
                          v-smart-img.noFade
                          :src="imageUrl(getVipLevelImageName(listItem.vipLevelName))"
                          alt=""
                          style="width: 12vw; display: block"
                        />
                        <itemCenter
                          :imgUrl="imageUrl(getLevelBgImageName('userLevel', listItem.charmLevel))"
                          style="width: 12vw; margin: 0; padding: 0"
                          contentStyle="padding: 0 0 0 5vw; align-items: center; justify-content: center;"
                        >
                          <div
                            style="color: #fff; font-size: 1em; font-weight: 700; line-height: 1"
                          >
                            {{ listItem.charmLevel || 0 }}
                          </div>
                        </itemCenter>
                        <itemCenter
                          :imgUrl="
                            imageUrl(getLevelBgImageName('wealthLevel', listItem.wealthLevel))
                          "
                          style="width: 12vw; margin: 0; padding: 0"
                          contentStyle="padding: 0 0 0 4vw; align-items: center; justify-content: center;"
                        >
                          <div
                            style="color: #fff; font-size: 1em; font-weight: 700; line-height: 1"
                          >
                            {{ listItem.wealthLevel || 0 }}
                          </div>
                        </itemCenter>
                      </div>
                    </div>
                  </div>
                  <div
                    style="
                      display: flex;
                      align-items: center;
                      justify-content: center;
                      gap: 0.4vw;
                      color: #fff;
                      font-size: 1em;
                      font-weight: 700;
                    "
                  >
                    <img
                      v-smart-img.noFade
                      :src="coinIcon"
                      alt=""
                      style="display: block; width: 1.3em; aspect-ratio: 1/1"
                    />
                    <span style="color: #fff; font-size: 1em; font-weight: 700">
                      {{ listItem.amount || 0 }}
                    </span>
                  </div>
                </itemCenter>
              </div>
              <div
                ref="rankingLoadmore"
                style="width: 100%; height: 1px; margin: 0; padding: 0"
              ></div>
            </div>

            <!-- 当前模块临时内容：只保留无边框、无额外间距的预览结构 -->
            <div
              v-else
              style="
                min-height: 58vw;
                display: flex;
                flex-direction: column;
                align-items: center;
                justify-content: center;
                gap: 0;
                margin: 0;
                padding: 0;
              "
            >
              <img
                v-for="imageName in activePreviewImages"
                :key="imageName"
                v-smart-img.noFade
                :src="imageUrl(imageName)"
                alt=""
                class="preview-image"
              />
              <div
                style="
                  color: rgba(255, 246, 216, 0.86);
                  font-size: 1em;
                  font-weight: 700;
                  text-align: center;
                "
              >
                {{ activeContentTitle }}
              </div>
            </div>
          </div>
        </section>
      </main>

      <!-- Personal Rank / Ranking 下固定贴底展示我的排名 -->
      <itemCenter
        v-if="isRankingSection"
        style="
          position: fixed;
          bottom: 0;
          left: 0;
          right: 0;
          z-index: 3;
          width: min(100vw, 480px);
          margin: 0 auto;
          padding: 0;
        "
        :imgUrl="imageUrl('RankingItem')"
        :contentStyle="`padding: 2vw 8vw;`"
      >
        <div
          style="
            flex: 1;
            min-width: 0;
            display: flex;
            justify-content: space-around;
            align-items: center;
            gap: 2vw;
          "
        >
          <div style="color: #fff; font-size: 1em; font-weight: 700">
            {{ myRanking.rank || 999 }}
          </div>
          <img
            :src="myRanking.userAvatar || ''"
            alt=""
            :class="['ranking-avatar', { 'ranking-avatar-square': isRoomRankingSection }]"
            style="display: block; width: 4em; aspect-ratio: 1/1; object-fit: cover"
          />
          <div style="flex: 1; min-width: 0">
            <div
              style="
                color: #fff;
                font-size: 1em;
                font-weight: 860;
                overflow: hidden;
                white-space: nowrap;
                text-overflow: ellipsis;
              "
            >
              {{ myRanking.userName }}
            </div>
            <div
              v-if="isRoomRankingSection"
              style="
                color: #fff;
                font-size: 1em;
                font-weight: 590;
                overflow: hidden;
                white-space: nowrap;
                text-overflow: ellipsis;
              "
            >
              {{ t('user_id_prefix') }} {{ myRanking.specialAccount || myRanking.account }}
            </div>
            <div v-else style="display: flex; align-items: center; gap: 1vw; margin-top: 0.5vw">
              <img
                v-if="getVipLevelImageName(myRanking.vipLevelName)"
                v-smart-img.noFade
                :src="imageUrl(getVipLevelImageName(myRanking.vipLevelName))"
                alt=""
                style="width: 12vw; display: block"
              />
              <itemCenter
                :imgUrl="imageUrl(getLevelBgImageName('userLevel', myRanking.charmLevel))"
                style="width: 12vw; margin: 0; padding: 0"
                contentStyle="padding: 0 0 0 5vw; align-items: center; justify-content: center;"
              >
                <div style="color: #fff; font-size: 1em; font-weight: 700; line-height: 1">
                  {{ myRanking.charmLevel || 0 }}
                </div>
              </itemCenter>
              <itemCenter
                :imgUrl="imageUrl(getLevelBgImageName('wealthLevel', myRanking.wealthLevel))"
                style="width: 12vw; margin: 0; padding: 0"
                contentStyle="padding: 0 0 0 4vw; align-items: center; justify-content: center;"
              >
                <div style="color: #fff; font-size: 1em; font-weight: 700; line-height: 1">
                  {{ myRanking.wealthLevel || 0 }}
                </div>
              </itemCenter>
            </div>
          </div>
        </div>
        <div
          style="
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 0.4vw;
            color: #fff;
            font-size: 1em;
            font-weight: 700;
          "
        >
          <img
            v-smart-img.noFade
            :src="coinIcon"
            alt=""
            style="display: block; width: 1.3em; aspect-ratio: 1/1"
          />
          <span style="color: #fff; font-size: 1em; font-weight: 700">
            {{ myRanking.amount || 0 }}
          </span>
        </div>
      </itemCenter>

      <!-- 弹窗：点击遮罩关闭，规则、中奖历史和抽卡结果共用 MaskLayer -->
      <MaskLayer :maskLayerShow="maskLayerShow">
        <div
          style="
            min-height: 100%;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 8vw 0;
            box-sizing: border-box;
          "
          @click="closePopup"
          @touchend.self.prevent="closePopup"
        >
          <!-- 规则弹窗：内容使用 ruleInfoBg 背景承载 -->
          <div
            v-if="ruleShow"
            style="position: relative; width: min(92vw, 440px)"
            @click.stop
            @touchend.stop
          >
            <itemCenter
              :imgUrl="imageUrl('ruleInfoBg')"
              style="width: 100%; margin: 0; padding: 0"
              contentStyle="padding: 27vw 7vw 12vw; align-items: stretch; justify-content: flex-start; box-sizing: border-box;"
            >
              <div
                class="rule-popup-scroll"
                style="
                  max-height: 62vh;
                  overflow-y: auto;
                  color: #fff;
                  font-size: 1.5em;
                  font-weight: 600;
                  line-height: 1.35;
                  text-align: left;
                "
              >
                <div>{{ t('gulben_rule_title_earn') }}</div>
                <div>{{ t('gulben_rule_earn_1') }}</div>
                <div style="margin-top: 2vw">{{ t('gulben_rule_title_use') }}</div>
                <div>{{ t('gulben_rule_use_1') }}</div>
                <div style="margin-top: 2vw">{{ t('gulben_rule_title_combine') }}</div>
                <div>{{ t('gulben_rule_combine_1') }}</div>
                <div>{{ t('gulben_rule_combine_2') }}</div>
                <div style="margin-top: 2vw">{{ t('gulben_rule_title_redeem') }}</div>
                <div>{{ t('gulben_rule_redeem_1') }}</div>
                <div>{{ t('gulben_rule_redeem_2') }}</div>
                <div>{{ t('gulben_rule_redeem_3') }}</div>
              </div>
            </itemCenter>
          </div>

          <!-- 中奖历史弹窗：内容使用 historyBg 背景承载，底部监听点触发分页加载 -->
          <div
            v-if="historyShow"
            style="position: relative; width: min(95vw, 456px); direction: ltr"
            @click.stop
            @touchend.stop
          >
            <itemCenter
              :imgUrl="imageUrl('historyBg')"
              style="width: 100%; min-height: 97.53vw; margin: 0; padding: 0"
              contentStyle="padding: 24vw 6vw 11vw; align-items: stretch; justify-content: flex-start; box-sizing: border-box;"
            >
              <div
                class="history-popup-scroll"
                style="
                  width: 100%;
                  max-height: 62vh;
                  overflow-y: auto;
                  display: flex;
                  flex-direction: column;
                  gap: 8px;
                "
              >
                <div
                  v-for="(record, index) in historyRecords"
                  :key="record.recordNo || record.id || index"
                  style="
                    border-radius: 8px;
                    border: 1px solid rgba(64, 64, 64, 0.5);
                    background: rgba(240, 240, 240, 0.3);
                    box-shadow:
                      -42px 103px 31px 0 rgba(145, 145, 145, 0),
                      -27px 66px 29px 0 rgba(145, 145, 145, 0.01),
                      -15px 37px 24px 0 rgba(145, 145, 145, 0.03),
                      -7px 17px 18px 0 rgba(145, 145, 145, 0.04),
                      -2px 4px 10px 0 rgba(145, 145, 145, 0.05);
                    backdrop-filter: blur(7.5px);
                    display: flex;
                    padding: 12px;
                    justify-content: space-between;
                    align-items: center;
                    gap: 2vw;
                  "
                >
                  <div style="display: flex; align-items: center; gap: 4px; min-width: 0">
                    <img
                      v-if="getHistoryRecordImage(record)"
                      v-smart-img.noFade
                      :src="getHistoryRecordImage(record)"
                      alt=""
                      style="
                        width: 10vw;
                        aspect-ratio: 1/1;
                        flex: 0 0 10vw;
                        object-fit: contain;
                        display: block;
                      "
                      @error="handleRewardImageError"
                    />
                    <div v-else style="width: 10vw; aspect-ratio: 1/1; flex: 0 0 10vw"></div>
                    <div
                      style="
                        min-width: 0;
                        color: #fff;
                        font-size: 0.9em;
                        font-weight: 590;
                        line-height: 1.2;
                        text-align: left;
                        overflow: hidden;
                        text-overflow: ellipsis;
                        white-space: nowrap;
                      "
                    >
                      {{ getHistoryRecordPrizeText(record) }}
                    </div>
                  </div>
                  <div
                    style="
                      color: #fff;
                      font-size: 0.8em;
                      font-weight: 510;
                      line-height: 1.2;
                      text-align: right;
                      white-space: nowrap;
                    "
                  >
                    {{ formatHistoryRecordTime(record.drawTime) }}
                  </div>
                </div>

                <div
                  ref="historyLoadmore"
                  style="width: 100%; height: 1px; flex: 0 0 1px; margin: 0; padding: 0"
                ></div>
              </div>
            </itemCenter>
          </div>

          <!-- 抽卡结果弹窗：展示本次抽中的卡片，下方保留抽卡按钮用于连续抽卡 -->
          <div
            v-if="cardDrawResultShow"
            style="position: relative; width: min(92vw, 440px); direction: ltr"
            @click.stop
            @touchend.stop
          >
            <itemCenter
              :imgUrl="imageUrl('resultBg')"
              style="width: 100%; min-height: 90vw; margin: 0; padding: 0"
              contentStyle="padding: 22vw 8vw 8vw; flex-direction: column;  align-items: center; justify-content: space-between; box-sizing: border-box;"
            >
              <div
                style="
                  width: 100%;
                  display: flex;
                  align-items: center;
                  justify-content: space-between;
                  gap: 2vw;
                "
              >
                <div
                  style="
                    width: 8vw;
                    min-width: 8vw;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                  "
                >
                  <img
                    v-if="showRewardResultSwitcher"
                    v-smart-img.noFade
                    :src="imageUrl('leftBt')"
                    alt=""
                    class="result-popup-arrow result-popup-arrow-prev"
                    @click="changeCardDrawResultIndex(-1)"
                    @touchend.stop.prevent="changeCardDrawResultIndex(-1)"
                  />
                </div>

                <img
                  v-if="currentCardDrawResultImageUrl"
                  v-smart-img.noFade
                  :src="currentCardDrawResultImageUrl"
                  alt=""
                  :style="{
                    width: cardResultActionMode === 'rewardDraw' ? '45vw' : '28vw',
                    display: 'block',
                    objectFit: 'contain',
                  }"
                  @error="handleRewardImageError"
                />
                <div
                  v-else
                  :style="{
                    width: cardResultActionMode === 'rewardDraw' ? '45vw' : '28vw',
                    aspectRatio: '1/1',
                    flex: '0 0 auto',
                  }"
                ></div>

                <div
                  style="
                    width: 8vw;
                    min-width: 8vw;
                    display: flex;
                    align-items: center;
                    justify-content: center;
                  "
                >
                  <img
                    v-if="showRewardResultSwitcher"
                    v-smart-img.noFade
                    :src="imageUrl('leftBt')"
                    alt=""
                    class="result-popup-arrow result-popup-arrow-next"
                    @click="changeCardDrawResultIndex(1)"
                    @touchend.stop.prevent="changeCardDrawResultIndex(1)"
                  />
                </div>
              </div>

              <div
                v-if="cardResultActionMode === 'rewardDraw'"
                style="
                  position: relative;
                  width: 100%;
                  min-height: 1.2em;
                  color: #fff;
                  font-size: 1em;
                  font-weight: 700;
                  line-height: 1.2;
                  text-align: center;
                "
              >
                <span style="color: #fff; font-size: 1em; font-weight: 700">
                  {{ currentCardDrawResultText }}
                </span>
                <span
                  v-if="showRewardResultSwitcher"
                  style="
                    position: absolute;
                    top: 50%;
                    right: 0;
                    transform: translateY(-50%);
                    color: #fff;
                    font-size: 1em;
                    font-weight: 700;
                    line-height: 1;
                    white-space: nowrap;
                  "
                >
                  {{ cardDrawResultIndex + 1 }}/{{ cardDrawResultList.length }}
                </span>
              </div>

              <itemCenter
                v-if="cardResultActionMode === 'draw'"
                :imgUrl="imageUrl('GameDrawBtBg')"
                style="width: 40vw; margin: 0; padding: 0"
                contentStyle="padding: 10vw 6vw 5vw; flex-direction: column; gap: 0.5vw; align-items: center; justify-content: center;"
                @click="handleCardDraw()"
                @touchend.stop.prevent="handleCardDraw()"
              >
                <div
                  style="
                    color: #fff;
                    font-size: 0.75em;
                    font-weight: 700;
                    line-height: 1.1;
                    text-align: center;
                    white-space: nowrap;
                  "
                >
                  {{ t('gulben_remaining_tickets', { count: cardDrawTicketCount }) }}
                </div>
                <div
                  style="
                    color: #fff;
                    font-size: 0.75em;
                    font-weight: 700;
                    line-height: 1.1;
                    text-align: center;
                    white-space: nowrap;
                  "
                >
                  {{ t('gulben_coins_cost', { count: 1000 }) }}
                </div>
              </itemCenter>

              <div
                v-else-if="cardResultActionMode === 'combine'"
                style="
                  width: 100%;
                  display: flex;
                  align-items: center;
                  justify-content: center;
                  gap: 4vw;
                "
              >
                <itemCenter
                  :imgUrl="imageUrl('ticketHandleBtBg')"
                  style="width: 25vw; margin: 0; padding: 0"
                  contentStyle="padding: 0; align-items: center; justify-content: center;"
                  @click="closePopup"
                  @touchend.stop.prevent="closePopup"
                >
                  <div
                    style="
                      color: #fff;
                      font-size: 1em;
                      font-weight: 700;
                      line-height: 1.1;
                      text-align: center;
                      white-space: nowrap;
                    "
                  >
                    {{ t('gulben_accept') }}
                  </div>
                </itemCenter>
                <itemCenter
                  :imgUrl="imageUrl('ticketHandleBtBg')"
                  :class="{ 'collect-action-disabled': !canCombineActiveCards }"
                  style="width: 25vw; margin: 0; padding: 0"
                  contentStyle="padding: 0; align-items: center; justify-content: center;"
                  @click="handleCardCombine"
                  @touchend.stop.prevent="handleCardCombine"
                >
                  <div
                    style="
                      color: #fff;
                      font-size: 1em;
                      font-weight: 700;
                      line-height: 1.1;
                      text-align: center;
                      white-space: nowrap;
                    "
                  >
                    {{ t('gulben_combine_count', { count: 1 }) }}
                  </div>
                </itemCenter>
              </div>

              <itemCenter
                v-else
                :imgUrl="imageUrl('ticketHandleBtBg')"
                style="width: 30vw; margin: 0; padding: 0"
                contentStyle="padding: 0; align-items: center; justify-content: center;"
                @click="closePopup"
                @touchend.stop.prevent="closePopup"
              >
                <div
                  style="
                    color: #fff;
                    font-size: 1em;
                    font-weight: 700;
                    line-height: 1.1;
                    text-align: center;
                    white-space: nowrap;
                  "
                >
                  {{ t('confirm') }}
                </div>
              </itemCenter>
            </itemCenter>
          </div>
        </div>
      </MaskLayer>

      <!-- 抽卡动效层：接口成功后全屏展示 1.5 秒，再打开中奖弹窗 -->
      <div
        v-if="cardDrawAnimationShow"
        style="
          position: fixed;
          inset: 0;
          z-index: 9999;
          width: 100vw;
          height: 100vh;
          display: flex;
          align-items: center;
          justify-content: center;
          pointer-events: auto;
        "
        @click.stop
      >
        <img
          v-smart-img.noFade
          :src="cardDrawAnimationUrl"
          alt=""
          style="width: 100vw; height: 100vh; display: block; object-fit: contain"
        />
      </div>
    </div>
  </div>
</template>

<script setup>
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { setDocumentDirection } from '@/locales/i18n'
import { useLangStore } from '@/stores/lang'
import { getWebpUrl } from '@/config/imagePaths.js'
import { preloadImages, preloadImagesIdle } from '@/utils/image/imagePreloader.js'
import { showError } from '@/utils/toast.js'
import { viewUserInfo } from '@/utils/appBridge.js'
import { connectToApp } from '@/utils/appConnector.js'
import { handleAvatarImageError, handleRewardImageError } from '@/utils/image/imageHandler.js'
import { formatRewardDisplay } from '@/utils/rewardFormatter.js'
import { formatUTCCustom } from '@/utils/utcFormat.js'
import { useDebounce } from '@/utils/useDebounce.js'
import {
  apiGetCardRewardMarquee,
  apiGetCardRewardPrizeList,
  apiGetCardTicketList,
  apiPostCardBagList,
  apiPostCardCombine,
  apiPostCardDraw,
  apiPostCardRedeem,
  apiPostCardRewardDraw,
  apiPostCardRewardRecordList,
  getRankingListAndMyRanking,
  getThisWeekRewardsAndGifts,
} from '@/api/activity.js'
import { ActTaskList, activityDetail, receiveTickets } from '@/api/lottery'
import { getRoomContributionTotalRank } from '@/api/ranking.js'
import BackgroundLayer from '@/components/BackgroundLayer.vue'
import itemCenter from '@/components/itemCenter.vue'
import MaskLayer from '@/components/MaskLayer.vue'
import TopUser from './components/topUser.vue'
import { pageConfig } from './page.config.js'

const { t, locale } = useI18n()
const langStore = useLangStore()
const coinIcon = new URL('../../../assets/icon/Azizi/coin.png', import.meta.url).href

const gulbenActivityId = '1001'
const templateId = ref(pageConfig.activity.templateId || '2057676469390872578')
const activityType = ref(pageConfig.activity.activityType || '')
const roomRankingParams = {
  startDate: '20260523',
  endDate: '20260606',
  size: 20,
}

const isLoading = ref(true)
const activeTopTab = ref(pageConfig.modules.tabs.defaultValue)
const activeChildTab = ref('')
const activeLotteryIndex = ref(0)
const isLotteryRolling = ref(false)
const isCardDrawing = ref(false)
const isCardCombining = ref(false)
const isCardRedeeming = ref(false)
const activeLotteryPoolIndex = ref(0)
const activeCollectGameIndex = ref(0)
const ruleShow = ref(false)
const historyShow = ref(false)
const cardDrawResultShow = ref(false)
const cardDrawResultImageUrl = ref('')
const cardDrawResultList = ref([])
const cardDrawResultIndex = ref(0)
const cardResultActionMode = ref('draw')
const cardDrawAnimationShow = ref(false)
const cardDrawAnimationUrl = ref('')
const activityCode = ref(pageConfig.activity.activityCode || '')
const activity = ref({})
const Days = ref(0)
const Hours = ref('00')
const Minutes = ref('00')
const Seconds = ref('00')
const weeklyGifts = ref([
  {
    giftPhoto: '',
    giftCandy: 0,
  },
  {
    giftPhoto: '',
    giftCandy: 0,
  },
  {
    giftPhoto: '',
    giftCandy: 0,
  },
])
const ranking = ref([])
const myRanking = ref({})
const rankingLoaded = ref(false)
const loadedRankingType = ref('')
const rankingLoading = ref(false)
const rankingHasMore = ref(true)
const rankingLoadmore = ref(null)
const historyRecords = ref([])
const historyPage = ref(1)
const historySize = 10
const historyLoading = ref(false)
const historyHasMore = ref(true)
const historyLoadmore = ref(null)
const personalRewardLoaded = ref(false)
const kingQueenEffectiveData = ref(null)
const activityRewards = ref([])
const cardTicketList = ref([])
const cardRewardPrizePools = ref({})
const cardRewardMarqueeList = ref([])
const cardBagPools = ref({})
const taskList = ref([])

let countdownTimer = null
let lotteryAnimationTimer = null
let rankingObserver = null
let historyObserver = null
let cardDrawAnimationTimer = null
let kingQueenEffectiveRequest = null

const createLotteryPrizes = (poolName, count) =>
  Array.from({ length: count }, (_, index) => ({
    id: `${poolName}-${index}`,
    index,
    renderKey: `${poolName}-${index}`,
  }))

const lotteryPoolModes = [
  {
    name: 'lotteryR',
    poolType: 'STANDARD',
    ticketImage: 'ticketR',
    rowCounts: [2, 3, 2],
    listClass: '',
    itemClass: '',
    prizes: createLotteryPrizes('lotteryR', 7),
  },
  {
    name: 'lotterySR',
    poolType: 'PREMIUM',
    ticketImage: 'ticketSR',
    rowCounts: [3],
    listClass: 'lottery-prize-list-large',
    itemClass: 'lottery-prize-item-sr',
    prizes: createLotteryPrizes('lotterySR', 3),
  },
  {
    name: 'lotterySSR',
    poolType: 'LUXURY',
    ticketImage: 'ticketSSR',
    rowCounts: [2],
    listClass: 'lottery-prize-list-large',
    itemClass: 'lottery-prize-item-ssr',
    prizes: createLotteryPrizes('lotterySSR', 2),
  },
]
const collectTicketItems = [
  {
    type: 'STANDARD',
    image: 'ticketR',
  },
  {
    type: 'PREMIUM',
    image: 'ticketSR',
  },
  {
    type: 'LUXURY',
    image: 'ticketSSR',
  },
]
const collectGameModes = [
  {
    name: 'gameR',
    cardGrade: 'R',
    poolType: 'STANDARD',
    title: 'gameTitleR',
    ticketImage: 'ticketR',
    cardTemplateIds: [2001, 2002, 2003, 2004, 2005],
  },
  {
    name: 'gameSR',
    cardGrade: 'SR',
    poolType: 'PREMIUM',
    title: 'gameTitleSR',
    ticketImage: 'ticketSR',
    cardTemplateIds: [2011, 2012, 2013],
  },
  {
    name: 'gameSSR',
    cardGrade: 'SSR',
    poolType: 'LUXURY',
    title: 'gameTitleSSR',
    ticketImage: 'ticketSSR',
    cardTemplateIds: [2021, 2022],
  },
]

// 卡包模板图片映射：接口返回对应 cardTemplateId 后，替换默认 cardBackBg。
const cardTemplateImageMap = {
  2001: 'redeemR2001',
  2002: 'redeemR2002',
  2003: 'redeemR2003',
  2004: 'redeemR2004',
  2005: 'redeemR2005',
  2011: 'redeemSR2011',
  2012: 'redeemSR2012',
  2013: 'redeemSR2013',
  2021: 'redeemSSR2021',
  2022: 'redeemSSR2022',
}

const cardTemplateAnimationMap = Object.fromEntries(
  Object.entries(cardTemplateImageMap).map(([cardTemplateId, imageName]) => [
    Number(cardTemplateId),
    imageName.replace('redeem', 'anima'),
  ]),
)

// 多语言方向：当前只根据语言类型判断是否需要 RTL 布局。
const currentLangType = computed(() => {
  return langStore.selectedLang?.type || 'en'
})

const isRtlLayout = computed(() => currentLangType.value === 'ar')

const imageUrl = (filename) => getWebpUrl(pageConfig.assetPath, filename)
const webpUrl = (filename) => getWebpUrl(pageConfig.assetPath, filename)

// 多语言图片：只在 page.config.js 声明已有阿语图时切换 `_ar`，没有就继续使用原图。
const getImgName = (filename) => {
  if (currentLangType.value !== 'ar') {
    return filename
  }

  const localizedImages = pageConfig.images?.localized || {}
  const arImageList = localizedImages.ar
  const imageConfig = localizedImages[filename]

  if (Array.isArray(arImageList) && arImageList.includes(filename)) {
    return `${filename}_ar`
  }

  if (typeof imageConfig === 'string') {
    return imageConfig
  }

  if (Array.isArray(imageConfig) && imageConfig.includes('ar')) {
    return `${filename}_ar`
  }

  if (imageConfig?.ar) {
    return imageConfig.ar === true ? `${filename}_ar` : imageConfig.ar
  }

  return filename
}

// 一级标签配置归一化：字符串写法和对象写法都转成统一结构。
const normalizeTab = (item, index) => {
  if (typeof item === 'string') {
    return {
      value: item,
      label: item,
      section: pageConfig.modules.sections[index],
    }
  }

  return {
    value: item.value,
    label: item.label || item.labelKey || item.value,
    section: pageConfig.modules.sections[index],
  }
}

// 一级标签：只展示已启用的 section。
const visibleTopTabs = computed(() => {
  if (!pageConfig.modules.tabs.enabled) {
    return []
  }

  return pageConfig.modules.tabs.items
    .map(normalizeTab)
    .filter((tab) => tab.section?.enabled !== false)
})

const currentTopTab = computed(() => {
  return (
    visibleTopTabs.value.find((tab) => tab.value === activeTopTab.value) || visibleTopTabs.value[0]
  )
})

// 当前一级标签和对应 section。
const currentTopSection = computed(() => {
  return currentTopTab.value?.section
})

// 二级标签：读取当前一级 section.children.tabs / children.sections。
const visibleChildTabs = computed(() => {
  const children = currentTopSection.value?.children
  if (!children?.tabs?.enabled) {
    return []
  }

  return children.tabs.items
    .map((item, index) => {
      const section = children.sections?.[index]
      const value = typeof item === 'string' ? item : item.value
      return {
        value,
        label: typeof item === 'string' ? item : item.label || item.labelKey || value,
        section,
      }
    })
    .filter((tab) => tab.section?.enabled !== false)
})

const currentChildTab = computed(() => {
  return (
    visibleChildTabs.value.find((tab) => tab.value === activeChildTab.value) ||
    visibleChildTabs.value[0]
  )
})

// 当前真正生效的内容区：优先取二级标签，没有二级标签时取一级 section。
const activeSection = computed(() => {
  return currentChildTab.value?.section || currentTopSection.value
})

const isPersonalRankingSection = computed(() => {
  return (
    currentTopSection.value?.name === 'personalRank' && currentChildTab.value?.value === 'Ranking'
  )
})

const isRoomRewardSection = computed(() => {
  return currentTopSection.value?.name === 'roomRank' && currentChildTab.value?.value === 'Reward'
})

const isRoomRankingSection = computed(() => {
  return currentTopSection.value?.name === 'roomRank' && currentChildTab.value?.value === 'Ranking'
})

const isRankingSection = computed(() => {
  return isPersonalRankingSection.value || isRoomRankingSection.value
})

const activePreviewImages = computed(() => {
  return activeSection.value?.previewImages || []
})

// 模块大背景容器：md 中 images.container 或 page.config 中 containerImages 都会按顺序渲染。
const activeContainerImages = computed(() => {
  return activeSection.value?.containerImages || activeSection.value?.images?.container || []
})

const barrageRenderList = computed(() => {
  const marqueeList = Array.isArray(cardRewardMarqueeList.value) ? cardRewardMarqueeList.value : []
  if (!marqueeList.length) {
    return []
  }

  return marqueeList.length === 1
    ? [...marqueeList, ...marqueeList, ...marqueeList, ...marqueeList]
    : [...marqueeList, ...marqueeList]
})

const activeLotteryPoolMode = computed(() => {
  return lotteryPoolModes[activeLotteryPoolIndex.value] || lotteryPoolModes[0]
})

const activeLotteryPrizes = computed(() => {
  const poolPrizes = cardRewardPrizePools.value[activeLotteryPoolMode.value.poolType]

  if (!Array.isArray(poolPrizes) || !poolPrizes.length) {
    return activeLotteryPoolMode.value.prizes || []
  }

  return [...poolPrizes]
    .sort((a, b) => Number(a.sortOrder || 0) - Number(b.sortOrder || 0))
    .map((prize, index) => ({
      ...prize,
      index,
      renderKey: `${prize.poolType || activeLotteryPoolMode.value.poolType}-${prize.id}-${index}`,
    }))
})

const activeLotteryTicketImage = computed(
  () => activeLotteryPoolMode.value.ticketImage || 'ticketR',
)

const activeLotteryTicketCount = computed(() =>
  getTicketRemainingCount(activeLotteryPoolMode.value.poolType),
)

const canLotteryRewardDraw = (drawCount) => {
  return activeLotteryTicketCount.value >= drawCount
}

const activeLotteryPrizeRows = computed(() => {
  const prizes = activeLotteryPrizes.value
  const rows = []
  let cursor = 0

  activeLotteryPoolMode.value.rowCounts.forEach((count) => {
    const row = prizes.slice(cursor, cursor + count)
    if (row.length) {
      rows.push(row)
    }
    cursor += count
  })

  if (cursor < prizes.length) {
    rows.push(prizes.slice(cursor))
  }

  return rows
})

const activeCollectGameMode = computed(() => {
  return collectGameModes[activeCollectGameIndex.value] || collectGameModes[0]
})

const activeCollectGameTitle = computed(() => activeCollectGameMode.value.title)

const activeCollectTicketImage = computed(
  () => activeCollectGameMode.value.ticketImage || 'ticketR',
)

const activeCollectCards = computed(() => {
  const cardGrade = activeCollectGameMode.value.cardGrade
  const cards = cardBagPools.value[cardGrade] || []
  const cardMap = new Map(cards.map((card) => [Number(card.cardTemplateId), card]))

  return activeCollectGameMode.value.cardTemplateIds.map((cardTemplateId) => {
    const card = cardMap.get(cardTemplateId)
    return {
      ...card,
      cardTemplateId,
      imageName: card ? cardTemplateImageMap[cardTemplateId] || 'cardBackBg' : 'cardBackBg',
      isCardBack: !card || !cardTemplateImageMap[cardTemplateId],
      quantity: Number(card?.quantity || 0),
    }
  })
})

const activeOwnedCollectCardTemplateIds = computed(() => {
  return activeCollectCards.value
    .filter((card) => !card.isCardBack && card.quantity > 0)
    .map((card) => String(card.cardTemplateId))
})

const activeCollectRequiredCardCount = computed(() => {
  return activeCollectGameMode.value.cardTemplateIds.length
})

const canCombineActiveCards = computed(() => {
  return (
    activeCollectGameMode.value.name !== 'gameSSR' &&
    activeOwnedCollectCardTemplateIds.value.length >= activeCollectRequiredCardCount.value
  )
})

const canRedeemActiveCards = computed(() => {
  return activeOwnedCollectCardTemplateIds.value.length >= activeCollectRequiredCardCount.value
})

const cardDrawTicketCount = computed(() => getTicketRemainingCount('CARD'))

const maskLayerShow = computed(
  () => ruleShow.value || historyShow.value || cardDrawResultShow.value,
)

const activeRankingLoadKey = computed(() => {
  if (isRoomRankingSection.value) {
    return `room-${roomRankingParams.startDate}-${roomRankingParams.endDate}-${roomRankingParams.size}`
  }

  return `personal-${activityType.value}`
})

const rankingTotal = computed(() => ranking.value)

const rankingTop3 = computed(() => {
  const top3 = [...rankingTotal.value]
  if (top3.length < 3) {
    top3.push(
      ...Array.from({ length: 3 - top3.length }, () => ({
        userAvatar: '',
        userName: '',
        amount: '',
      })),
    )
  }
  return top3
})

const rankingListAfterTop3 = computed(() => rankingTotal.value.filter((_, index) => index >= 3))

const isPersonalRewardSection = computed(() => {
  return (
    currentTopSection.value?.name === 'personalRank' && currentChildTab.value?.value === 'Reward'
  )
})

const personalRewardTops = computed(() => {
  return activityRewards.value.slice(0, 3)
})

const changeCollectGameMode = (step) => {
  if (
    isCardDrawing.value ||
    isCardCombining.value ||
    isCardRedeeming.value ||
    cardDrawAnimationShow.value
  ) {
    return
  }

  activeCollectGameIndex.value =
    (activeCollectGameIndex.value + step + collectGameModes.length) % collectGameModes.length
}

const changeLotteryPoolMode = (step) => {
  if (isLotteryRolling.value) {
    return
  }

  activeLotteryPoolIndex.value =
    (activeLotteryPoolIndex.value + step + lotteryPoolModes.length) % lotteryPoolModes.length
  activeLotteryIndex.value = 0
}

// 任务目标展示：接口任务码带 CARD_ 前缀时，按后缀匹配到对应翻译。
const showTarget = (task) => {
  const taskCode = task.taskCode || ''

  if (task.targetType === 'MIC_TIME') {
    const timeType = task.targetValue < 60
    let currentValue = timeType ? task.currentValue : task.currentValue / 60
    let targetValue = timeType ? task.targetValue : task.targetValue / 60
    currentValue = Math.floor(currentValue * 10) / 10
    targetValue = Math.floor(targetValue * 10) / 10
    const timeUnit = timeType ? t('minutes_short') : t('hours_short')

    return t('on_mic_progress', {
      current: currentValue,
      total: targetValue,
      unit: timeUnit,
    })
  }

  if (task.targetType === 'SEND_GIFT') {
    if (taskCode.endsWith('SP_SEND_GIFT_5')) {
      return t('Send 5 Ramadan gifts ({currentValue}/{targetValue})', {
        currentValue: task.currentValue,
        targetValue: task.targetValue,
      })
    }
  }

  if (task.targetType === 'SAVE_COINS') {
    if (taskCode.endsWith('SP_SAVE_50000')) {
      return t('Win 50,000 coins in total across any games ({currentValue}/{targetValue})', {
        currentValue: task.currentValue,
        targetValue: task.targetValue,
      })
    }

    if (taskCode.endsWith('SP_SAVE_100000')) {
      return t('Win 100,000 coins in total across any games ({currentValue}/{targetValue})', {
        currentValue: task.currentValue,
        targetValue: task.targetValue,
      })
    }

    if (taskCode.endsWith('SP_SAVE_500000')) {
      return t('Win 500,000 coins in total across any games ({currentValue}/{targetValue})', {
        currentValue: task.currentValue,
        targetValue: task.targetValue,
      })
    }
  }

  if (task.targetType === 'RECHARGE_AMOUNT') {
    if (taskCode.endsWith('SP_RECHARGE_1')) {
      return t('Daily total recharge amount: $1 ({currentValue}/{targetValue})', {
        currentValue: task.currentValue,
        targetValue: task.targetValue,
      })
    }

    if (taskCode.endsWith('SP_RECHARGE_10')) {
      return t('Daily total recharge amount: $10 ({currentValue}/{targetValue})', {
        currentValue: task.currentValue,
        targetValue: task.targetValue,
      })
    }

    if (taskCode.endsWith('SP_RECHARGE_50')) {
      return t('Daily total recharge amount: $50 ({currentValue}/{targetValue})', {
        currentValue: task.currentValue,
        targetValue: task.targetValue,
      })
    }

    if (taskCode.endsWith('SP_RECHARGE_100')) {
      return t('Daily total recharge amount: $100 ({currentValue}/{targetValue})', {
        currentValue: task.currentValue,
        targetValue: task.targetValue,
      })
    }
  }

  return `${task.taskName}(${task.currentValue}/${task.targetValue})`
}

const getActivityDetail = async () => {
  if (!activityCode.value) {
    activity.value = {}
    return
  }

  const resDetail = await activityDetail(activityCode.value)
  if (resDetail.status && resDetail.body) {
    activity.value = resDetail.body
  } else {
    activity.value = {}
  }
}

const getTaskList = async () => {
  if (!gulbenActivityId) {
    return
  }

  const resTaskList = await ActTaskList(gulbenActivityId)
  if (resTaskList.status && resTaskList.body) {
    taskList.value = resTaskList.body
  }
}

const getTicketRemainingCount = (ticketType) => {
  const ticket = cardTicketList.value.find((item) => item.ticketType === ticketType)
  return Number(ticket?.remainingCount || 0)
}

const getCardTicketList = async () => {
  const resTicketList = await apiGetCardTicketList(gulbenActivityId)
  if (resTicketList.status && resTicketList.body) {
    cardTicketList.value = resTicketList.body
  }
}

const getCardRewardPrizePools = async () => {
  const prizePoolEntries = await Promise.all(
    lotteryPoolModes.map(async (pool) => {
      const resPrizeList = await apiGetCardRewardPrizeList(gulbenActivityId, pool.poolType)
      return [pool.poolType, resPrizeList.status && resPrizeList.body ? resPrizeList.body : []]
    }),
  )

  cardRewardPrizePools.value = Object.fromEntries(prizePoolEntries)
}

const getCardRewardMarquee = async () => {
  const resMarquee = await apiGetCardRewardMarquee(gulbenActivityId)
  if (resMarquee.status && resMarquee.body) {
    cardRewardMarqueeList.value = resMarquee.body
  }
}

const getCardBagListByGrade = async (cardGrade) => {
  if (!cardGrade) {
    return []
  }

  const resCardBag = await apiPostCardBagList({
    activityId: gulbenActivityId,
    cardGrade,
    current: 1,
    size: 20,
  })

  const records = resCardBag.status && resCardBag.body?.records ? resCardBag.body.records : []
  cardBagPools.value = {
    ...cardBagPools.value,
    [cardGrade]: records,
  }

  return records
}

const getCardBagPools = async () => {
  const cardBagEntries = await Promise.all(
    collectGameModes.map(async (mode) => {
      const records = await getCardBagListByGrade(mode.cardGrade)
      return [mode.cardGrade, records]
    }),
  )

  cardBagPools.value = Object.fromEntries(cardBagEntries)
}

const refreshCardBagGrades = async (cardGrades) => {
  const uniqueGrades = [...new Set(cardGrades.filter(Boolean))]
  await Promise.all(uniqueGrades.map((cardGrade) => getCardBagListByGrade(cardGrade)))
}

const getCardDrawResultItem = (body) => {
  if (Array.isArray(body)) {
    return body[0] || null
  }

  if (!body || typeof body !== 'object') {
    return null
  }

  const candidateLists = [body.cards, body.records, body.results, body.rewards]
  const listItem = candidateLists.find((list) => Array.isArray(list) && list.length)?.[0]

  return listItem || body.card || body.cardInfo || body
}

const getCardTemplateId = (item = {}) => {
  return (
    item.cardTemplateId ||
    item.templateId ||
    item.cardId ||
    item.prizeId ||
    item.card?.cardTemplateId ||
    item.card?.templateId ||
    item.prize?.cardTemplateId ||
    item.prize?.templateId ||
    item.prize?.prizeId ||
    ''
  )
}

const getCardTemplateLocalImageUrl = (item = {}) => {
  const cardTemplateId = getCardTemplateId(item)
  const imageName = cardTemplateImageMap[Number(cardTemplateId)]

  return imageName ? imageUrl(imageName) : ''
}

const getCardDrawAnimationUrl = (body) => {
  const resultItem = getCardDrawResultItem(body)
  const animationName = cardTemplateAnimationMap[Number(getCardTemplateId(resultItem || {}))]

  return animationName ? webpUrl(animationName) : ''
}

const getCardDrawResultImageUrl = (body) => {
  const resultItem = getCardDrawResultItem(body)
  if (!resultItem) {
    return imageUrl('cardBackBg')
  }

  const localImage = getCardTemplateLocalImageUrl(resultItem)
  if (localImage) {
    return localImage
  }

  return (
    resultItem.cardImage ||
    resultItem.prizeImage ||
    resultItem.image ||
    resultItem.cover ||
    resultItem.card?.cardImage ||
    resultItem.card?.image ||
    imageUrl('cardBackBg')
  )
}

const getLotteryRewardResultItem = (drawResult = {}) => {
  const rewards = Array.isArray(drawResult?.results) ? drawResult.results : []
  return rewards.find((reward) => reward?.isWin) || rewards[0] || null
}

const getLotteryRewardResultImageUrl = (drawResult = {}) => {
  const resultItem = getLotteryRewardResultItem(drawResult)
  return resultItem?.prizeImage || resultItem?.prize?.prizeImage || ''
}

const getLotteryRewardResultList = (drawResult = {}) => {
  const rewards = Array.isArray(drawResult?.results) ? drawResult.results : []
  return rewards.map((reward, index) => ({
    ...reward,
    imageUrl: reward?.prizeImage || reward?.prize?.prizeImage || '',
    renderKey: reward?.recordNo || reward?.prizeId || `${drawResult.batchNo || 'reward'}-${index}`,
  }))
}

const currentCardDrawResultImageUrl = computed(() => {
  return (
    cardDrawResultList.value[cardDrawResultIndex.value]?.imageUrl || cardDrawResultImageUrl.value
  )
})

const currentCardDrawResult = computed(() => {
  return cardDrawResultList.value[cardDrawResultIndex.value] || null
})

const currentCardDrawResultText = computed(() => {
  if (cardResultActionMode.value !== 'rewardDraw') {
    return ''
  }

  const result = currentCardDrawResult.value
  if (!result) {
    return ''
  }

  const days = result?.prize?.days ?? result.days
  if (days !== undefined && days !== null && days !== '') {
    return t('reward_days', { quantity: days })
  }

  const prizeValue = result?.prize?.prizeValue ?? result.prizeValue
  return prizeValue !== undefined && prizeValue !== null && prizeValue !== ''
    ? String(prizeValue)
    : ''
})

const showRewardResultSwitcher = computed(() => {
  return cardResultActionMode.value === 'rewardDraw' && cardDrawResultList.value.length > 1
})

const changeCardDrawResultIndex = (step) => {
  const resultCount = cardDrawResultList.value.length
  if (resultCount <= 1) {
    return
  }

  cardDrawResultIndex.value = (cardDrawResultIndex.value + step + resultCount) % resultCount
}

// 弹窗互斥：打开某个弹窗前先关闭其它弹窗，避免多个内容同时挂在同一遮罩层中。
const closeOtherPopups = (activePopup = '') => {
  if (activePopup !== 'rule') {
    ruleShow.value = false
  }

  if (activePopup !== 'history') {
    historyShow.value = false
    if (historyObserver) {
      historyObserver.disconnect()
    }
  }

  if (activePopup !== 'result') {
    cardDrawResultShow.value = false
    cardDrawResultList.value = []
    cardDrawResultIndex.value = 0
  }
}

const showCardDrawAnimationThenResult = (animationUrl, actionMode = 'draw') => {
  if (cardDrawAnimationTimer) {
    window.clearTimeout(cardDrawAnimationTimer)
  }

  closeOtherPopups()
  cardResultActionMode.value = actionMode
  cardDrawAnimationUrl.value = animationUrl
  cardDrawAnimationShow.value = Boolean(animationUrl)

  if (!animationUrl) {
    closeOtherPopups('result')
    cardDrawResultShow.value = true
    return
  }

  cardDrawAnimationTimer = window.setTimeout(() => {
    cardDrawAnimationShow.value = false
    cardDrawAnimationTimer = null
    closeOtherPopups('result')
    cardDrawResultShow.value = true
  }, 1500)
}

const handleCardDrawRequest = async (drawCount = 1) => {
  if (isCardDrawing.value || cardDrawAnimationShow.value) {
    return
  }

  isCardDrawing.value = true

  try {
    const resDraw = await apiPostCardDraw({
      activityId: gulbenActivityId,
      drawCount,
      needCoins: cardDrawTicketCount.value === 0,
    })

    if (resDraw.status) {
      cardDrawResultImageUrl.value = getCardDrawResultImageUrl(resDraw.body)
      cardDrawResultList.value = []
      cardDrawResultIndex.value = 0
      showCardDrawAnimationThenResult(getCardDrawAnimationUrl(resDraw.body), 'draw')
      await Promise.all([getCardTicketList(), getCardBagListByGrade('R')])
    }
  } catch (error) {
    showError(error.errorMsg)
  } finally {
    isCardDrawing.value = false
  }
}

const handleCardDraw = useDebounce(handleCardDrawRequest, 600, true)

const getCombineTargetGrade = () => {
  if (activeCollectGameMode.value.cardGrade === 'R') {
    return 'SR'
  }

  if (activeCollectGameMode.value.cardGrade === 'SR') {
    return 'SSR'
  }

  return ''
}

const handleCardCombineRequest = async () => {
  if (isCardCombining.value || cardDrawAnimationShow.value || !canCombineActiveCards.value) {
    return
  }

  isCardCombining.value = true
  const sourceCardGrade = activeCollectGameMode.value.cardGrade
  const targetCardGrade = getCombineTargetGrade()
  const cardTemplateIds = [...activeOwnedCollectCardTemplateIds.value]

  try {
    const resCombine = await apiPostCardCombine({
      activityId: gulbenActivityId,
      cardTemplateIds,
    })

    if (resCombine.status) {
      cardDrawResultImageUrl.value = getCardDrawResultImageUrl(resCombine.body)
      cardDrawResultList.value = []
      cardDrawResultIndex.value = 0
      showCardDrawAnimationThenResult(getCardDrawAnimationUrl(resCombine.body), 'combine')
      await refreshCardBagGrades([sourceCardGrade, targetCardGrade])
    }
  } catch (error) {
    showError(error.errorMsg)
  } finally {
    isCardCombining.value = false
  }
}

const handleCardCombine = useDebounce(handleCardCombineRequest, 600, true)

const handleCardRedeemRequest = async () => {
  if (isCardRedeeming.value || !canRedeemActiveCards.value) {
    return
  }

  isCardRedeeming.value = true
  const sourceCardGrade = activeCollectGameMode.value.cardGrade
  const cardTemplateIds = [...activeOwnedCollectCardTemplateIds.value]

  try {
    const resRedeem = await apiPostCardRedeem({
      activityId: gulbenActivityId,
      cardTemplateIds,
    })

    if (resRedeem.status) {
      await Promise.all([getCardTicketList(), getCardBagListByGrade(sourceCardGrade)])
    }
  } catch (error) {
    showError(error.errorMsg)
  } finally {
    isCardRedeeming.value = false
  }
}

const handleCardRedeem = useDebounce(handleCardRedeemRequest, 600, true)

const initData = async () => {
  await Promise.all([
    getActivityDetail(), // 获取活动详情
    getTaskList(), // 获取任务列表
    getCardTicketList(), // 查询用户票券数量
    getCardRewardPrizePools(), // 查询奖励池奖品列表
    getCardRewardMarquee(), // 查询中奖飘窗列表
    getCardBagPools(), // 查询用户卡包
  ])
}

const receiveTaskRewardRequest = async (code) => {
  if (!gulbenActivityId || !code) {
    return
  }

  try {
    const resReceive = await receiveTickets({
      activityId: gulbenActivityId,
      taskCode: code,
    })
    if (resReceive.status && resReceive.body) {
      getTaskList() // 获取任务列表
      getCardTicketList() // 查询用户票券数量
    }
  } catch (error) {
    showError(error.errorMsg)
  }
}

const receiveTaskReward = useDebounce(receiveTaskRewardRequest, 600, true)

const getWeeklyGifts = async () => {
  const rewardsAndGifts = await getKingQueenEffectiveData()
  if (rewardsAndGifts.gifts?.length) {
    weeklyGifts.value = rewardsAndGifts.gifts.slice(0, 3)
  }
}

const getPersonalRewardExtraItem = (topIndex) => {
  if (topIndex === 0) {
    return {
      type: 'EXTRA_REWARD_ITEM',
      quantity: 30,
      cover: imageUrl('top1ExtraRewardItem'),
      isExtraPreviewReward: true,
    }
  }

  if (topIndex === 1) {
    return {
      type: 'EXTRA_REWARD_ITEM',
      quantity: 30,
      cover: imageUrl('top2ExtraRewardItem'),
      isExtraPreviewReward: true,
    }
  }

  return null
}

const getPersonalRewardList = (rewardTop, topIndex) => {
  const rewards = rewardTop?.rewards || rewardTop?.propsGroup?.activityRewardProps
  if (!Array.isArray(rewards)) {
    return []
  }

  const rewardList = rewards.slice(0, 6)
  const extraReward = getPersonalRewardExtraItem(topIndex)
  if (extraReward && rewardList.length < 6) {
    rewardList.push(extraReward)
  }

  if (rewardList.length === 5) {
    rewardList.push({
      isEmptyPreviewReward: true,
    })
  }

  return rewardList
}

const getRewardTopMainReward = (rewardTop, topIndex) => {
  return getPersonalRewardList(rewardTop, topIndex)[0] || null
}

const getRewardTopGridRows = (rewardTop, topIndex) => {
  const rewards = getPersonalRewardList(rewardTop, topIndex).slice(1, 6)
  return [rewards.slice(0, 2), rewards.slice(2, 4), rewards.slice(4, 5)]
}

const getRewardDisplayText = (reward) => {
  if (!reward || reward.isEmptyPreviewReward) {
    return ''
  }

  return formatRewardDisplay(reward.type, reward)
}

const hasRewardCover = (reward) => {
  return Boolean(reward?.cover)
}

const vipLevelImageMap = {
  VISCOUNT: 'vip1',
  EARL: 'vip2',
  MARQUIS: 'vip3',
  DUKEI: 'vip4',
  KING: 'vip5',
  EMPEROR: 'vip6',
}

const getVipLevelImageName = (vipLevelName = '') => {
  return vipLevelImageMap[String(vipLevelName || '').toUpperCase()] || ''
}

const getLevelBgImageName = (prefix, level) => {
  const levelValue = Number(level) || 0
  const imageIndex = Math.min(5, Math.max(1, Math.ceil(levelValue / 10) || 1))

  return `${prefix}${imageIndex}Bg`
}

const getLotteryPrizeDisplayText = (prize) => {
  if (!prize) {
    return ''
  }

  if (prize.days !== undefined && prize.days !== null && prize.days !== '') {
    return t('reward_days', { quantity: prize.days })
  }

  return prize.prizeValue ?? ''
}

const getHistoryRecordImage = (record = {}) => {
  return (
    getCardTemplateLocalImageUrl(record) || record?.prize?.prizeImage || record.prizeImage || ''
  )
}

const getHistoryRecordPrizeText = (record = {}) => {
  const days = record?.prize?.days ?? record.days
  if (days !== undefined && days !== null && days !== '') {
    return t('reward_days', { quantity: days })
  }

  const prizeValue = record?.prize?.prizeValue ?? record.prizeValue
  return prizeValue !== undefined && prizeValue !== null && prizeValue !== '' ? prizeValue : ''
}

const formatHistoryRecordTime = (time) => {
  return time ? formatUTCCustom(time) : ''
}

const normalizeHistoryRecords = (body) => {
  if (Array.isArray(body?.records)) {
    return body.records
  }

  if (Array.isArray(body)) {
    return body
  }

  return []
}

const getHistoryRecords = async (isReset = false) => {
  if (historyLoading.value || (!historyHasMore.value && !isReset)) {
    return
  }

  historyLoading.value = true

  try {
    const resRecords = await apiPostCardRewardRecordList({
      activityId: gulbenActivityId,
      poolType: activeLotteryPoolMode.value.poolType,
      size: historySize,
      current: historyPage.value,
    })

    const body = resRecords.status ? resRecords.body : null
    const records = normalizeHistoryRecords(body)
    historyRecords.value = isReset ? records : [...historyRecords.value, ...records]

    const total = Number(body?.total || 0)
    historyHasMore.value = total
      ? historyRecords.value.length < total
      : records.length >= historySize
  } catch (error) {
    showError(error.errorMsg)
  } finally {
    historyLoading.value = false
    observeHistoryLoadmore()
  }
}

const getRewardRenderKey = (reward, index) => {
  return [
    index,
    reward?.isEmptyPreviewReward ? 'empty' : '',
    reward?.type || '',
    reward?.detailType || '',
    reward?.cover || '',
  ].join('-')
}

const getKingQueenEffectiveData = async () => {
  if (kingQueenEffectiveData.value) {
    return kingQueenEffectiveData.value
  }

  if (!kingQueenEffectiveRequest) {
    kingQueenEffectiveRequest = getThisWeekRewardsAndGifts(templateId.value)
  }

  try {
    const resRewardsAndGifts = await kingQueenEffectiveRequest
    kingQueenEffectiveData.value =
      resRewardsAndGifts.status && resRewardsAndGifts.body ? resRewardsAndGifts.body : {}
    return kingQueenEffectiveData.value
  } finally {
    kingQueenEffectiveRequest = null
  }
}

const getPersonalRewardConfigs = async () => {
  if (personalRewardLoaded.value) {
    return
  }

  try {
    const rewardsAndGifts = await getKingQueenEffectiveData()
    activityRewards.value = (rewardsAndGifts.butOneRewards || []).slice(0, 3)
    personalRewardLoaded.value = true
  } catch (error) {
    showError(error.errorMsg)
  }
}

const normalizePersonalRankingItem = (item = {}, index = 0) => {
  return {
    ...item,
    rank: item.rank || item.ranking || (index === null ? '' : index + 1),
    userId: item.userId || item.id || '',
    userAvatar: item.avatar || item.userAvatar || '',
    userName: item.userName || item.userNickname || item.nickname || '',
    amount: item.amount ?? item.quantity ?? item.total ?? 0,
    account: item.specialAccount || item.account || '',
    specialAccount: item.specialAccount || '',
    wealthLevel: item.wealthLevel ?? item.userProfile?.wealthLevel ?? 0,
    charmLevel: item.charmLevel ?? item.userProfile?.charmLevel ?? 0,
    vipLevelName: item.vipLevelName || item.userProfile?.vipLevelName || '',
  }
}

const normalizeRoomContributionRankingItem = (item = {}, index = 0) => {
  const profile = item.userProfile || {}
  const specialAccount = profile.ownSpecialId?.account || ''
  const fallbackAccount = profile.account || ''

  return {
    ...item,
    rank: item.rank ?? (index === null ? '' : index + 1),
    userId: profile.id || item.userId || '',
    roomId: profile.inRoomId || '',
    userAvatar: profile.userAvatar || profile.roomIcon || '',
    userName: profile.userNickname || profile.account || '',
    amount: item.totalFormat || item.total || 0,
    account: specialAccount || fallbackAccount,
    specialAccount,
  }
}

const getPersonalRanking = async () => {
  if (rankingLoading.value || !rankingHasMore.value) {
    return
  }

  rankingLoading.value = true

  try {
    const resRanking = await getRankingListAndMyRanking({
      activityType: activityType.value,
    })

    if (resRanking.status && resRanking.body) {
      myRanking.value = normalizePersonalRankingItem(resRanking.body.currentUserRank || {}, null)
      ranking.value = (resRanking.body.rankingList || []).map((item, index) =>
        normalizePersonalRankingItem(item, index),
      )
      rankingHasMore.value = false
      return
    }

    ranking.value = []
    myRanking.value = {}
    rankingHasMore.value = false
  } finally {
    rankingLoading.value = false
  }
}

const getRoomContributionRanking = async () => {
  if (rankingLoading.value || !rankingHasMore.value) {
    return
  }

  rankingLoading.value = true

  try {
    const resRanking = await getRoomContributionTotalRank(roomRankingParams)

    if (resRanking.status && resRanking.body) {
      const records = Array.isArray(resRanking.body.rankList) ? resRanking.body.rankList : []
      ranking.value = records.map((item, index) =>
        normalizeRoomContributionRankingItem(item, index),
      )
      myRanking.value = resRanking.body.current
        ? normalizeRoomContributionRankingItem(resRanking.body.current, null)
        : {}
      rankingHasMore.value = false
      return
    }

    ranking.value = []
    myRanking.value = {}
    rankingHasMore.value = false
  } finally {
    rankingLoading.value = false
  }
}

const getCurrentRankingList = async () => {
  if (isRoomRankingSection.value) {
    await getRoomContributionRanking()
    return
  }

  await getPersonalRanking()
}

const handleRankingClickRequest = (rankingItem = {}) => {
  if (isPersonalRankingSection.value) {
    console.log('[GulbenFestival] Personal Rank click userId:', rankingItem.userId, rankingItem)
    viewUserInfo(rankingItem.userId)
  }
}

const handleRankingClick = useDebounce(handleRankingClickRequest, 600, true)

const loadRankingSection = async () => {
  const rankingType = activeRankingLoadKey.value

  if (rankingLoaded.value && loadedRankingType.value === rankingType) {
    observeRankingLoadmore()
    return
  }

  rankingLoaded.value = true
  loadedRankingType.value = rankingType
  ranking.value = []
  myRanking.value = {}
  rankingHasMore.value = true

  try {
    const loaders = isRoomRankingSection.value
      ? [getRoomContributionRanking()]
      : [getPersonalRanking()]

    if (isPersonalRankingSection.value) {
      loaders.unshift(getWeeklyGifts())
    }

    await Promise.all(loaders)
    observeRankingLoadmore()
  } catch (error) {
    showError(error.errorMsg)
  }
}

const observeRankingLoadmore = async () => {
  await nextTick()
  if (!rankingObserver || !rankingLoadmore.value || !isRankingSection.value) {
    return
  }

  rankingObserver.disconnect()
  rankingObserver.observe(rankingLoadmore.value)
}

const observeHistoryLoadmore = async () => {
  await nextTick()
  if (!historyObserver || !historyLoadmore.value || !historyShow.value || !historyHasMore.value) {
    return
  }

  historyObserver.disconnect()
  historyObserver.observe(historyLoadmore.value)
}

const loadMoreHistoryRecords = useDebounce(async () => {
  if (historyLoading.value || !historyHasMore.value) {
    return
  }

  console.log('上拉加载')
  historyPage.value += 1
  await getHistoryRecords()
}, 300)

const normalizeLotteryIndex = (index) => {
  const prizeCount = activeLotteryPrizes.value.length
  if (!prizeCount) {
    return 0
  }

  return (((Number(index) || 0) % prizeCount) + prizeCount) % prizeCount
}

// 抽奖结果位置计算：优先用接口返回的奖品 id 在奖池中查找位置。
const getLotteryResultIndex = (drawResult) => {
  const rewards = Array.isArray(drawResult?.results) ? drawResult.results : []
  const resultReward = rewards.find((reward) => reward?.isWin) || rewards[0] || drawResult
  const prizeList = activeLotteryPrizes.value

  const matchedPrizeIndex = prizeList.findIndex((prize) => {
    const samePrizeId =
      resultReward?.prizeId !== undefined && String(prize.id) === String(resultReward.prizeId)
    const samePrizeImage =
      resultReward?.prizeImage && prize.prizeImage && prize.prizeImage === resultReward.prizeImage
    const samePrizeTypeAndValue =
      resultReward?.prizeType &&
      prize.prizeType &&
      resultReward.prizeType === prize.prizeType &&
      Number(resultReward?.prizeValue || 0) === Number(prize?.prizeValue || 0)

    return samePrizeId || samePrizeImage || samePrizeTypeAndValue
  })

  if (matchedPrizeIndex >= 0) {
    return normalizeLotteryIndex(matchedPrizeIndex)
  }

  if (typeof drawResult?.prizeIndex === 'number') {
    return normalizeLotteryIndex(drawResult.prizeIndex)
  }

  return normalizeLotteryIndex(0)
}

// 抽奖动画：从左到右、从上到下依次切换奖品背景，最终停在接口返回的中奖位置。
const startLotteryAnimation = (resultIndex) => {
  return new Promise((resolve) => {
    const prizeCount = activeLotteryPrizes.value.length
    if (!prizeCount) {
      resolve()
      return
    }

    if (lotteryAnimationTimer) {
      window.clearTimeout(lotteryAnimationTimer)
    }

    const targetIndex = normalizeLotteryIndex(resultIndex)
    const startIndex =
      activeLotteryIndex.value >= 0 ? normalizeLotteryIndex(activeLotteryIndex.value) : 0
    const distanceToTarget = (targetIndex - startIndex + prizeCount) % prizeCount
    const minRounds = 3
    const totalSteps = prizeCount * minRounds + distanceToTarget + 1
    let step = 0

    const roll = () => {
      if (step >= totalSteps) {
        activeLotteryIndex.value = targetIndex
        lotteryAnimationTimer = null
        window.setTimeout(resolve, 350)
        return
      }

      activeLotteryIndex.value = (startIndex + step) % prizeCount
      step += 1

      const progress = step / totalSteps
      const delay = 130 + Math.round(progress * progress * 220)
      lotteryAnimationTimer = window.setTimeout(roll, delay)
    }

    roll()
  })
}

// 奖励池抽奖：消耗当前奖池奖励票，成功后刷新第一层三种票券数量。
const requestLotteryDraw = async (drawCount) => {
  const resRewardDraw = await apiPostCardRewardDraw({
    activityId: gulbenActivityId,
    ticketType: activeLotteryPoolMode.value.poolType,
    drawCount,
  })

  if (resRewardDraw.status) {
    await getCardTicketList()
  } else {
    return null
  }

  const responseBody = resRewardDraw.body || {}
  const rewards = Array.isArray(responseBody?.rewards)
    ? responseBody.rewards
    : Array.isArray(responseBody?.results)
      ? responseBody.results
      : Array.isArray(responseBody)
        ? responseBody
        : responseBody
          ? [responseBody]
          : []

  return {
    drawCount,
    batchNo: responseBody?.batchNo || '',
    results: rewards,
    raw: responseBody,
  }
}

const handleLotteryDrawRequest = async (drawCount = 1) => {
  if (isLotteryRolling.value || !canLotteryRewardDraw(drawCount)) {
    return
  }

  isLotteryRolling.value = true

  try {
    const drawResult = await requestLotteryDraw(drawCount)
    if (!drawResult) {
      return
    }

    const resultIndex = getLotteryResultIndex(drawResult)
    await startLotteryAnimation(resultIndex)
    closeOtherPopups('result')
    cardResultActionMode.value = 'rewardDraw'
    cardDrawResultImageUrl.value = getLotteryRewardResultImageUrl(drawResult)
    cardDrawResultList.value = getLotteryRewardResultList(drawResult)
    cardDrawResultIndex.value = 0
    cardDrawResultShow.value = true
  } catch (error) {
    showError(error.errorMsg)
  } finally {
    isLotteryRolling.value = false
  }
}

const handleLotteryDraw = useDebounce(handleLotteryDrawRequest, 600, true)

const activeContentTitle = computed(() => {
  if (currentChildTab.value) {
    return `${t(currentTopTab.value?.label || '')} / ${t(currentChildTab.value.label)}`
  }

  return t(currentTopTab.value?.label || '')
})

const padTime = (value) => String(value).padStart(2, '0')

// 获取目标时间（2026年6月7日上午5点，北京时间）
const getTargetTime = () => {
  const target = new Date(2026, 5, 7, 5, 0, 0) // 月份从0开始，5代表6月
  return target.getTime()
}

// 计算倒计时：写法参考 LuckyDollars Season5，四个展示字段单独维护。
const calculateCountdown = () => {
  const targetTime = getTargetTime()
  const diff = targetTime - Date.now()

  if (!targetTime || diff <= 0) {
    Days.value = 0
    Hours.value = '00'
    Minutes.value = '00'
    Seconds.value = '00'

    if (countdownTimer) {
      window.clearInterval(countdownTimer)
      countdownTimer = null
    }
    return
  }

  const totalSeconds = Math.floor(diff / 1000)
  Days.value = Math.floor(totalSeconds / 86400)
  Hours.value = padTime(Math.floor((totalSeconds % 86400) / 3600))
  Minutes.value = padTime(Math.floor((totalSeconds % 3600) / 60))
  Seconds.value = padTime(totalSeconds % 60)
}

// 启动倒计时：立即执行一次，之后每秒刷新。
const startCountdown = () => {
  if (countdownTimer) {
    window.clearInterval(countdownTimer)
  }

  calculateCountdown()
  countdownTimer = window.setInterval(calculateCountdown, 1000)
}

const changeTopTab = (value) => {
  activeTopTab.value = value
}

const changeChildTab = (value) => {
  activeChildTab.value = value
}

// 规则弹窗：参考 LuckyDollars Season5，点击规则入口打开，点击遮罩关闭。
const openRulePopup = () => {
  closeOtherPopups('rule')
  ruleShow.value = true
}

const closeRulePopup = () => {
  closePopup()
}

const openHistoryPopupRequest = async () => {
  closeOtherPopups('history')
  historyShow.value = true
  historyPage.value = 1
  historyHasMore.value = true
  historyRecords.value = []
  await getHistoryRecords(true)
  observeHistoryLoadmore()
}

const openHistoryPopup = useDebounce(openHistoryPopupRequest, 600, true)

const closePopup = () => {
  ruleShow.value = false
  historyShow.value = false
  cardDrawResultShow.value = false
  cardDrawResultList.value = []
  cardDrawResultIndex.value = 0
  cardDrawAnimationShow.value = false

  if (cardDrawAnimationTimer) {
    window.clearTimeout(cardDrawAnimationTimer)
    cardDrawAnimationTimer = null
  }

  if (historyObserver) {
    historyObserver.disconnect()
  }
}

// 图片预加载：支持普通图片名和带 localized 标记的配置项。
const resolvePreloadUrls = (items = []) => {
  return items.map((item) => {
    if (typeof item === 'string') {
      return imageUrl(getImgName(item))
    }

    return imageUrl(item.localized ? getImgName(item.name) : item.name)
  })
}

// 进入页面前阻塞加载首屏关键图，进入后后台加载其它图片。
const completePreloading = async () => {
  try {
    await Promise.all([initData(), preloadImages(resolvePreloadUrls(pageConfig.preload.blocking))])
  } catch (error) {
    console.error('预加载过程中发生错误:', error)
  } finally {
    isLoading.value = false
    preloadImagesIdle([
      ...resolvePreloadUrls(pageConfig.preload.background),
      ...Object.values(cardTemplateAnimationMap).map((imageName) => webpUrl(imageName)),
    ])
    observeRankingLoadmore()
  }
}

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

// 切换一级标签时，同步选中当前模块的默认二级标签。
watch(
  currentTopSection,
  (section) => {
    const firstChild = section?.children?.tabs?.defaultValue || section?.children?.tabs?.items?.[0]
    activeChildTab.value = typeof firstChild === 'string' ? firstChild : firstChild?.value || ''
  },
  { immediate: true },
)

watch(
  isRankingSection,
  (enabled) => {
    if (enabled) {
      loadRankingSection()
    } else if (rankingObserver) {
      rankingObserver.disconnect()
    }
  },
  { immediate: true },
)

watch(activeRankingLoadKey, () => {
  if (isRankingSection.value) {
    loadRankingSection()
  }
})

watch(
  isPersonalRewardSection,
  (enabled) => {
    if (enabled) {
      getPersonalRewardConfigs()
    }
  },
  { immediate: true },
)

// 页面初始化：设置文档方向、加载首屏图片并启动倒计时刷新。
onMounted(() => {
  locale.value && setDocumentDirection(locale.value)
  if ('IntersectionObserver' in window) {
    rankingObserver = new IntersectionObserver((entries) => {
      entries.forEach((entry) => {
        if (
          entry.intersectionRatio > 0 &&
          isRankingSection.value &&
          rankingTotal.value.length !== 0 &&
          rankingHasMore.value &&
          !rankingLoading.value
        ) {
          getCurrentRankingList()
        }
      })
    })

    historyObserver = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          if (entry.intersectionRatio > 0 && historyShow.value) {
            loadMoreHistoryRecords()
          }
        })
      },
      {
        rootMargin: '80px',
        threshold: 0,
      },
    )
  }
  connectToAppHandler()
  startCountdown()
})

// 页面卸载：清理倒计时定时器。
onUnmounted(() => {
  if (countdownTimer) {
    window.clearInterval(countdownTimer)
  }

  if (lotteryAnimationTimer) {
    window.clearTimeout(lotteryAnimationTimer)
  }

  if (rankingObserver) {
    rankingObserver.disconnect()
  }

  if (historyObserver) {
    historyObserver.disconnect()
  }

  if (cardDrawAnimationTimer) {
    window.clearTimeout(cardDrawAnimationTimer)
  }
})
</script>

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

.fullPage {
  width: 100%;
  height: 100vh;
  overflow-y: auto;
  overflow-x: hidden;
  background: #0b190d;
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.fullPage::-webkit-scrollbar,
.scroll::-webkit-scrollbar {
  display: none;
}

.scroll {
  scrollbar-width: none;
  -ms-overflow-style: none;
}

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

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

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

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

/* 第一行：规则按钮 */
.rule-row {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 4vw;
}

[dir='rtl'] .rule-row {
  justify-content: flex-start;
}

/* 第二行：倒计时 */
.countdown-cell {
  width: min(18vw, 88px);
  min-width: 0;
  flex: 0 1 min(18vw, 88px);
}

.countdown-value {
  width: 100%;
  color: #fff8d7;
  font-size: 2em;
  font-weight: 600;
  line-height: 1;
  text-align: center;
  white-space: nowrap;
  text-shadow: 0 1px 2px rgba(63, 20, 57, 0.5);
}

.countdown-colon {
  flex: 0 0 auto;
  color: #fff8d7;
  font-size: 2em;
  font-weight: 800;
  line-height: 1;
  text-shadow: 0 1px 2px rgba(63, 20, 57, 0.5);
}

/* 第三行：一级标签和内容区内的二级标签 */
.image-tab {
  min-width: 0;
  border: 0;
  padding: 0;
  background-color: transparent;
  color: #6d3426;
  font-weight: 800;
  line-height: 1.1;
  text-align: center;
}

.tab-background {
  height: 100%;
  overflow: hidden;
  contain: layout paint;
  isolation: isolate;
  transform: translateZ(0);
}

.tab-background :deep(.scrollY) {
  z-index: 1;
  overflow: hidden;
}

.tab-active-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  display: block;
  object-fit: fill;
  pointer-events: none;
}

.image-tab span {
  display: block;
  width: 100%;
  font-size: 1em;
  overflow-wrap: anywhere;
  color: #9fff71;
}

.top-tab {
  min-height: 18vw;
  font-size: 1em;
}

.child-tab {
  min-height: 12vw;
  font-size: 1em;
}

.image-tab-active {
  text-shadow: 0 1px 2px rgba(74, 30, 27, 0.55);
}

.image-tab-active span {
  font-size: 1em;
  color: #5b0000;
}

/* 第四行：当前标签内容区 */
.barrage-track {
  width: max-content;
  animation: gulben-barrage-scroll 80s linear infinite;
  will-change: transform;
}

@keyframes gulben-barrage-scroll {
  0% {
    transform: translateX(100vw);
  }

  100% {
    transform: translateX(-100%);
  }
}

.pool-arrow {
  width: 8vw;
  display: block;
}

.pool-arrow-next {
  transform: scaleX(-1);
}

.result-popup-arrow {
  width: 8vw;
  display: block;
}

.result-popup-arrow-next {
  transform: scaleX(-1);
}

[dir='rtl'] .pool-arrow-prev {
  transform: scaleX(-1);
}

[dir='rtl'] .pool-arrow-next {
  transform: scaleX(1);
}

.lottery-prize-list {
  --lottery-prize-gap: 3vw;
}

.lottery-prize-list-large {
  --lottery-prize-gap: 1vw;
}

.lottery-prize-item {
  width: calc((100% - var(--lottery-prize-gap) * 2) / 3);
  min-width: 0;
  flex: 0 0 calc((100% - var(--lottery-prize-gap) * 2) / 3);
  overflow: hidden;
  contain: layout paint;
  isolation: isolate;
  transform: translateZ(0);
}

.lottery-prize-item :deep(.scrollY) {
  z-index: 1;
}

.lottery-prize-active-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  display: block;
  object-fit: fill;
  pointer-events: none;
}

.lottery-prize-item-sr {
  width: calc((100% - var(--lottery-prize-gap) * 2) / 3);
  flex-basis: calc((100% - var(--lottery-prize-gap) * 2) / 3);
}

.lottery-prize-item-ssr {
  width: calc((100% - var(--lottery-prize-gap)) / 2.2);
  flex-basis: calc((100% - var(--lottery-prize-gap)) / 2.2);
}

.preview-image {
  width: min(58vw, 280px);
  max-height: 42vw;
  display: block;
  object-fit: contain;
}

.room-rule-text {
  width: 100%;
  color: #fff;
  font-size: 1.2em;
  font-weight: 600;
  line-height: 1.35;
  text-align: left;
  white-space: pre-wrap;
}

[dir='rtl'] .room-rule-text {
  text-align: right;
}

.ranking-avatar {
  border-radius: 50%;
}

.ranking-avatar-square {
  border-radius: 4px;
}

.rule-popup-scroll,
.history-popup-scroll {
  scrollbar-width: none;
  -ms-overflow-style: none;
}

.rule-popup-scroll::-webkit-scrollbar,
.history-popup-scroll::-webkit-scrollbar {
  display: none;
}

.collect-action-disabled {
  filter: grayscale(1);
  opacity: 0.55;
}
</style>
