<script lang="ts" setup>
import { computed, nextTick, reactive, ref, watch } from 'vue';

import { Page } from '@vben/common-ui';
import { useAccessStore } from '@vben/stores';

import {
  Button,
  Card,
  Input,
  InputNumber,
  message,
  Select,
  Space,
  Spin,
  Switch,
  Table,
  TabPane,
  Tabs,
  Tag,
} from 'antdv-next';

import {
  getResidentWheelConfig,
  pageResidentWheelDrawRecords,
  saveResidentWheelCategoryConfig,
} from '#/api/legacy/resident-activity';
import PropsSourceSelectDrawer from '#/views/operate/components/props-source-select-drawer.vue';
import { getAllowedSysOrigins } from '#/views/system/shared';

defineOptions({ name: 'ResidentWheelConfigPage' });

const PROBABILITY_TOTAL = 10_000;
const WHEEL_CATEGORIES = [
  {
    category: 'CLASSIC',
    label: 'Classic',
    priceFiftyGold: 25_000,
    priceOneGold: 500,
    priceTenGold: 5000,
    rewardCount: 8,
  },
  {
    category: 'LUXURY',
    label: 'Luxury',
    priceFiftyGold: 100_000,
    priceOneGold: 2000,
    priceTenGold: 20_000,
    rewardCount: 8,
  },
  {
    category: 'ADVANCED',
    label: 'Advanced',
    priceFiftyGold: 500_000,
    priceOneGold: 10_000,
    priceTenGold: 100_000,
    rewardCount: 12,
  },
] as const;
type WheelCategoryMeta = (typeof WHEEL_CATEGORIES)[number];

const CATEGORY_OPTIONS = WHEEL_CATEGORIES.map((item) => ({
  label: `${item.label} (${item.rewardCount})`,
  value: item.category,
}));

const REWARD_TYPE_OPTIONS = [
  { label: '资源', value: 'RESOURCE' },
  { label: '金币', value: 'GOLD' },
];

let localRowId = 0;

function normalizeRewardTypeValue(value: any) {
  return String(value || 'RESOURCE').toUpperCase() === 'GOLD' ? 'GOLD' : 'RESOURCE';
}

function createRowKey(seed?: number | string) {
  localRowId += 1;
  return `wheel-reward-${seed || localRowId}-${localRowId}`;
}

function findCategoryMeta(category: string): WheelCategoryMeta {
  return (
    WHEEL_CATEGORIES.find((item) => item.category === String(category || '').toUpperCase()) ||
    WHEEL_CATEGORIES[0]
  );
}

function createReward(type = 'RESOURCE', sort = 1) {
  return {
    coverUrl: '',
    displayGoldAmount: type === 'RESOURCE' ? 0 : 0,
    durationDays: type === 'RESOURCE' ? 7 : 0,
    enabled: true,
    goldAmount: type === 'GOLD' ? 100 : 0,
    id: 0,
    probability: 0,
    rewardType: type,
    resourceId: null as null | number | string,
    resourceName: '',
    resourceType: 'AVATAR_FRAME',
    resourceUrl: '',
    rowKey: createRowKey(sort),
    sort,
  };
}

function createCategory(meta: WheelCategoryMeta = WHEEL_CATEGORIES[0]) {
  return {
    category: meta.category,
    enabled: false,
    label: meta.label,
    priceFiftyGold: meta.priceFiftyGold,
    priceOneGold: meta.priceOneGold,
    priceTenGold: meta.priceTenGold,
    rewardCount: meta.rewardCount,
    rewards: Array.from({ length: meta.rewardCount }, (_, index) =>
      createReward('RESOURCE', index + 1),
    ),
  };
}

function createEmptyForm(sysOrigin = '') {
  return {
    categories: WHEEL_CATEGORIES.map((item) => createCategory(item)),
    configured: false,
    enabled: false,
    id: 0,
    sysOrigin,
    timezone: 'Asia/Riyadh',
    updateTime: '',
  };
}

const accessStore = useAccessStore();
const sysOriginOptions = computed(() => {
  const options = getAllowedSysOrigins(accessStore.accessCodes || []);
  return options.length > 0 ? options : getAllowedSysOrigins([]);
});

const selectedSysOrigin = ref('');
const activePageTab = ref('config');
const activeCategoryTab = ref('CLASSIC');
const loading = ref(false);
const savingCategory = ref('');
const recordLoading = ref(false);
const resourcePickerOpen = ref(false);
const pickerCategory = ref('CLASSIC');
const pickerRewardIndex = ref(-1);
const pickerRewardRecord = ref<null | Record<string, any>>(null);

