<template>
  <div class="app-container aslan-gift-challenge">
    <div class="filter-container">
      <el-button
        v-if="canEdit"
        class="filter-item"
        type="primary"
        icon="el-icon-plus"
        @click="openCreate"
      >
        新建活动
      </el-button>
      <el-button
        class="filter-item"
        icon="el-icon-refresh"
        :loading="listLoading"
        @click="loadActivities"
      >
        刷新
      </el-button>
      <el-alert
        class="page-tip"
        type="info"
        :closable="false"
        show-icon
        title="积分从活动开始时间起按 1 金币 = 1 积分累计；普通幸运礼物与超级幸运礼物由服务端强制排除，不参与榜单或送礼任务。"
      />
    </div>

    <el-table v-loading="listLoading" :data="activities" border fit highlight-current-row>
      <el-table-column label="活动" min-width="230">
        <template slot-scope="scope">
          <div class="primary-text">{{ scope.row.activityName }}</div>
          <div class="secondary-text">{{ scope.row.activityCode }}</div>
          <div class="secondary-text">ID: {{ scope.row.id }}</div>
        </template>
      </el-table-column>
      <el-table-column label="平台 / 时区" min-width="140" align="center">
        <template slot-scope="scope">
          <div>{{ scope.row.sysOrigin }}</div>
          <div class="secondary-text">{{ scope.row.timeZone || 'Asia/Riyadh' }}</div>
        </template>
      </el-table-column>
      <el-table-column label="活动时间" min-width="210" align="center">
        <template slot-scope="scope">
          <div>{{ formatTime(scope.row.startTime) }}</div>
          <div>{{ formatTime(scope.row.endTime) }}</div>
        </template>
      </el-table-column>
      <el-table-column label="榜单配置" min-width="155" align="center">
        <template slot-scope="scope">
          <div>展示 Top {{ scope.row.displayTopN || 100 }}</div>
          <div class="secondary-text">日榜等待 {{ scope.row.dailySettlementDelayMinutes || 0 }} 分钟</div>
          <div class="secondary-text">总榜等待 {{ scope.row.overallSettlementDelayMinutes || 0 }} 分钟</div>
        </template>
      </el-table-column>
      <el-table-column label="状态" min-width="150" align="center">
        <template slot-scope="scope">
          <el-tag :type="activityStatusTag(activityStatus(scope.row))">
            {{ activityStatusText(activityStatus(scope.row)) }}
          </el-tag>
          <div class="secondary-text">总榜结算：{{ settlementStatusText(scope.row.overallSettlementStatus) }}</div>
          <div class="secondary-text">可结算：{{ formatTime(scope.row.overallSettlementTime) }}</div>
        </template>
      </el-table-column>
      <el-table-column label="启用" width="90" align="center">
        <template slot-scope="scope">
          <el-switch
            v-model="scope.row.enabled"
            :disabled="!canEnable || hasStarted(scope.row)"
            @change="changeEnabled(scope.row)"
          />
        </template>
      </el-table-column>
      <el-table-column label="操作" min-width="245" align="center" fixed="right">
        <template slot-scope="scope">
          <el-button type="text" @click="openEdit(scope.row)">
            {{ canEdit ? '配置' : '查看配置' }}
          </el-button>
          <el-button type="text" @click="openRanking(scope.row)">查看榜单</el-button>
          <el-button type="text" @click="openSettlements(scope.row)">结算记录</el-button>
        </template>
      </el-table-column>
    </el-table>

    <el-dialog
      :title="form.id ? '编辑 Aslan 礼物挑战' : '新建 Aslan 礼物挑战'"
      :visible.sync="editorVisible"
      width="1240px"
      top="3vh"
      :close-on-click-modal="false"
      @closed="resetEditor"
    >
      <div v-loading="editorLoading" class="editor-body">
        <el-alert
          v-if="startedLocked"
          type="warning"
          :closable="false"
          show-icon
          title="活动已经开始：仅名称和说明可修改；时间、榜单容量、结算窗口、任务和奖励均被服务端锁定。"
        />

        <div class="section-title">基础活动配置</div>
        <el-form ref="activityForm" :model="form" :rules="formRules" label-width="145px">
          <el-row :gutter="20">
            <el-col :span="12">
              <el-form-item label="活动编码" prop="activityCode">
                <el-input
                  v-model.trim="form.activityCode"
                  maxlength="64"
                  show-word-limit
                  :disabled="editorReadOnly"
                  placeholder="例如 ASLAN_GIFT_CHALLENGE_S1"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="活动名称" prop="activityName">
                <el-input
                  v-model.trim="form.activityName"
                  maxlength="128"
                  show-word-limit
                  :disabled="!canEdit"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="所属平台" prop="sysOrigin">
                <el-input v-model="form.sysOrigin" disabled />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="活动时区" prop="timeZone">
                <el-select
                  v-model="form.timeZone"
                  filterable
                  allow-create
                  default-first-option
                  style="width: 100%"
                  :disabled="editorReadOnly"
                  placeholder="IANA 时区，例如 Asia/Riyadh"
                >
                  <el-option v-for="item in timeZoneOptions" :key="item" :label="item" :value="item" />
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="活动时间" prop="timeRange">
                <el-date-picker
                  v-model="form.timeRange"
                  type="datetimerange"
                  value-format="timestamp"
                  style="width: 100%"
                  range-separator="至"
                  start-placeholder="开始时间"
                  end-placeholder="结束时间"
                  :disabled="editorReadOnly"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="榜单展示 TopN" prop="displayTopN">
                <el-input-number
                  v-model="form.displayTopN"
                  :min="1"
                  :max="500"
                  :precision="0"
                  controls-position="right"
                  style="width: 100%"
                  :disabled="editorReadOnly"
                />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="日榜晚到窗口" prop="dailySettlementDelayMinutes">
                <el-input-number
                  v-model="form.dailySettlementDelayMinutes"
                  :min="0"
                  :max="1440"
                  :precision="0"
                  controls-position="right"
                  style="width: 100%"
                  :disabled="editorReadOnly"
                />
                <div class="form-help">每日结束后继续接收异步礼物流水的分钟数。</div>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="总榜晚到窗口" prop="overallSettlementDelayMinutes">
                <el-input-number
                  v-model="form.overallSettlementDelayMinutes"
                  :min="0"
                  :max="1440"
                  :precision="0"
                  controls-position="right"
                  style="width: 100%"
                  :disabled="editorReadOnly"
                />
                <div class="form-help">活动结束后等待异步流水，再冻结活动总榜。</div>
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="积分比例">
                <el-input value="1 金币 = 1 积分（服务端固定）" disabled />
              </el-form-item>
            </el-col>
            <el-col :span="12">
              <el-form-item label="排除礼物">
                <el-input value="普通幸运礼物、超级幸运礼物（服务端强制）" disabled />
              </el-form-item>
            </el-col>
            <el-col :span="24">
              <el-form-item label="活动说明">
                <el-input
                  v-model.trim="form.activityDesc"
                  type="textarea"
                  :rows="3"
                  maxlength="1000"
                  show-word-limit
                  :disabled="!canEdit"
                />
              </el-form-item>
            </el-col>
          </el-row>
        </el-form>

        <div class="section-title">每日任务（固定 3 个槽位）</div>
        <el-alert
          type="info"
          :closable="false"
          show-icon
          title="任务固定为 1 个每日进入页面和 2 个累计送礼金币档位；第二档门槛必须高于第一档，运营只配置展示文案、门槛和奖励。"
        />
        <el-table :data="form.tasks" border class="config-table">
          <el-table-column label="槽位" width="58" align="center">
            <template slot-scope="scope">{{ scope.$index + 1 }}</template>
          </el-table-column>
          <el-table-column label="启用" width="70" align="center">
            <template slot-scope="scope">
              <el-switch v-model="scope.row.enabled" disabled />
            </template>
          </el-table-column>
          <el-table-column label="任务编码" min-width="165">
            <template slot-scope="scope">
              <span>{{ scope.row.taskCode }}</span>
            </template>
          </el-table-column>
          <el-table-column label="任务类型" min-width="170">
            <template slot-scope="scope">
              <el-tag size="small">{{ scope.row.taskType === 'ENTER_PAGE' ? '每日进入活动页面' : '每日累计送礼金币' }}</el-tag>
            </template>
          </el-table-column>
          <el-table-column label="任务标题" min-width="180">
            <template slot-scope="scope">
              <el-input
                v-model.trim="scope.row.taskTitle"
                maxlength="128"
                :disabled="editorReadOnly"
                placeholder="H5 展示标题"
              />
            </template>
          </el-table-column>
          <el-table-column label="目标值" min-width="120">
            <template slot-scope="scope">
              <el-input-number
                v-model="scope.row.targetValue"
                :min="1"
                :max="999999999999"
                :precision="0"
                controls-position="right"
                style="width: 100%"
                :disabled="editorReadOnly || scope.row.taskType === 'ENTER_PAGE'"
              />
            </template>
          </el-table-column>
          <el-table-column label="排序" width="95">
            <template slot-scope="scope">
              <span>{{ scope.row.sortOrder }}</span>
            </template>
          </el-table-column>
          <el-table-column label="奖励资源组" min-width="210">
            <template slot-scope="scope">
              <el-button type="text" :disabled="editorReadOnly" @click="selectResourceGroup('task', scope.$index)">
                {{ scope.row.resourceGroupId ? `资源组 ${scope.row.resourceGroupId}` : '选择资源组' }}
              </el-button>
              <props-row v-if="scope.row.rewards && scope.row.rewards.length" :list="scope.row.rewards" />
            </template>
          </el-table-column>
        </el-table>
        <div class="task-desc-grid">
          <div v-for="(task, index) in form.tasks" :key="`desc-${index}`" class="task-desc-item">
            <div class="task-desc-label">槽位 {{ index + 1 }} 描述</div>
            <el-input
              v-model.trim="task.taskDesc"
              maxlength="500"
              show-word-limit
              :disabled="editorReadOnly"
              placeholder="选填，H5 展示说明"
            />
          </div>
        </div>

        <div class="section-title rank-title-row">
          <span>日榜奖励区间</span>
          <el-button type="primary" size="mini" icon="el-icon-plus" :disabled="editorReadOnly" @click="addRankReward('DAILY')">
            新增日榜区间
          </el-button>
        </div>
        <el-alert
          type="warning"
          :closable="false"
          show-icon
          title="同一周期内名次区间不能重叠，结束名次不能超过 TopN；允许留空档，但至少配置一档。"
        />
        <el-table :data="form.dailyRankRewards" border class="config-table">
          <el-table-column label="开始名次" width="125">
            <template slot-scope="scope">
              <el-input-number v-model="scope.row.startRank" :min="1" :max="form.displayTopN" :precision="0" controls-position="right" style="width: 100%" :disabled="editorReadOnly" />
            </template>
          </el-table-column>
          <el-table-column label="结束名次" width="125">
            <template slot-scope="scope">
              <el-input-number v-model="scope.row.endRank" :min="1" :max="form.displayTopN" :precision="0" controls-position="right" style="width: 100%" :disabled="editorReadOnly" />
            </template>
          </el-table-column>
          <el-table-column label="奖励名称" min-width="190">
            <template slot-scope="scope">
              <el-input v-model.trim="scope.row.rewardName" maxlength="128" :disabled="editorReadOnly" placeholder="例如 日榜 Top 1" />
            </template>
          </el-table-column>
          <el-table-column label="奖励资源组" min-width="310">
            <template slot-scope="scope">
              <el-button type="text" :disabled="editorReadOnly" @click="selectResourceGroup('daily', scope.$index)">
                {{ scope.row.resourceGroupId ? `资源组 ${scope.row.resourceGroupId}` : '选择资源组' }}
              </el-button>
              <props-row v-if="scope.row.rewards && scope.row.rewards.length" :list="scope.row.rewards" />
            </template>
          </el-table-column>
          <el-table-column label="操作" width="80" align="center">
            <template slot-scope="scope">
              <el-button type="text" class="danger-link" :disabled="editorReadOnly || form.dailyRankRewards.length <= 1" @click="removeRankReward('DAILY', scope.$index)">删除</el-button>
            </template>
          </el-table-column>
        </el-table>

        <div class="section-title rank-title-row">
          <span>活动总榜奖励区间</span>
          <el-button type="primary" size="mini" icon="el-icon-plus" :disabled="editorReadOnly" @click="addRankReward('OVERALL')">
            新增总榜区间
          </el-button>
        </div>
        <el-table :data="form.overallRankRewards" border class="config-table">
          <el-table-column label="开始名次" width="125">
            <template slot-scope="scope">
              <el-input-number v-model="scope.row.startRank" :min="1" :max="form.displayTopN" :precision="0" controls-position="right" style="width: 100%" :disabled="editorReadOnly" />
            </template>
          </el-table-column>
          <el-table-column label="结束名次" width="125">
            <template slot-scope="scope">
              <el-input-number v-model="scope.row.endRank" :min="1" :max="form.displayTopN" :precision="0" controls-position="right" style="width: 100%" :disabled="editorReadOnly" />
            </template>
          </el-table-column>
          <el-table-column label="奖励名称" min-width="190">
            <template slot-scope="scope">
              <el-input v-model.trim="scope.row.rewardName" maxlength="128" :disabled="editorReadOnly" placeholder="例如 总榜 Top 1" />
            </template>
          </el-table-column>
          <el-table-column label="奖励资源组" min-width="310">
            <template slot-scope="scope">
              <el-button type="text" :disabled="editorReadOnly" @click="selectResourceGroup('overall', scope.$index)">
                {{ scope.row.resourceGroupId ? `资源组 ${scope.row.resourceGroupId}` : '选择资源组' }}
              </el-button>
              <props-row v-if="scope.row.rewards && scope.row.rewards.length" :list="scope.row.rewards" />
            </template>
          </el-table-column>
          <el-table-column label="操作" width="80" align="center">
            <template slot-scope="scope">
              <el-button type="text" class="danger-link" :disabled="editorReadOnly || form.overallRankRewards.length <= 1" @click="removeRankReward('OVERALL', scope.$index)">删除</el-button>
            </template>
          </el-table-column>
        </el-table>
      </div>

      <span slot="footer">
        <el-button @click="editorVisible = false">关闭</el-button>
        <el-button v-if="canEdit" type="primary" :loading="saveLoading" @click="submitActivity">
          保存全部配置
        </el-button>
      </span>
    </el-dialog>

    <el-dialog
      :title="`${selectedActivity.activityName || ''} - 排行榜`"
      :visible.sync="rankingVisible"
      width="930px"
      top="6vh"
      :close-on-click-modal="false"
    >
      <div class="dialog-actions">
        <el-select v-model="rankingQuery.periodType" @change="rankingPeriodChanged">
          <el-option label="活动总榜" value="OVERALL" />
          <el-option label="日榜" value="DAILY" />
        </el-select>
        <el-date-picker
          v-if="rankingQuery.periodType === 'DAILY'"
          v-model="rankingQuery.statDate"
          type="date"
          value-format="yyyy-MM-dd"
          placeholder="选择活动日"
        />
        <el-input-number v-model="rankingQuery.limit" :min="1" :max="500" :precision="0" controls-position="right" />
        <el-button type="primary" icon="el-icon-search" :loading="rankingLoading" @click="loadRanking">查询</el-button>
        <el-tag :type="rankingResult.settled ? 'success' : 'info'">
          {{ rankingResult.settled ? '已生成结算快照' : '未结算' }}
        </el-tag>
      </div>
      <el-table v-loading="rankingLoading" :data="rankingResult.entries || []" border max-height="560">
        <el-table-column label="名次" width="80" align="center" prop="rank" />
        <el-table-column label="用户 ID" min-width="165" prop="userId" />
        <el-table-column label="账号" min-width="125" prop="account" />
        <el-table-column label="昵称" min-width="160" show-overflow-tooltip prop="nickname" />
        <el-table-column label="积分" min-width="140" align="right">
          <template slot-scope="scope">{{ formatNumber(scope.row.score) }}</template>
        </el-table-column>
      </el-table>
    </el-dialog>

    <el-dialog
      :title="`${selectedActivity.activityName || ''} - 结算记录`"
      :visible.sync="settlementsVisible"
      width="1240px"
      top="4vh"
      :close-on-click-modal="false"
    >
      <div class="dialog-actions settlement-actions">
        <el-select v-model="settlementQuery.periodType" clearable placeholder="全部周期" @change="settlementPeriodChanged">
          <el-option label="活动总榜" value="OVERALL" />
          <el-option label="日榜" value="DAILY" />
        </el-select>
        <el-date-picker
          v-if="settlementQuery.periodType === 'DAILY'"
          v-model="settlementQuery.statDate"
          type="date"
          value-format="yyyy-MM-dd"
          placeholder="选择活动日"
        />
        <el-select v-model="settlementQuery.deliveryStatus" clearable placeholder="全部发放状态">
          <el-option label="待发放" value="PENDING" />
          <el-option label="发放中" value="PROCESSING" />
          <el-option label="成功" value="SUCCESS" />
          <el-option label="结果未知（需核账）" value="UNKNOWN" />
        </el-select>
        <el-input-number v-model="settlementQuery.limit" :min="1" :max="500" :precision="0" controls-position="right" />
        <el-button icon="el-icon-search" :loading="settlementsLoading" @click="loadSettlements">查询</el-button>
        <el-button
          v-if="canSettle"
          type="danger"
          icon="el-icon-s-check"
          :loading="settleLoading"
          :disabled="!settlementQuery.periodType"
          @click="manualSettle"
        >
          手动结算当前周期
        </el-button>
      </div>
      <el-alert
        type="warning"
        :closable="false"
        show-icon
        title="冻结榜单前先确认 ASLAN_GIFT_CHALLENGE_GIFT 消费组无不可接受积压；UNKNOWN 表示奖励可能已到账，只有核账确认未到账才会开放该单项补发。"
      />
      <el-table v-loading="settlementsLoading" :data="settlementRecords" border max-height="580" class="settlement-table">
        <el-table-column type="expand" width="50">
          <template slot-scope="scope">
            <el-table :data="scope.row.deliveryItems || []" border size="mini" class="delivery-item-table">
              <el-table-column label="奖励项 ID" min-width="150" prop="id" />
              <el-table-column label="类型" min-width="135">
                <template slot-scope="itemScope">
                  {{ itemScope.row.rewardType }} / {{ itemScope.row.detailType || '-' }}
                </template>
              </el-table-column>
              <el-table-column label="内容" min-width="150" show-overflow-tooltip prop="content" />
              <el-table-column label="数量" width="75" align="right" prop="quantity" />
              <el-table-column label="状态" width="105" align="center">
                <template slot-scope="itemScope">
                  <el-tag :type="deliveryStatusTag(itemScope.row.deliveryStatus)">{{ itemScope.row.deliveryStatus }}</el-tag>
                </template>
              </el-table-column>
              <el-table-column label="状态说明" min-width="190" show-overflow-tooltip prop="failureReason" />
              <el-table-column label="UNKNOWN 核账" min-width="190" align="center" fixed="right">
                <template slot-scope="itemScope">
                  <template v-if="itemScope.row.deliveryStatus === 'UNKNOWN' && canReconcile">
                    <el-button type="text" :loading="resolvingDeliveryItemId === itemScope.row.id" @click="resolveUnknownDeliveryItem(itemScope.row, true)">已到账</el-button>
                    <el-button type="text" class="danger-link" :loading="resolvingDeliveryItemId === itemScope.row.id" @click="resolveUnknownDeliveryItem(itemScope.row, false)">未到账并补发</el-button>
                  </template>
                </template>
              </el-table-column>
            </el-table>
          </template>
        </el-table-column>
        <el-table-column label="周期" width="90" align="center">
          <template slot-scope="scope">{{ periodText(scope.row.periodType) }}</template>
        </el-table-column>
        <el-table-column label="统计日" width="115" align="center">
          <template slot-scope="scope">{{ scope.row.statDate || '-' }}</template>
        </el-table-column>
        <el-table-column label="名次" width="70" align="center" prop="rank" />
        <el-table-column label="用户 ID" min-width="155" prop="userId" />
        <el-table-column label="积分" min-width="110" align="right">
          <template slot-scope="scope">{{ formatNumber(scope.row.score) }}</template>
        </el-table-column>
        <el-table-column label="资源组" min-width="145" prop="resourceGroupId" />
        <el-table-column label="状态" width="105" align="center">
          <template slot-scope="scope">
            <el-tag :type="deliveryStatusTag(scope.row.deliveryStatus)">{{ scope.row.deliveryStatus }}</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="补发次数" width="90" align="center" prop="retryCount" />
        <el-table-column label="状态说明" min-width="180" show-overflow-tooltip prop="failureReason" />
      </el-table>
    </el-dialog>

    <associated-resources
      v-if="resourceSelectorVisible"
      :sys-origin="form.sysOrigin"
      @select="applyResourceGroup"
      @close="closeResourceSelector"
    />
  </div>