const form = reactive<Record<string, any>>(createEmptyForm());
const recordQuery = reactive<Record<string, any>>({
  category: '',
  cursor: 1,
  limit: 20,
  status: '',
  userId: '',
});
const recordPage = reactive<Record<string, any>>({
  current: 1,
  records: [],
  size: 20,
  total: 0,
});

const rewardColumns = [
  { dataIndex: 'enabled', key: 'enabled', title: '启用', width: 80 },
  { dataIndex: 'sort', key: 'sort', title: '位置', width: 90 },
  { dataIndex: 'rewardType', key: 'rewardType', title: '类型', width: 130 },
  { dataIndex: 'reward', key: 'reward', title: '奖励', width: 620 },
  { dataIndex: 'probability', key: 'probability', title: '概率', width: 180 },
  { dataIndex: 'actions', key: 'actions', title: '操作', width: 90 },
];

const recordColumns = [
  { dataIndex: 'drawNo', key: 'drawNo', title: '抽奖单号', width: 190 },
  { dataIndex: 'category', key: 'category', title: '转盘', width: 120 },
  { dataIndex: 'userId', key: 'userId', title: '用户ID', width: 140 },
  { dataIndex: 'drawTimes', key: 'drawTimes', title: '次数', width: 90 },
  { dataIndex: 'paidGold', key: 'paidGold', title: '消耗金币', width: 120 },
  { dataIndex: 'reward', key: 'reward', title: '中奖奖励', width: 360 },
  { dataIndex: 'probability', key: 'probability', title: '命中概率', width: 110 },
  { dataIndex: 'status', key: 'status', title: '状态', width: 110 },
  { dataIndex: 'createTime', key: 'createTime', title: '时间', width: 180 },
  { dataIndex: 'errorMessage', key: 'errorMessage', title: '失败原因', width: 260 },
];

const statusMeta = computed(() => {
  if (!form.configured) {
    return { color: 'processing', text: '未配置' };
  }
  const enabledCount = (form.categories || []).filter(
    (item: Record<string, any>) => item.enabled !== false,
  ).length;
  if (enabledCount > 0) {
    return { color: 'success', text: `已开启 ${enabledCount} 个奖池` };
  }
  return { color: 'default', text: '已关闭' };
});

function getCategory(category: string) {
  return (
    (form.categories || []).find(
      (item: Record<string, any>) => item.category === String(category || '').toUpperCase(),
    ) || form.categories?.[0]
  );
}

function categoryLabel(category: string) {
  return findCategoryMeta(category).label;
}

function categoryProbability(category: Record<string, any>) {
  return (category?.rewards || [])
    .filter((item: Record<string, any>) => item.enabled !== false)
    .reduce((sum: number, item: Record<string, any>) => sum + Number(item.probability || 0), 0);
}

function categoryProbabilityColor(category: Record<string, any>) {
  return categoryProbability(category) === PROBABILITY_TOTAL ? 'success' : 'error';
}

function normalizeReward(item: Record<string, any> | undefined, index: number) {
  const value = item || {};
  const rewardType = normalizeRewardTypeValue(value.rewardType);
  const resourceId = firstPresent(value, [
    'resourceId',
    'resourceID',
    'resource_id',
    'sourceId',
    'propsId',
    'propsSourceId',
    'badgeConfigId',
    'giftId',
    'groupId',
  ]);
  return {
    coverUrl: String(value.coverUrl || ''),
    displayGoldAmount: Number(value.displayGoldAmount || 0),
    durationDays: Number(value.durationDays || (rewardType === 'RESOURCE' ? 7 : 0)),
    enabled: value.enabled !== false,
    goldAmount: Number(value.goldAmount || 0),
    id: Number(value.id || 0),
    probability: Number(value.probability || 0),
    rewardType,
    resourceId: resourceId || null,
    resourceName: String(value.resourceName || ''),
    resourceType: String(value.resourceType || 'AVATAR_FRAME'),
    resourceUrl: String(value.resourceUrl || ''),
    rowKey: createRowKey(value.id || index + 1),
    sort: Number(value.sort || index + 1),
  };
}

function normalizeCategory(
  value: Record<string, any> | undefined,
  meta: WheelCategoryMeta = WHEEL_CATEGORIES[0],
) {
  const sourceRewards = Array.isArray(value?.rewards) ? value.rewards : [];
  const rewards = sourceRewards
    .slice(0, meta.rewardCount)
    .map((item: Record<string, any>, index: number) => normalizeReward(item, index));
  while (rewards.length < meta.rewardCount) {
    rewards.push(createReward('RESOURCE', rewards.length + 1));
  }
  return {
    category: meta.category,
    enabled: value ? value.enabled !== false : false,
    label: meta.label,
    priceFiftyGold: Number(value?.priceFiftyGold || meta.priceFiftyGold),
    priceOneGold: Number(value?.priceOneGold || meta.priceOneGold),
    priceTenGold: Number(value?.priceTenGold || meta.priceTenGold),
    rewardCount: meta.rewardCount,
    rewards,
  };
}

function unwrapResponsePayload(result: null | Record<string, any> | undefined) {
  const value = result || {};
  if (Array.isArray(value.categories)) {
    return value;
  }
  if (value.body && Array.isArray(value.body.categories)) {
    return value.body;
  }
  if (value.data && Array.isArray(value.data.categories)) {
    return value.data;
  }
  return value;
}

function syncRewardRows(
  targetRewards: Array<Record<string, any>>,
  nextRewards: Array<Record<string, any>>,
) {
  const syncedRewards = nextRewards.map((reward, index) => {
    const target = targetRewards[index] || {};
    Object.assign(target, reward);
    return target;
  });
  targetRewards.splice(0, targetRewards.length, ...syncedRewards);
}

function syncCategories(nextCategories: Array<Record<string, any>>) {
  const currentByCategory = new Map<string, Record<string, any>>(
    (form.categories || []).map((category: Record<string, any>) => [
      String(category.category || '').toUpperCase(),
      category,
    ]),
  );
  const syncedCategories = nextCategories.map((category) => {
    const current: Record<string, any> =
      currentByCategory.get(String(category.category || '').toUpperCase()) ||
      (createCategory(findCategoryMeta(category.category)) as Record<string, any>);
    const currentRewards = Array.isArray(current.rewards) ? current.rewards : [];
    const nextRewards = Array.isArray(category.rewards) ? category.rewards : [];
    Object.assign(current, category, { rewards: currentRewards });
    syncRewardRows(currentRewards, nextRewards);
    return current;
  });
  form.categories.splice(0, form.categories.length, ...syncedCategories);
}

function applyConfig(result: null | Record<string, any> | undefined) {
  const value = unwrapResponsePayload(result);
  const sysOrigin = String(
    value.sysOrigin || selectedSysOrigin.value || sysOriginOptions.value[0]?.value || '',
  );
  const categories = Array.isArray(value.categories) ? value.categories : [];
  const normalizedCategories = WHEEL_CATEGORIES.map((meta) => {
    const source = categories.find(
      (item: Record<string, any>) => String(item.category || '').toUpperCase() === meta.category,
    );
    return normalizeCategory(source, meta);
  });

  form.configured = Boolean(value.configured);
  form.enabled = Boolean(value.enabled);
  form.id = Number(value.id || 0);
  form.sysOrigin = sysOrigin;
  form.timezone = String(value.timezone || 'Asia/Riyadh');
  form.updateTime = String(value.updateTime || '');
  syncCategories(normalizedCategories);
}

async function loadConfig() {
  if (!selectedSysOrigin.value) {
    return;
  }
  loading.value = true;
  try {
    const result = await getResidentWheelConfig(selectedSysOrigin.value);
    applyConfig(result);
  } finally {
    loading.value = false;
  }
}

function normalizeRecordPage(result: null | Record<string, any> | undefined) {
  const value = result || {};
  recordPage.records = Array.isArray(value.records) ? value.records : [];
  recordPage.total = Number(value.total || 0);
  recordPage.current = Number(value.current || 1);
  recordPage.size = Number(value.size || 20);
}

async function loadRecords(reset = false) {
  if (!selectedSysOrigin.value) {
    return;
  }
  if (reset) {
    recordQuery.cursor = 1;
  }
  recordLoading.value = true;
  try {
    const result = await pageResidentWheelDrawRecords({
      ...recordQuery,
      sysOrigin: selectedSysOrigin.value,
    });
    normalizeRecordPage(result);
  } finally {
    recordLoading.value = false;
  }
}

function firstPresent(record: Record<string, any>, keys: string[]) {
  for (const key of keys) {
    const value = record?.[key];
    if (value !== undefined && value !== null && String(value).trim() !== '') {
      return value;
    }
  }
  return '';
}