</template>

<script>
import { mapGetters } from 'vuex'
import AssociatedResources from '@/components/data/AssociatedResources'
import PropsRow from '@/components/data/PropsRow'
import {
  getAslanGiftChallengeDetail,
  getAslanGiftChallengeRanking,
  listAslanGiftChallengeActivities,
  listAslanGiftChallengeRankRewards,
  listAslanGiftChallengeSettlementRecords,
  listAslanGiftChallengeTasks,
  resolveAslanGiftChallengeUnknownDeliveryItem,
  saveAslanGiftChallengeActivity,
  setAslanGiftChallengeEnabled,
  settleAslanGiftChallengeActivity
} from '@/api/aslan-gift-challenge'

const PERIOD_DAILY = 'DAILY'
const PERIOD_OVERALL = 'OVERALL'
const TASK_ENTER_PAGE = 'ENTER_PAGE'
const TASK_SEND_GIFT_GOLD = 'SEND_GIFT_GOLD'

export default {
  name: 'ActivityGiftChallenge',
  components: { AssociatedResources, PropsRow },
  data() {
    return {
      activities: [],
      listLoading: false,
      editorVisible: false,
      editorLoading: false,
      saveLoading: false,
      serverTime: 0,
      originalStartTime: 0,
      backendActivityStatus: '',
      form: this.emptyForm(),
      formRules: {
        activityCode: [{ required: true, message: '请输入活动编码', trigger: 'blur' }],
        activityName: [{ required: true, message: '请输入活动名称', trigger: 'blur' }],
        sysOrigin: [{ required: true, message: '所属平台不能为空', trigger: 'change' }],
        timeZone: [{ required: true, message: '请输入活动时区', trigger: 'change' }],
        timeRange: [{ type: 'array', required: true, len: 2, message: '请选择完整活动时间', trigger: 'change' }],
        displayTopN: [{ required: true, message: '请输入榜单展示数量', trigger: 'blur' }],
        dailySettlementDelayMinutes: [{ required: true, message: '请输入日榜晚到窗口', trigger: 'blur' }],
        overallSettlementDelayMinutes: [{ required: true, message: '请输入总榜晚到窗口', trigger: 'blur' }]
      },
      timeZoneOptions: ['Asia/Riyadh', 'Asia/Shanghai', 'Europe/Istanbul', 'UTC'],
      resourceSelectorVisible: false,
      resourceSelection: null,
      selectedActivity: {},
      rankingVisible: false,
      rankingLoading: false,
      rankingQuery: {
        periodType: PERIOD_OVERALL,
        statDate: '',
        limit: 100
      },
      rankingResult: {
        settled: false,
        entries: []
      },
      settlementsVisible: false,
      settlementsLoading: false,
      settleLoading: false,
      settlementQuery: {
        periodType: PERIOD_OVERALL,
        statDate: '',
        deliveryStatus: '',
        limit: 100
      },
      settlementRecords: [],
      resolvingDeliveryItemId: ''
    }
  },
  computed: {
    ...mapGetters(['buttonPermissions']),
    canEdit() {
      return this.hasButtonPermission('activity:gift-challenge:edit')
    },
    canEnable() {
      return this.hasButtonPermission('activity:gift-challenge:enable')
    },
    canSettle() {
      return this.hasButtonPermission('activity:gift-challenge:settle')
    },
    canReconcile() {
      return this.hasButtonPermission('activity:gift-challenge:reconcile')
    },
    startedLocked() {
      if (!this.form.id) {
        return false
      }
      // 后端返回的活动状态和 serverTime 是锁定判断基准，不能依赖运营电脑的本地时间绕开规则冻结。
      return ['ACTIVE', 'ENDED'].indexOf(this.backendActivityStatus) >= 0 ||
        (this.serverTime > 0 && this.originalStartTime > 0 && this.serverTime >= this.originalStartTime)
    },
    editorReadOnly() {
      return !this.canEdit || this.startedLocked
    }
  },
  created() {
    this.loadActivities()
  },
  methods: {
    emptyForm() {
      return {
        id: '',
        activityCode: '',
        activityName: '',
        activityDesc: '',
        sysOrigin: 'ATYOU',
        timeZone: 'Asia/Riyadh',
        timeRange: [],
        dailySettlementDelayMinutes: 30,
        overallSettlementDelayMinutes: 30,
        displayTopN: 100,
        enabled: false,
        version: null,
        tasks: this.emptyTasks(),
        dailyRankRewards: [this.emptyRankReward(PERIOD_DAILY, 1)],
        overallRankRewards: [this.emptyRankReward(PERIOD_OVERALL, 1)]
      }
    },
    emptyTasks() {
      // 产品固定一个进入页任务和两档送礼任务；编码、类型和排序不是运营配置项。
      return [
        this.emptyTask('daily_enter', TASK_ENTER_PAGE, '每日进入活动页面', 1, 1),
        this.emptyTask('daily_gift_1', TASK_SEND_GIFT_GOLD, '每日累计送礼任务一', 1, 2),
        this.emptyTask('daily_gift_2', TASK_SEND_GIFT_GOLD, '每日累计送礼任务二', 1, 3)
      ]
    },
    emptyTask(taskCode, taskType, taskTitle, targetValue, sortOrder) {
      return {
        id: '',
        activityId: '',
        taskCode,
        taskType,
        taskTitle,
        taskDesc: '',
        targetValue,
        resourceGroupId: '',
        enabled: true,
        sortOrder,
        rewards: []
      }
    },
    emptyRankReward(periodType, startRank) {
      return {
        id: '',
        activityId: '',
        periodType,
        startRank,
        endRank: startRank,
        resourceGroupId: '',
        rewardName: '',
        rewards: []
      }
    },
    hasButtonPermission(alias) {
      return Array.isArray(this.buttonPermissions) && this.buttonPermissions.indexOf(alias) >= 0
    },
    loadActivities() {
      this.listLoading = true
      listAslanGiftChallengeActivities().then(res => {
        this.activities = Array.isArray(res.body) ? res.body : []
      }).finally(() => {
        this.listLoading = false
      })
    },
    openCreate() {
      this.resetEditor()
      this.editorVisible = true
    },
    openEdit(row) {
      this.resetEditor()
      this.editorVisible = true
      this.editorLoading = true
      // 详情、任务和排行奖励分别走已发布的管理端点；即使以后详情做裁剪，配置页仍不会丢失子配置。
      Promise.all([
        getAslanGiftChallengeDetail(row.id),
        listAslanGiftChallengeTasks(row.id),
        listAslanGiftChallengeRankRewards(row.id)
      ]).then(results => {
        const detail = results[0].body || {}
        const activity = detail.activity || {}
        const tasks = Array.isArray(results[1].body) ? results[1].body : (detail.tasks || [])
        const rewards = Array.isArray(results[2].body) ? results[2].body : (detail.rankRewards || [])
        this.form = Object.assign(this.emptyForm(), activity, {
          sysOrigin: 'ATYOU',
          timeZone: activity.timeZone || 'Asia/Riyadh',
          dailySettlementDelayMinutes: this.numberOrDefault(activity.dailySettlementDelayMinutes, 30),
          overallSettlementDelayMinutes: this.numberOrDefault(activity.overallSettlementDelayMinutes, 30),
          displayTopN: this.numberOrDefault(activity.displayTopN, 100),
          timeRange: [activity.startTime, activity.endTime],
          tasks: this.normalizeTasks(tasks),
          dailyRankRewards: this.normalizeRankRewards(rewards, PERIOD_DAILY),
          overallRankRewards: this.normalizeRankRewards(rewards, PERIOD_OVERALL)
        })
        this.serverTime = Number(detail.serverTime || 0)
        this.originalStartTime = Number(activity.startTime || 0)
        this.backendActivityStatus = detail.activityStatus || ''
      }).finally(() => {
        this.editorLoading = false
      })
    },
    normalizeTasks(tasks) {
      const defaults = this.emptyTasks()
      if (!Array.isArray(tasks) || tasks.length === 0) {
        return defaults
      }
      return tasks.slice().sort((left, right) => Number(left.sortOrder) - Number(right.sortOrder))
        .slice(0, 3).map((item, index) => Object.assign({}, defaults[index], item, {
          taskCode: defaults[index].taskCode,
          taskType: defaults[index].taskType,
          sortOrder: defaults[index].sortOrder,
          targetValue: index === 0 ? 1 : Number(item.targetValue || defaults[index].targetValue),
          enabled: true,
          rewards: item.rewardItems || item.rewards || []
        })).concat(defaults.slice(Math.min(tasks.length, 3)))
    },
    normalizeRankRewards(rewards, periodType) {
      const normalized = (Array.isArray(rewards) ? rewards : []).filter(item => item.periodType === periodType)
        .sort((left, right) => Number(left.startRank) - Number(right.startRank))
        .map(item => Object.assign({}, item, {
          rewards: item.rewardItems || item.rewards || []
        }))
      return normalized.length > 0 ? normalized : [this.emptyRankReward(periodType, 1)]
    },
    numberOrDefault(value, defaultValue) {
      return value === null || value === undefined ? defaultValue : Number(value)
    },
    resetEditor() {
      this.form = this.emptyForm()
      this.serverTime = 0
      this.originalStartTime = 0
      this.backendActivityStatus = ''
      this.resourceSelectorVisible = false
      this.resourceSelection = null
      this.$nextTick(() => {
        if (this.$refs.activityForm) {
          this.$refs.activityForm.clearValidate()
        }
      })
    },
    submitActivity() {
      this.$refs.activityForm.validate(valid => {
        if (!valid || !this.validateConfiguration()) {
          return
        }
        const payload = this.buildSavePayload()
        this.saveLoading = true
        // 新建或开始前编辑采用聚合保存，基础配置、三个任务和两类排行奖励由后端事务一次提交，避免半套配置被启用。
        saveAslanGiftChallengeActivity(payload).then(() => {
          this.$message.success('礼物挑战配置已保存')
          this.editorVisible = false
          this.loadActivities()
        }).finally(() => {
          this.saveLoading = false
        })
      })
    },
    validateConfiguration() {
      const timeRange = this.form.timeRange || []
      if (timeRange.length !== 2 || Number(timeRange[0]) >= Number(timeRange[1])) {
        this.$message.error('活动结束时间必须晚于开始时间')
        return false
      }
      if (this.form.sysOrigin !== 'ATYOU') {
        this.$message.error('Aslan 礼物挑战只允许配置 ATYOU 平台')
        return false
      }
      if (!this.validInteger(this.form.displayTopN, 1, 500)) {
        this.$message.error('榜单 TopN 必须是 1 到 500 的整数')
        return false
      }
      if (!this.validInteger(this.form.dailySettlementDelayMinutes, 0, 1440) ||
        !this.validInteger(this.form.overallSettlementDelayMinutes, 0, 1440)) {
        this.$message.error('日榜和总榜晚到窗口必须是 0 到 1440 的整数分钟')
        return false
      }
      // 统计开始后后端要求 tasks/rankRewards 完全不出现在请求中；这里只校验仍允许修改的基础字段。
      if (this.startedLocked) {
        return true
      }
      if (!this.validateTasks()) {
        return false
      }
      return this.validateRankRewards(PERIOD_DAILY, this.form.dailyRankRewards) &&
        this.validateRankRewards(PERIOD_OVERALL, this.form.overallRankRewards)
    },
    validateTasks() {
      const tasks = this.form.tasks || []
      if (tasks.length !== 3) {
        this.$message.error('任务必须且只能保留 3 个槽位')
        return false
      }
      const codes = new Set()
      const sortOrders = new Set()
      let enterCount = 0
      let giftCount = 0
      const fixedTasks = this.emptyTasks()
      for (let index = 0; index < tasks.length; index++) {
        const task = tasks[index]
        const code = String(task.taskCode || '').trim()
        const title = String(task.taskTitle || '').trim()
        if (!/^[A-Za-z0-9_-]{1,64}$/.test(code) || codes.has(code) ||
          code !== fixedTasks[index].taskCode || task.taskType !== fixedTasks[index].taskType) {
          this.$message.error(`任务槽位 ${index + 1} 的固定编码或类型无效`)
          return false
        }
        codes.add(code)
        if (!title || title.length > 128 || String(task.taskDesc || '').length > 500) {
          this.$message.error(`请检查任务槽位 ${index + 1} 的标题和描述长度`)
          return false
        }
        if (!this.validInteger(task.targetValue, 1, 999999999999)) {
          this.$message.error(`任务槽位 ${index + 1} 的目标值必须是正整数`)
          return false
        }
        if (Number(task.sortOrder) !== fixedTasks[index].sortOrder ||
          sortOrders.has(Number(task.sortOrder))) {
          this.$message.error(`任务槽位 ${index + 1} 的固定排序无效`)
          return false
        }
        sortOrders.add(Number(task.sortOrder))
        if (!task.enabled || !task.resourceGroupId) {
          this.$message.error(`任务槽位 ${index + 1} 必须启用并选择奖励资源组`)
          return false
        }
        if (task.taskType === TASK_ENTER_PAGE) {
          enterCount++
          if (Number(task.targetValue) !== 1) {
            this.$message.error('进入页面任务的目标值必须为 1')
            return false
          }
        } else {
          giftCount++
        }
      }
      if (enterCount !== 1 || giftCount !== 2) {
        this.$message.error('任务必须固定为 1 个每日进入页面和 2 个累计送礼金币档位')
        return false
      }
      const giftTasks = tasks
        .filter(item => item.taskType === TASK_SEND_GIFT_GOLD)
        .sort((left, right) => Number(left.sortOrder) - Number(right.sortOrder))
      // 两档按固定槽位递增，提前在 console 阻止“同一次送礼同时完成两个重复档位”的配置。
      if (Number(giftTasks[0].targetValue) >= Number(giftTasks[1].targetValue)) {
        this.$message.error('第二档累计送礼金币门槛必须高于第一档')
        return false
      }
      return true
    },
    validateRankRewards(periodType, rewards) {
      const periodName = periodType === PERIOD_DAILY ? '日榜' : '总榜'
      if (!Array.isArray(rewards) || rewards.length === 0) {
        this.$message.error(`${periodName}至少配置一个奖励区间`)
        return false
      }
      const ordered = rewards.slice().sort((left, right) => Number(left.startRank) - Number(right.startRank))
      let previousEnd = 0
      for (let index = 0; index < ordered.length; index++) {
        const reward = ordered[index]
        const start = Number(reward.startRank)
        const end = Number(reward.endRank)
        if (!this.validInteger(start, 1, Number(this.form.displayTopN)) ||
          !this.validInteger(end, 1, Number(this.form.displayTopN)) || start > end) {
          this.$message.error(`${periodName}第 ${index + 1} 档名次范围无效或超过 TopN`)
          return false
        }
        if (start <= previousEnd) {
          this.$message.error(`${periodName}奖励区间存在重叠`)
          return false
        }
        if (!reward.resourceGroupId || String(reward.rewardName || '').length > 128) {
          this.$message.error(`${periodName}第 ${index + 1} 档必须选择奖励资源组，并检查奖励名称长度`)
          return false
        }
        previousEnd = end
      }
      return true
    },
    validInteger(value, min, max) {
      const number = Number(value)
      return Number.isSafeInteger(number) && number >= min && number <= max
    },
    buildSavePayload() {
      const payload = {
        id: this.form.id || null,
        activityCode: this.form.activityCode.trim(),
        activityName: this.form.activityName.trim(),
        activityDesc: this.form.activityDesc,
        sysOrigin: 'ATYOU',
        timeZone: this.form.timeZone,
        startTime: Number(this.form.timeRange[0]),
        endTime: Number(this.form.timeRange[1]),
        dailySettlementDelayMinutes: Number(this.form.dailySettlementDelayMinutes),
        overallSettlementDelayMinutes: Number(this.form.overallSettlementDelayMinutes),
        displayTopN: Number(this.form.displayTopN),
        enabled: Boolean(this.form.enabled),
        version: this.form.id ? Number(this.form.version) : null
      }
      if (!this.startedLocked) {
        payload.tasks = this.form.tasks.map(item => ({
          id: item.id || null,
          activityId: this.form.id || null,
          taskCode: item.taskCode.trim(),
          taskType: item.taskType,
          taskTitle: item.taskTitle.trim(),
          taskDesc: item.taskDesc,
          targetValue: Number(item.targetValue),
          resourceGroupId: String(item.resourceGroupId),
          enabled: true,
          sortOrder: Number(item.sortOrder)
        }))
        payload.rankRewards = this.form.dailyRankRewards.concat(this.form.overallRankRewards).map(item => ({
          id: item.id || null,
          activityId: this.form.id || null,
          periodType: item.periodType,
          startRank: Number(item.startRank),
          endRank: Number(item.endRank),
          resourceGroupId: String(item.resourceGroupId),
          rewardName: item.rewardName
        }))
      }
      return payload
    },
    addRankReward(periodType) {
      const list = this.rankRewardList(periodType)
      const lastEnd = list.reduce((max, item) => Math.max(max, Number(item.endRank || 0)), 0)
      const nextRank = lastEnd + 1
      if (nextRank > Number(this.form.displayTopN)) {
        this.$message.warning('当前区间已到达 TopN，请先调整已有区间或提高 TopN')
        return
      }
      list.push(this.emptyRankReward(periodType, nextRank))
    },
    removeRankReward(periodType, index) {
      const list = this.rankRewardList(periodType)
      if (list.length <= 1) {
        this.$message.warning('每个排行周期至少保留一个奖励区间')
        return
      }
      list.splice(index, 1)
    },
    rankRewardList(periodType) {
      return periodType === PERIOD_DAILY ? this.form.dailyRankRewards : this.form.overallRankRewards
    },
    selectResourceGroup(scope, index) {
      if (this.editorReadOnly) {
        return
      }
      this.resourceSelection = { scope, index }
      this.resourceSelectorVisible = true
    },
    applyResourceGroup(source) {
      if (!this.resourceSelection || !source) {
        return
      }
      let row
      if (this.resourceSelection.scope === 'task') {
        row = this.form.tasks[this.resourceSelection.index]
      } else if (this.resourceSelection.scope === 'daily') {
        row = this.form.dailyRankRewards[this.resourceSelection.index]
      } else {
        row = this.form.overallRankRewards[this.resourceSelection.index]
      }
      if (!row) {
        this.closeResourceSelector()
        return
      }
      // 奖励组 Long ID 始终按字符串保存，防止浏览器 Number 截断 Snowflake ID 后配置到错误资源组。
      row.resourceGroupId = String(source.id)
      row.rewards = source.rewardConfigList || []
      if (this.resourceSelection.scope !== 'task' && !row.rewardName) {
        row.rewardName = source.name || ''
      }
      this.closeResourceSelector()
    },
    closeResourceSelector() {
      this.resourceSelectorVisible = false
      this.resourceSelection = null
    },
    changeEnabled(row) {
      const enabled = Boolean(row.enabled)
      const previous = !enabled
      const action = enabled ? '启用' : '停用'
      this.$confirm(`确认${action}活动「${row.activityName}」？活动开始后不可切换状态。`, '状态确认', {
        type: 'warning'
      }).then(() => setAslanGiftChallengeEnabled(row.id, enabled)).then(() => {
        this.$message.success(`${action}成功`)
        this.loadActivities()
      }).catch(() => {
        // 用户取消和接口失败都必须回滚 switch；服务端仍是唯一状态真相。
        row.enabled = previous
      })
    },
    openRanking(row) {
      this.selectedActivity = row
      this.rankingQuery = {
        periodType: PERIOD_OVERALL,
        statDate: '',
        limit: Number(row.displayTopN || 100)
      }
      this.rankingResult = { settled: false, entries: [] }
      this.rankingVisible = true
      this.loadRanking()
    },
    rankingPeriodChanged() {
      // 活动日由配置时区决定，不能用运营电脑本地日期猜测；切到日榜后要求人工明确选择。
      this.rankingQuery.statDate = ''
      this.loadRanking()
    },
    loadRanking() {
      if (!this.selectedActivity.id) {
        return
      }
      if (this.rankingQuery.periodType === PERIOD_DAILY && !this.rankingQuery.statDate) {
        this.$message.warning('查询日榜时请选择统计日')
        return
      }
      this.rankingLoading = true
      getAslanGiftChallengeRanking(this.selectedActivity.id, {
        periodType: this.rankingQuery.periodType,
        statDate: this.rankingQuery.periodType === PERIOD_DAILY ? this.rankingQuery.statDate : undefined,
        limit: Number(this.rankingQuery.limit)
      }).then(res => {
        this.rankingResult = res.body || { settled: false, entries: [] }
      }).finally(() => {
        this.rankingLoading = false
      })
    },
    openSettlements(row) {
      this.selectedActivity = row
      this.settlementQuery = {
        periodType: PERIOD_OVERALL,
        statDate: '',
        deliveryStatus: '',
        limit: 100
      }
      this.settlementRecords = []
      this.settlementsVisible = true
      this.loadSettlements()
    },
    settlementPeriodChanged() {
      // 日榜结算会真实发奖，切换周期时清空旧日期，防止沿用另一时区下的“今天”。
      this.settlementQuery.statDate = ''
      this.loadSettlements()
    },
    loadSettlements() {
      if (!this.selectedActivity.id) {
        return
      }
      this.settlementsLoading = true
      listAslanGiftChallengeSettlementRecords(this.selectedActivity.id, {
        periodType: this.settlementQuery.periodType || undefined,
        statDate: this.settlementQuery.periodType === PERIOD_DAILY ? this.settlementQuery.statDate || undefined : undefined,
        deliveryStatus: this.settlementQuery.deliveryStatus || undefined,
        limit: Number(this.settlementQuery.limit)
      }).then(res => {
        this.settlementRecords = Array.isArray(res.body) ? res.body : []
      }).finally(() => {
        this.settlementsLoading = false
      })
    },
    manualSettle() {
      const period = this.settlementQuery.periodType
      const statDate = period === PERIOD_DAILY ? this.settlementQuery.statDate : ''
      if (!period || (period === PERIOD_DAILY && !statDate)) {
        this.$message.warning('手动结算日榜时必须选择统计日')
        return
      }
      const periodName = period === PERIOD_DAILY ? `日榜 ${statDate}` : '活动总榜'
      this.$confirm(`确认手动结算「${periodName}」？未到晚到窗口或已结算时服务端会拒绝。`, '结算确认', {
        type: 'warning'
      }).then(() => {
        this.settleLoading = true
        return settleAslanGiftChallengeActivity(this.selectedActivity.id, period, statDate)
      }).then(() => {
        this.$message.success('结算请求执行完成')
        this.loadSettlements()
        this.loadActivities()
      }).finally(() => {
        this.settleLoading = false
      })
    },
    resolveUnknownDeliveryItem(item, delivered) {
      const action = delivered ? '确认该奖励项实际已到账' : '确认未到账并开放补发'
      this.$confirm(`${action}？此操作依赖人工核账结果，不可用作普通重试。`, 'UNKNOWN 核账确认', {
        type: 'warning'
      }).then(() => {
        this.resolvingDeliveryItemId = item.id
        return resolveAslanGiftChallengeUnknownDeliveryItem(item.id, delivered)
      }).then(() => {
        this.$message.success('核账结果已提交')
        this.loadSettlements()
      }).finally(() => {
        this.resolvingDeliveryItemId = ''
      })
    },
    activityStatus(row) {
      const now = Date.now()
      if (!row.enabled) {
        return 'DISABLED'
      }
      if (now < Number(row.startTime || 0)) {
        return 'PLANNED'
      }
      if (now <= Number(row.endTime || 0)) {
        return 'ACTIVE'
      }
      return 'ENDED'
    },
    activityStatusText(status) {
      return {
        DISABLED: '未启用',
        PLANNED: '待开始',
        ACTIVE: '进行中',
        ENDED: '已结束'
      }[status] || status
    },
    activityStatusTag(status) {
      return {
        DISABLED: 'info',
        PLANNED: 'warning',
        ACTIVE: 'success',
        ENDED: ''
      }[status] || 'info'
    },
    settlementStatusText(status) {
      return {
        NOT_STARTED: '未开始',
        PROCESSING: '处理中',
        COMPLETED: '已完成',
        RECONCILIATION_REQUIRED: '待核账'
      }[status] || status || '未开始'
    },
    deliveryStatusTag(status) {
      return {
        SUCCESS: 'success',
        UNKNOWN: 'warning',
        PROCESSING: '',
        PENDING: 'info'
      }[status] || 'info'
    },
    periodText(period) {
      return period === PERIOD_DAILY ? '日榜' : (period === PERIOD_OVERALL ? '总榜' : period)
    },
    hasStarted(row) {
      return Number(row.startTime || 0) <= Date.now()
    },
    formatTime(value) {
      if (!value) {
        return '-'
      }
      const date = new Date(Number(value))
      return Number.isNaN(date.getTime()) ? '-' : date.toLocaleString()
    },
    formatNumber(value) {
      if (value === null || value === undefined || value === '') {
        return '0'
      }
      const number = Number(value)
      return Number.isFinite(number) ? number.toLocaleString() : String(value)
    }
  }
}
</script>