function normalizeSelectedResource(row: Record<string, any>, fallbackType = 'AVATAR_FRAME') {
  const resourceId = firstPresent(row, [
    'id',
    'resourceId',
    'sourceId',
    'propsId',
    'propsSourceId',
    'badgeConfigId',
    'giftId',
    'groupId',
  ]);
  return {
    coverUrl: String(
      firstPresent(row, [
        'cover',
        'coverUrl',
        'resourceCover',
        'resourceCoverUrl',
        'giftPhoto',
        'selectUrl',
        'icon',
        'smallIcon',
      ]),
    ),
    resourceId,
    resourceName: String(
      firstPresent(row, [
        'name',
        'resourceName',
        'sourceName',
        'giftName',
        'badgeName',
        'groupName',
        'title',
        'remark',
      ]) || (resourceId ? `资源 ${resourceId}` : ''),
    ),
    resourcePriceGold: Number(firstPresent(row, ['amount', 'giftCandy', 'price', 'goldAmount']) || 0),
    resourceType: String(
      firstPresent(row, ['type', 'resourceType', 'propsType']) || fallbackType || 'AVATAR_FRAME',
    ),
    resourceUrl: String(
      firstPresent(row, [
        'sourceUrl',
        'resourceUrl',
        'url',
        'giftSourceUrl',
        'animationUrl',
        'playUrl',
      ]),
    ),
  };
}

function normalizeRewardResourceId(item: Record<string, any>) {
  return String(
    firstPresent(item, [
      'resourceId',
      'resourceID',
      'resource_id',
      'sourceId',
      'propsId',
      'propsSourceId',
      'badgeConfigId',
      'giftId',
      'groupId',
    ]),
  ).trim();
}

function hasRewardResourceId(item: Record<string, any>) {
  const resourceId = normalizeRewardResourceId(item);
  return resourceId !== '' && resourceId !== '0';
}

function openResourcePicker(category: string, index: number, record?: Record<string, any>) {
  pickerCategory.value = category;
  pickerRewardIndex.value = index;
  pickerRewardRecord.value = record || null;
  resourcePickerOpen.value = true;
}

function closeResourcePicker() {
  resourcePickerOpen.value = false;
  pickerRewardRecord.value = null;
}

function handleResourceSelect(row: Record<string, any>) {
  const category = getCategory(pickerCategory.value);
  const target = pickerRewardRecord.value || category?.rewards?.[pickerRewardIndex.value];
  if (!target) {
    closeResourcePicker();
    return;
  }
  const selected = normalizeSelectedResource(row, target.resourceType || 'AVATAR_FRAME');
  if (!selected.resourceId) {
    message.warning('资源数据缺少 ID，无法选择');
    closeResourcePicker();
    return;
  }
  target.rewardType = 'RESOURCE';
  target.resourceId = String(selected.resourceId).trim();
  target.resourceType = selected.resourceType;
  target.resourceName = selected.resourceName;
  target.resourceUrl = selected.resourceUrl;
  target.coverUrl = selected.coverUrl || target.coverUrl || '';
  target.displayGoldAmount =
    selected.resourcePriceGold > 0
      ? selected.resourcePriceGold
      : Number(target.displayGoldAmount || 0);
  target.durationDays = Number(target.durationDays || 7);
  target.goldAmount = 0;
  closeResourcePicker();
}

function clearResource(record: Record<string, any>) {
  record.resourceId = null;
  record.resourceName = '';
  record.resourceUrl = '';
  record.coverUrl = '';
  record.displayGoldAmount = 0;
  record.durationDays = normalizeRewardTypeValue(record.rewardType) === 'RESOURCE' ? 7 : 0;
}

function setCategoryEnabled(category: Record<string, any>, checked: any) {
  category.enabled = checked === true;
}

function setRewardEnabled(record: Record<string, any>, checked: any) {
  record.enabled = checked === true;
}

function handleRewardTypeChange(record: Record<string, any>, value?: string) {
  record.rewardType = normalizeRewardTypeValue(value || record.rewardType);
  if (record.rewardType === 'GOLD') {
    record.resourceId = null;
    record.resourceName = '';
    record.resourceType = '';
    record.resourceUrl = '';
    record.coverUrl = '';
    record.displayGoldAmount = 0;
    record.durationDays = 0;
    record.goldAmount = Number(record.goldAmount || 100);
    return;
  }
  record.goldAmount = 0;
  record.displayGoldAmount = Number(record.displayGoldAmount || 0);
  record.resourceType = record.resourceType || 'AVATAR_FRAME';
  record.durationDays = Number(record.durationDays || 7);
}