<style scoped lang="scss">
.aslan-gift-challenge {
  .page-tip {
    margin: 8px 0 18px;
  }

  .primary-text {
    font-weight: 600;
  }

  .secondary-text,
  .form-help {
    margin-top: 4px;
    color: #909399;
    font-size: 12px;
    line-height: 18px;
  }

  .editor-body {
    max-height: 76vh;
    padding-right: 8px;
    overflow-y: auto;
  }

  .section-title {
    margin: 24px 0 14px;
    padding-left: 10px;
    border-left: 4px solid #409eff;
    color: #303133;
    font-size: 16px;
    font-weight: 600;
  }

  .rank-title-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
  }

  .config-table,
  .settlement-table {
    margin-top: 12px;
  }

  .task-desc-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 12px;
    margin-top: 12px;
  }

  .task-desc-item {
    padding: 10px;
    border: 1px solid #ebeef5;
    border-radius: 4px;
  }

  .task-desc-label {
    margin-bottom: 8px;
    color: #606266;
    font-size: 13px;
  }

  .dialog-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    margin-bottom: 14px;
  }

  .delivery-item-table {
    width: calc(100% - 30px);
    margin: 4px 15px 12px;
  }

  .danger-link {
    color: #f56c6c;
  }
}

@media (max-width: 1100px) {
  .aslan-gift-challenge .task-desc-grid {
    grid-template-columns: 1fr;
  }
}
</style>