function rewardValidateLabel(category: Record<string, any>, item: Record<string, any>) {
  const sort = Number(item.sort || 0);
  return `${category.label}${sort > 0 ? ` 第 ${sort} 位` : ''}`;
}

function validateReward(category: Record<string, any>, item: Record<string, any>) {
  const rewardType = normalizeRewardTypeValue(item.rewardType);
  item.rewardType = rewardType;
  const label = rewardValidateLabel(category, item);
  const requiresContent = category.enabled !== false && item.enabled !== false;
  if (rewardType === 'RESOURCE') {
    const resourceId = normalizeRewardResourceId(item);
    if (resourceId) {
      item.resourceId = resourceId;
    }
    if (requiresContent && !hasRewardResourceId(item)) {
      message.warning(`${label} 资源奖励必须选择资源`);
      return false;
    }
    if (requiresContent && !item.resourceType) {
      message.warning(`${label} 资源奖励必须有资源类型`);
      return false;
    }
    if (requiresContent && Number(item.durationDays || 0) <= 0) {
      message.warning(`${label} 资源奖励有效天数必须大于 0`);
      return false;
    }
    if (requiresContent && Number(item.displayGoldAmount || 0) <= 0) {
      message.warning(`${label} 资源奖励展示金币价格必须大于 0`);
      return false;
    }
  }
  if (rewardType === 'GOLD' && requiresContent && Number(item.goldAmount || 0) <= 0) {
    message.warning(`${label} 金币奖励必须设置金币数量`);
    return false;
  }
  if (Number(item.probability || 0) < 0) {
    message.warning(`${label} 概率不能小于 0`);
    return false;
  }
  if (
    category.enabled !== false &&
    item.enabled !== false &&
    Number(item.probability || 0) <= 0
  ) {
    message.warning(`${category.label} 已启用奖励概率必须大于 0`);
    return false;
  }
  return true;
}

function validateCategory(category: Record<string, any>) {
  const meta = findCategoryMeta(category.category);
  if (
    Number(category.priceOneGold || 0) <= 0 ||
    Number(category.priceTenGold || 0) <= 0 ||
    Number(category.priceFiftyGold || 0) <= 0
  ) {
    message.warning(`${category.label} 抽奖价格必须大于 0`);
    return false;
  }
  const rewards = category.rewards as Array<Record<string, any>>;
  if (rewards.length !== meta.rewardCount) {
    message.warning(`${category.label} 必须固定配置 ${meta.rewardCount} 个奖励`);
    return false;
  }
  for (const item of rewards) {
    if (!validateReward(category, item)) {
      return false;
    }
  }
  if (category.enabled !== false) {
    const enabledCount = rewards.filter((item) => item.enabled !== false).length;
    if (enabledCount !== meta.rewardCount) {
      message.warning(`${category.label} 启用时必须启用 ${meta.rewardCount} 个奖励`);
      return false;
    }
    if (categoryProbability(category) !== PROBABILITY_TOTAL) {
      message.warning(`${category.label} 启用时概率总和必须等于 10000`);
      return false;
    }
  }
  return true;
}

function validateBeforeSave(category?: Record<string, any>) {
  if (!selectedSysOrigin.value) {
    message.warning('请选择系统');
    return false;
  }
  const targetCategory = category || getCategory(activeCategoryTab.value);
  if (!targetCategory) {
    message.warning('请选择奖池');
    return false;
  }
  return validateCategory(targetCategory);
}

function categoryPayload(category: Record<string, any>) {
  return {
    category: String(category.category || ''),
    enabled: category.enabled !== false,
    priceFiftyGold: Number(category.priceFiftyGold || 0),
    priceOneGold: Number(category.priceOneGold || 0),
    priceTenGold: Number(category.priceTenGold || 0),
    rewards: (category.rewards || []).map((item: Record<string, any>) => {
      const rewardType = normalizeRewardTypeValue(item.rewardType);
      const resourceId = normalizeRewardResourceId(item);
      return {
        coverUrl: rewardType === 'RESOURCE' ? String(item.coverUrl || '') : '',
        displayGoldAmount:
          rewardType === 'RESOURCE' ? Number(item.displayGoldAmount || 0) : 0,
        durationDays: rewardType === 'RESOURCE' ? Number(item.durationDays || 0) : 0,
        enabled: item.enabled !== false,
        goldAmount: rewardType === 'GOLD' ? Number(item.goldAmount || 0) : 0,
        id: Number(item.id || 0),
        probability: Number(item.probability || 0),
        rewardType,
        resourceId: rewardType === 'RESOURCE' && resourceId ? resourceId : null,
        resourceName: rewardType === 'RESOURCE' ? String(item.resourceName || '') : '',
        resourceType: rewardType === 'RESOURCE' ? String(item.resourceType || '') : '',
        resourceUrl: rewardType === 'RESOURCE' ? String(item.resourceUrl || '') : '',
        sort: Number(item.sort || 0),
      };
    }),
  };
}

async function submitCategory(category: Record<string, any>) {
  await nextTick();
  if (!validateBeforeSave(category)) {
    return;
  }
  if (!category) {
    return;
  }
  savingCategory.value = String(category.category || '');
  try {
    const result = await saveResidentWheelCategoryConfig({
      category: categoryPayload(category),
      sysOrigin: selectedSysOrigin.value,
      timezone: String(form.timezone || '').trim(),
    });
    const savedValue = unwrapResponsePayload(result);
    const savedCategory = (savedValue?.categories || []).find(
      (item: Record<string, any>) =>
        String(item.category || '').toUpperCase() === String(category.category || '').toUpperCase(),
    );
    if (savedCategory?.enabled === false) {
      message.warning(`${category.label} 已保存为关闭状态，H5 不展示这个转盘`);
    } else {
      message.success(`${category.label} 保存成功`);
    }
    await loadConfig();
  } finally {
    savingCategory.value = '';
  }
}

function handleRecordSearch() {
  void loadRecords(true);
}

function handleRecordTableChange(pagination: Record<string, any>) {
  recordQuery.cursor = Number(pagination.current || 1);
  recordQuery.limit = Number(pagination.pageSize || 20);
  void loadRecords();
}

function rewardRecordName(record: Record<string, any>) {
  if (record.resourceName) {
    return record.resourceName;
  }
  if (normalizeRewardTypeValue(record.rewardType) === 'GOLD') {
    return `${Number(record.goldAmount || 0).toLocaleString()} 金币`;
  }
  return '-';
}

function resourceDisplayName(record: Record<string, any>) {
  if (record.resourceName) {
    return record.resourceName;
  }
  if (record.resourceId) {
    return `资源 ${record.resourceId}`;
  }
  return '未选择资源';
}

function statusColor(status: string) {
  switch (String(status || '').toUpperCase()) {
    case 'FAILED': {
      return 'error';
    }
    case 'SUCCESS': {
      return 'success';
    }
    default: {
      return 'processing';
    }
  }
}

function coverStyle(url: string) {
  return {
    backgroundImage: `url("${String(url || '').replaceAll('"', '%22')}")`,
  };
}

watch(
  sysOriginOptions,
  (options) => {
    if (!selectedSysOrigin.value) {
      selectedSysOrigin.value = String(options[0]?.value || '');
    }
  },
  { immediate: true },
);

watch(
  selectedSysOrigin,
  (value) => {
    if (value) {
      void loadConfig();
      void loadRecords(true);
    }
  },
  { immediate: true },
);

watch(activePageTab, (value) => {
  if (value === 'records') {
    void loadRecords();
  }
});
</script>

<template>
  <Page auto-content-height>
    <Spin :spinning="loading">
      <Card :bordered="false" title="转盘配置">
        <div class="toolbar">
          <Space wrap>
            <Select
              v-model:value="selectedSysOrigin"
              :options="sysOriginOptions"
              option-filter-prop="label"
              option-label-prop="label"
              placeholder="请选择系统"
              show-search
              style="width: 180px"
            />
            <Tag :color="statusMeta.color">{{ statusMeta.text }}</Tag>
            <Tag>概率基数：{{ PROBABILITY_TOTAL }} = 100%</Tag>
            <Button :loading="loading" @click="loadConfig">刷新</Button>
          </Space>
        </div>

        <Tabs v-model:active-key="activePageTab">
          <TabPane key="config" tab="奖励配置">
            <div class="config-row">
              <Space align="center" wrap>
                <span class="field-label">时区</span>
                <Input v-model:value="form.timezone" style="width: 180px" />
                <span class="sub-text">更新时间：{{ form.updateTime || '-' }}</span>
              </Space>
            </div>

            <Tabs v-model:active-key="activeCategoryTab">
              <TabPane
                v-for="category in form.categories"
                :key="category.category"
                :tab="`${category.label} (${category.rewardCount})`"
              >
                <div class="category-header">
                  <Space align="center" wrap>
                    <span class="field-label">{{ category.label }} 开关</span>
                    <Switch
                      :checked="category.enabled !== false"
                      @change="(checked: boolean) => setCategoryEnabled(category, checked)"
                    />
                    <Tag :color="categoryProbabilityColor(category)">
                      概率合计：{{ categoryProbability(category) }} / {{ PROBABILITY_TOTAL }}
                    </Tag>
                    <span class="sub-text">
                      保存当前奖池；开关打开后 H5 才展示这个 tab
                    </span>
                    <Button
                      :loading="savingCategory === category.category"
                      type="primary"
                      @click="submitCategory(category)"
                    >
                      保存 {{ category.label }}
                    </Button>
                  </Space>
                </div>

                <div class="config-row">
                  <Space align="center" wrap>
                    <span class="field-label">抽 1 次</span>
                    <InputNumber
                      v-model:value="category.priceOneGold"
                      :min="1"
                      :precision="0"
                      style="width: 130px"
                    />
                    <span class="field-label">抽 10 次</span>
                    <InputNumber
                      v-model:value="category.priceTenGold"
                      :min="1"
                      :precision="0"
                      style="width: 130px"
                    />
                    <span class="field-label">抽 50 次</span>
                    <InputNumber
                      v-model:value="category.priceFiftyGold"
                      :min="1"
                      :precision="0"
                      style="width: 130px"
                    />
                  </Space>
                </div>

                <Table
                  :columns="rewardColumns"
                  :data-source="category.rewards"
                  :loading="loading"
                  :pagination="false"
                  row-key="rowKey"
                  :scroll="{ x: 1180 }"
                >
                  <template #bodyCell="{ column, index, record }">
                    <template v-if="column.key === 'enabled'">
                      <Switch
                        :checked="record.enabled !== false"
                        @change="(checked: boolean) => setRewardEnabled(record, checked)"
                      />
                    </template>
                    <template v-else-if="column.key === 'sort'">
                      <InputNumber
                        v-model:value="record.sort"
                        :min="1"
                        :precision="0"
                        style="width: 72px"
                      />
                    </template>
                    <template v-else-if="column.key === 'rewardType'">
                      <Select
                        v-model:value="record.rewardType"
                        :options="REWARD_TYPE_OPTIONS"
                        style="width: 112px"
                        @change="(value: string) => handleRewardTypeChange(record, value)"
                      />
                    </template>
                    <template v-else-if="column.key === 'reward'">
                      <div
                        v-if="
                          normalizeRewardTypeValue(record.rewardType) === 'RESOURCE'
                        "
                        class="reward-cell"
                      >
                        <Space wrap>
                          <Button
                            size="small"
                            @click="
                              openResourcePicker(category.category, index, record)
                            "
                          >
                            {{ record.resourceId ? '更换资源' : '选择资源' }}
                          </Button>
                          <Button
                            v-if="record.resourceId"
                            danger
                            size="small"
                            type="link"
                            @click="clearResource(record)"
                          >
                            清空
                          </Button>
                        </Space>
                        <div class="resource-summary">
                          <div
                            v-if="record.coverUrl"
                            class="cover-thumb"
                            :style="coverStyle(record.coverUrl)"
                          ></div>
                          <div>
                            <div class="reward-title">
                              {{ resourceDisplayName(record) }}
                              <span v-if="record.resourceId">
                                ID: {{ record.resourceId }}
                              </span>
                              <span v-if="record.resourceType">
                                {{ record.resourceType }}
                              </span>
                            </div>
                            <InputNumber
                              v-model:value="record.durationDays"
                              :min="1"
                              :precision="0"
                              addon-after="天"
                              style="width: 130px"
                            />
                            <InputNumber
                              v-model:value="record.displayGoldAmount"
                              :min="1"
                              :precision="0"
                              addon-after="金币"
                              placeholder="展示价格"
                              style="width: 160px"
                            />
                          </div>
                        </div>
                      </div>
                      <div v-else class="reward-cell">
                        <InputNumber
                          v-model:value="record.goldAmount"
                          :min="1"
                          :precision="0"
                          addon-after="金币"
                          style="width: 180px"
                        />
                      </div>
                    </template>
                    <template v-else-if="column.key === 'probability'">
                      <Space align="center">
                        <InputNumber
                          v-model:value="record.probability"
                          :max="PROBABILITY_TOTAL"
                          :min="0"
                          :precision="0"
                          style="width: 120px"
                        />
                        <span class="sub-text">
                          {{
                            (Number(record.probability || 0) / 100).toFixed(2)
                          }}%
                        </span>
                      </Space>
                    </template>
                    <template v-else-if="column.key === 'actions'">
                      <Button danger type="link" @click="clearResource(record)">
                        清空
                      </Button>
                    </template>
                  </template>
                </Table>
              </TabPane>
            </Tabs>
          </TabPane>

          <TabPane key="records" tab="抽奖记录">
            <div class="toolbar">
              <Space wrap>
                <Select
                  v-model:value="recordQuery.category"
                  allow-clear
                  :options="CATEGORY_OPTIONS"
                  placeholder="转盘分类"
                  style="width: 150px"
                />
                <Input
                  v-model:value="recordQuery.userId"
                  allow-clear
                  placeholder="用户ID"
                  style="width: 170px"
                />
                <Select
                  v-model:value="recordQuery.status"
                  allow-clear
                  :options="[
                    { label: '成功', value: 'SUCCESS' },
                    { label: '失败', value: 'FAILED' },
                    { label: '处理中', value: 'PENDING' },
                  ]"
                  placeholder="状态"
                  style="width: 130px"
                />
                <Button :loading="recordLoading" type="primary" @click="handleRecordSearch">
                  查询
                </Button>
              </Space>
            </div>

            <Table
              :columns="recordColumns"
              :data-source="recordPage.records"
              :loading="recordLoading"
              :pagination="{
                current: recordPage.current,
                pageSize: recordPage.size,
                showSizeChanger: true,
                total: recordPage.total,
              }"
              row-key="id"
              :scroll="{ x: 1680 }"
              @change="handleRecordTableChange"
            >
              <template #bodyCell="{ column, record }">
                <template v-if="column.key === 'category'">
                  <Tag>{{ categoryLabel(record.category) }}</Tag>
                </template>
                <template v-else-if="column.key === 'reward'">
                  <div class="record-reward">
                    <div
                      v-if="record.coverUrl"
                      class="cover-thumb"
                      :style="coverStyle(record.coverUrl)"
                    ></div>
                    <div>
                      <div>{{ rewardRecordName(record) }}</div>
                      <div class="sub-text">
                        {{ record.rewardType }}
                        <span v-if="record.durationDays"> / {{ record.durationDays }} 天</span>
                      </div>
                    </div>
                  </div>
                </template>
                <template v-else-if="column.key === 'probability'">
                  {{ (Number(record.probability || 0) / 100).toFixed(2) }}%
                </template>
                <template v-else-if="column.key === 'status'">
                  <Tag :color="statusColor(record.status)">{{ record.status || '-' }}</Tag>
                </template>
                <template v-else-if="column.key === 'errorMessage'">
                  {{ record.errorMessage || '-' }}
                </template>
              </template>
            </Table>
          </TabPane>
        </Tabs>
      </Card>

      <PropsSourceSelectDrawer
        :default-type="
          pickerRewardRecord?.resourceType ||
          getCategory(pickerCategory)?.rewards?.[pickerRewardIndex]?.resourceType ||
          'AVATAR_FRAME'
        "
        :open="resourcePickerOpen"
        :sys-origin="selectedSysOrigin"
        @close="closeResourcePicker"
        @select="handleResourceSelect"
      />
    </Spin>
  </Page>
</template>

<style scoped>
.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 16px;
}

.category-header,
.config-row {
  margin-bottom: 16px;
}

.cover-thumb {
  background-position: center;
  background-repeat: no-repeat;
  background-size: contain;
  border: 1px solid rgb(226 232 240);
  border-radius: 6px;
  flex: 0 0 auto;
  height: 48px;
  width: 48px;
}

.field-label {
  color: rgb(51 65 85);
  font-weight: 600;
}

.record-reward,
.resource-summary {
  align-items: center;
  display: flex;
  gap: 10px;
}

.resource-summary {
  margin-top: 8px;
}

.reward-cell {
  min-width: 340px;
}

.reward-title {
  color: rgb(15 23 42);
  font-weight: 600;
  margin-bottom: 6px;
}

.reward-title span {
  color: rgb(100 116 139);
  font-size: 12px;
  font-weight: 400;
  margin-left: 8px;
}

.sub-text {
  color: rgb(100 116 139);
  font-size: 12px;
}

.toolbar {
  margin-bottom: 16px;
}
</style>
