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

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

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

import { listSysOriginTypeList } from '#/api/legacy/props';
import {
  getResidentVipConfig,
  pageResidentVipOrders,
  pageResidentVipUserStates,
  saveResidentVipConfig,
} from '#/api/legacy/resident-activity';
import { getAllowedSysOrigins } from '#/views/system/shared';

defineOptions({ name: 'ResidentVipConfigPage' });

const resourceFields = [
  { key: 'longBadge', title: '长徽章素材', type: 'BADGE', badgeDisplayTypes: ['LONG'] },
  { key: 'shortBadge', title: '短徽章素材', type: 'BADGE', badgeDisplayTypes: ['SHORT'] },
  { key: 'avatarFrame', title: '头像框', type: 'AVATAR_FRAME' },
  { key: 'entryEffect', title: '进场特效', type: 'RIDE' },
  { key: 'chatBubble', title: '聊天气泡', type: 'CHAT_BUBBLE' },
  { key: 'floatPicture', title: '飘窗', type: 'FLOAT_PICTURE' },
  { key: 'backgroundCard', title: '背景卡素材', type: 'DATA_CARD' },
  { key: 'effectImage', title: '动效图', type: 'VIP_EFFECT_IMAGE' },
] as const;

type ResourceField = (typeof resourceFields)[number];

const statusOptions = [
  { label: '生效中', value: 'ACTIVE' },
  { label: '已过期', value: 'EXPIRED' },
];

const orderStatusOptions = [
  { label: '成功', value: 'SUCCESS' },
  { label: '失败', value: 'FAILED' },
  { label: '处理中', value: 'INIT' },
];

const orderTypeOptions = [
  { label: '购买', value: 'PURCHASE' },
  { label: '续费', value: 'RENEW' },
  { label: '升级', value: 'UPGRADE' },
];

const levelColumns = [
  { dataIndex: 'level', key: 'level', title: '等级', width: 90 },
  { dataIndex: 'enabled', key: 'enabled', title: '启用', width: 90 },
  { dataIndex: 'displayName', key: 'displayName', title: '名称', width: 160 },
  { dataIndex: 'priceGold', key: 'priceGold', title: '30天价格', width: 150 },
  { dataIndex: 'resources', key: 'resources', title: '素材', width: 560 },
];

const stateColumns = [
  { dataIndex: 'userId', key: 'userId', title: '用户ID', width: 160 },
  { dataIndex: 'status', key: 'status', title: '状态', width: 100 },
  { dataIndex: 'level', key: 'level', title: '等级', width: 100 },
  { dataIndex: 'expireAt', key: 'expireAt', title: '到期时间', width: 180 },
  { dataIndex: 'updateTime', key: 'updateTime', title: '更新时间', width: 180 },
];

const orderColumns = [
  { dataIndex: 'userId', key: 'userId', title: '用户ID', width: 150 },
  { dataIndex: 'orderType', key: 'orderType', title: '订单类型', width: 110 },
  { dataIndex: 'levelChange', key: 'levelChange', title: '等级变化', width: 130 },
  { dataIndex: 'paidGold', key: 'paidGold', title: '支付金币', width: 120 },
  { dataIndex: 'originalPriceGold', key: 'originalPriceGold', title: '目标原价', width: 120 },
  { dataIndex: 'status', key: 'status', title: '状态', width: 100 },
  { dataIndex: 'expireAt', key: 'expireAt', title: '权益到期', width: 180 },
  { dataIndex: 'createTime', key: 'createTime', title: '创建时间', width: 180 },
  { dataIndex: 'errorMessage', key: 'errorMessage', title: '失败原因', width: 90 },
];

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

const loading = ref(false);
const saving = ref(false);
const stateLoading = ref(false);
const orderLoading = ref(false);
const stateTotal = ref(0);
const orderTotal = ref(0);
const stateList = ref<Array<Record<string, any>>>([]);
const orderList = ref<Array<Record<string, any>>>([]);
const resourceRows = reactive<Record<string, Array<Record<string, any>>>>({});
const resourceLoading = reactive<Record<string, boolean>>({});
const activeTab = ref<'orders' | 'states'>('states');
const vipConfigDrawerOpen = ref(false);
const resourceDrawerOpen = ref(false);
const activeResourceLevel = ref<null | Record<string, any>>(null);
const activeResourceFieldKey = ref<ResourceField['key']>('longBadge');
const resourceKeyword = ref('');

const form = reactive<Record<string, any>>({
  configured: false,
  levels: Array.from({ length: 5 }, (_, index) => createLevel(index + 1)),
  sysOrigin: '',
  updateTime: '',
});

const stateQuery = reactive<Record<string, any>>({
  cursor: 1,
  limit: 20,
  status: '',
  userId: '',
});

const orderQuery = reactive<Record<string, any>>({
  cursor: 1,
  limit: 20,
  orderType: '',
  status: '',
  userId: '',
});

const activeResourceField = computed<ResourceField>(
  () => findResourceField(activeResourceFieldKey.value) || resourceFields[0],
);

const activeResourceList = computed(() => {
  const field = activeResourceField.value;
  const keyword = resourceKeyword.value.trim().toLowerCase();
  const rows = resourceRows[field.key] || [];
  if (!keyword) {
    return rows;
  }
  return rows.filter((item) => {
    return [
      resourceOptionId(item),
      resourceOptionName(item),
      String(item.code || ''),
    ].some((value) => value.toLowerCase().includes(keyword));
  });
});

const activeResource = computed(() => {
  const record = activeResourceLevel.value;
  if (!record) {
    return createResource();
  }
  return normalizeResource(record[activeResourceField.value.key]);
});

const activeResourceTitle = computed(() => {
  const level = activeResourceLevel.value?.level || '-';
  return `VIP ${level} 素材配置`;
});

function createResource() {
  return {
    cover: '',
    coverUrl: '',
    name: '',
    resourceId: '',
    url: '',
  };
}

function createLevel(level: number) {
  return {
    avatarFrame: createResource(),
    backgroundCard: createResource(),
    chatBubble: createResource(),
    displayName: `VIP ${level}`,
    durationDays: 30,
    enabled: false,
    entryEffect: createResource(),
    effectImage: createResource(),
    floatPicture: createResource(),
    level,
    levelCode: `VIP${level}`,
    longBadge: createResource(),
    priceGold: 0,
    shortBadge: createResource(),
  };
}

function normalizeResource(value: null | Record<string, any> | undefined) {
  const resourceId = String(value?.resourceId ?? '').trim();
  const url = String(value?.url || value?.sourceUrl || value?.resourceUrl || '').trim();
  const cover = String(value?.cover || value?.coverUrl || value?.resourceCover || '').trim();
  return {
    cover,
    coverUrl: cover,
    name: String(value?.name || ''),
    resourceId: resourceId === '0' ? '' : resourceId,
    resourceUrl: url,
    sourceUrl: url,
    url,
  };
}

function normalizePropsSourceResource(value: null | Record<string, any> | undefined) {
  const resourceId = String(value?.id ?? value?.resourceId ?? '').trim();
  const cover = String(
    value?.cover || value?.coverUrl || value?.resourceCover || value?.selectUrl || '',
  ).trim();
  const url = String(
    value?.sourceUrl ||
      value?.resourceUrl ||
      value?.url ||
      value?.animationUrl ||
      cover ||
      '',
  ).trim();
  return {
    cover,
    coverUrl: cover,
    name: String(value?.name || ''),
    resourceId,
    resourceUrl: url,
    sourceUrl: url,
    url,
  };
}

function findResourceField(key: string) {
  return resourceFields.find((item) => item.key === key);
}

function resetResourceRows() {
  for (const field of resourceFields) {
    resourceRows[field.key] = [];
    resourceLoading[field.key] = false;
  }
}

function buildResourceSavePayload(value: null | Record<string, any> | undefined) {
  const resource = normalizeResource(value);
  return {
    resourceId: resource.resourceId || '0',
  };
}

async function loadResourceOptions(field: ResourceField) {
  if (!form.sysOrigin || resourceLoading[field.key]) {
    return;
  }
  resourceLoading[field.key] = true;
  try {
    resourceRows[field.key] = filterResourceRowsByField(
      await listSysOriginTypeList(form.sysOrigin, field.type),
      field,
    );
  } finally {
    resourceLoading[field.key] = false;
  }
}

function filterResourceRowsByField(rows: Array<Record<string, any>>, field: ResourceField) {
  if (!('badgeDisplayTypes' in field) || !field.badgeDisplayTypes?.length) {
    return rows;
  }
  const allowed = new Set(field.badgeDisplayTypes.map((item) => String(item).toUpperCase()));
  return rows.filter((item) => allowed.has(resourceBadgeDisplayType(item)));
}

function resourceBadgeDisplayType(value: Record<string, any>) {
  const expand = parseJsonObject(value.expand);
  const displayType = String(expand.badgeDisplayType || expand.displayType || '').toUpperCase();
  if (displayType === 'LONG' || displayType === 'SHORT') {
    return displayType;
  }
  return String(expand.badgeType || 'ACTIVITY').toUpperCase() === 'VIP' ? 'LONG' : 'SHORT';
}

function parseJsonObject(value: unknown) {
  if (typeof value !== 'string' || !value.trim()) {
    return {} as Record<string, any>;
  }
  try {
    const parsed = JSON.parse(value);
    return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
      ? (parsed as Record<string, any>)
      : ({} as Record<string, any>);
  } catch {
    return {} as Record<string, any>;
  }
}

async function loadAllResourceOptions() {
  await Promise.all(resourceFields.map((field) => loadResourceOptions(field)));
}

function normalizeLevels(levels: Array<Record<string, any>> | undefined) {
  const levelMap = new Map<number, Record<string, any>>();
  for (const item of levels || []) {
    const level = Number(item.level || 0);
    if (level >= 1 && level <= 5) {
      levelMap.set(level, item);
    }
  }
  return Array.from({ length: 5 }, (_, index) => {
    const level = index + 1;
    const item = (levelMap.get(level) || createLevel(level)) as Record<string, any>;
    return {
      ...createLevel(level),
      displayName: String(item.displayName || `VIP ${level}`),
      enabled: Boolean(item.enabled),
      priceGold: Number(item.priceGold || 0),
      longBadge: normalizeResource(item.longBadge || item.badge),
      shortBadge: normalizeResource(item.shortBadge),
      avatarFrame: normalizeResource(item.avatarFrame),
      entryEffect: normalizeResource(item.entryEffect),
      chatBubble: normalizeResource(item.chatBubble),
      floatPicture: normalizeResource(item.floatPicture),
      backgroundCard: normalizeResource(item.backgroundCard),
      effectImage: normalizeResource(item.effectImage),
    };
  });
}

function applyConfig(result: null | Record<string, any> | undefined) {
  const value = result ?? {};
  form.configured = Boolean(value.configured);
  form.sysOrigin = String(value.sysOrigin || form.sysOrigin || sysOriginOptions.value[0]?.value || '');
  form.updateTime = String(value.updateTime || '');
  form.levels = normalizeLevels(value.levels);
}

function buildSavePayload() {
  return {
    sysOrigin: form.sysOrigin,
    levels: (form.levels || []).map((item: Record<string, any>) => ({
      avatarFrame: buildResourceSavePayload(item.avatarFrame),
      badge: buildResourceSavePayload(item.longBadge),
      backgroundCard: buildResourceSavePayload(item.backgroundCard),
      chatBubble: buildResourceSavePayload(item.chatBubble),
      displayName: String(item.displayName || '').trim(),
      durationDays: 30,
      enabled: Boolean(item.enabled),
      entryEffect: buildResourceSavePayload(item.entryEffect),
      effectImage: buildResourceSavePayload(item.effectImage),
      floatPicture: buildResourceSavePayload(item.floatPicture),
      level: Number(item.level || 0),
      levelCode: `VIP${Number(item.level || 0)}`,
      longBadge: buildResourceSavePayload(item.longBadge),
      priceGold: Number(item.priceGold || 0),
      shortBadge: buildResourceSavePayload(item.shortBadge),
    })),
  };
}

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

async function loadStates(reset = false) {
  if (!form.sysOrigin) {
    stateList.value = [];
    stateTotal.value = 0;
    return;
  }
  if (reset) {
    stateQuery.cursor = 1;
  }
  stateLoading.value = true;
  try {
    const result = await pageResidentVipUserStates({
      ...stateQuery,
      sysOrigin: form.sysOrigin,
    });
    stateList.value = Array.isArray(result.records) ? result.records : [];
    stateTotal.value = Number(result.total || 0);
  } finally {
    stateLoading.value = false;
  }
}

async function loadOrders(reset = false) {
  if (!form.sysOrigin) {
    orderList.value = [];
    orderTotal.value = 0;
    return;
  }
  if (reset) {
    orderQuery.cursor = 1;
  }
  orderLoading.value = true;
  try {
    const result = await pageResidentVipOrders({
      ...orderQuery,
      sysOrigin: form.sysOrigin,
    });
    orderList.value = Array.isArray(result.records) ? result.records : [];
    orderTotal.value = Number(result.total || 0);
  } finally {
    orderLoading.value = false;
  }
}

async function loadData() {
  await Promise.all([loadConfig(), loadStates(true), loadOrders(true), loadAllResourceOptions()]);
}

async function submitForm() {
  saving.value = true;
  try {
    await saveResidentVipConfig(buildSavePayload());
    message.success('保存成功');
    await loadConfig();
  } finally {
    saving.value = false;
  }
}

function handleSave() {
  Modal.confirm({
    async onOk() {
      await submitForm();
    },
    title: '确认保存当前系统的 VIP 配置？',
    content: '保存后 app 会立即按新的等级价格和资源快照售卖 VIP。',
  });
}

function handleStateSearch() {
  void loadStates(true);
}

function handleOrderSearch() {
  void loadOrders(true);
}

function handleStatePageChange(page: number, pageSize: number) {
  stateQuery.cursor = page;
  stateQuery.limit = pageSize;
  void loadStates();
}

function handleOrderPageChange(page: number, pageSize: number) {
  orderQuery.cursor = page;
  orderQuery.limit = pageSize;
  void loadOrders();
}

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

function orderTypeText(type: string) {
  const match = orderTypeOptions.find((item) => item.value === String(type || '').toUpperCase());
  return match?.label || type || '-';
}

function errorMessageText(record: Record<string, any>) {
  return String(record.errorMessage || '').trim();
}

function selectedResourceSummaries(record: Record<string, any>) {
  return resourceFields
    .map((field) => {
      const resource = normalizeResource(record[field.key]);
      const name = String(resource.name || resource.resourceId || '').trim();
      if (!name) {
        return null;
      }
      return {
        cover: resource.cover,
        key: field.key,
        name,
        title: field.title,
      };
    })
    .filter(Boolean) as Array<{
    cover: string;
    key: string;
    name: string;
    title: string;
  }>;
}

function selectedResourceCount(record: Record<string, any>) {
  return selectedResourceSummaries(record).length;
}

function resourceFieldSelected(record: Record<string, any>, field: ResourceField) {
  return Boolean(normalizeResource(record[field.key]).resourceId);
}

function resourceOptionId(item: Record<string, any>) {
  return String(item.id ?? item.resourceId ?? item.sourceId ?? '').trim();
}

function resourceOptionName(item: Record<string, any>) {
  const id = resourceOptionId(item);
  return String(
    item.name ||
      item.resourceName ||
      item.sourceName ||
      item.badgeName ||
      item.title ||
      (id ? `资源 ${id}` : ''),
  ).trim();
}

function resourceOptionCover(item: Record<string, any>) {
  return String(
    item.cover ||
      item.coverUrl ||
      item.resourceCover ||
      item.resourceCoverUrl ||
      item.selectUrl ||
      item.icon ||
      item.sourceUrl ||
      item.resourceUrl ||
      item.animationUrl ||
      '',
  ).trim();
}

function resourceOptionMeta(item: Record<string, any>) {
  return [resourceOptionId(item) ? `ID ${resourceOptionId(item)}` : '', item.code || '']
    .filter(Boolean)
    .join(' / ');
}

function openResourceDrawer(record: Record<string, any>) {
  activeResourceLevel.value = record;
  activeResourceFieldKey.value =
    resourceFields.find((field) => !resourceFieldSelected(record, field))?.key ||
    resourceFields[0].key;
  resourceKeyword.value = '';
  resourceDrawerOpen.value = true;
  void loadAllResourceOptions();
}

function closeResourceDrawer() {
  resourceDrawerOpen.value = false;
  activeResourceLevel.value = null;
}

function selectResourceField(field: ResourceField) {
  activeResourceFieldKey.value = field.key;
  resourceKeyword.value = '';
  void loadResourceOptions(field);
}

function selectResource(item: Record<string, any>) {
  const record = activeResourceLevel.value;
  if (!record) {
    return;
  }
  record[activeResourceField.value.key] = normalizePropsSourceResource(item);
}

function clearActiveResource() {
  const record = activeResourceLevel.value;
  if (!record) {
    return;
  }
  record[activeResourceField.value.key] = createResource();
}

function isActiveResource(item: Record<string, any>) {
  return String(activeResource.value.resourceId) === resourceOptionId(item);
}

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

watch(
  () => form.sysOrigin,
  (value, oldValue) => {
    if (value) {
      if (value !== oldValue) {
        resetResourceRows();
      }
      void loadData();
    }
  },
  { immediate: true },
);
</script>

<template>
  <Page auto-content-height>
    <Spin :spinning="loading">
      <Card :bordered="false" title="VIP管理">
        <div class="tabs-head">
          <Tabs v-model:active-key="activeTab" class="vip-tabs">
            <TabPane key="states" tab="用户VIP状态">
              <Space class="toolbar" wrap>
                <Input
                  v-model:value="stateQuery.userId"
                  allow-clear
                  placeholder="用户ID"
                  style="width: 160px"
                />
                <Select
                  v-model:value="stateQuery.status"
                  allow-clear
                  :options="statusOptions"
                  placeholder="状态"
                  style="width: 140px"
                />
                <Button :loading="stateLoading" type="primary" @click="handleStateSearch">
                  查询
                </Button>
              </Space>

              <Table
                :columns="stateColumns"
                :data-source="stateList"
                :loading="stateLoading"
                :pagination="false"
                row-key="userId"
                :scroll="{ x: 760 }"
              >
                <template #bodyCell="{ column, record }">
                  <template v-if="column.key === 'status'">
                    <Tag :color="statusColor(record.status)">
                      {{ record.status || '-' }}
                    </Tag>
                  </template>
                  <template v-else-if="column.key === 'level'">
                    <Tag v-if="record.active" color="gold">VIP {{ record.level }}</Tag>
                    <span v-else>-</span>
                  </template>
                </template>
              </Table>

              <div v-if="stateTotal > 0" class="pager">
                <Pagination
                  :current="stateQuery.cursor"
                  :page-size="stateQuery.limit"
                  :total="stateTotal"
                  show-size-changer
                  @change="handleStatePageChange"
                  @show-size-change="handleStatePageChange"
                />
              </div>
            </TabPane>

            <TabPane key="orders" tab="VIP订单记录">
              <Space class="toolbar" wrap>
                <Input
                  v-model:value="orderQuery.userId"
                  allow-clear
                  placeholder="用户ID"
                  style="width: 160px"
                />
                <Select
                  v-model:value="orderQuery.orderType"
                  allow-clear
                  :options="orderTypeOptions"
                  placeholder="订单类型"
                  style="width: 140px"
                />
                <Select
                  v-model:value="orderQuery.status"
                  allow-clear
                  :options="orderStatusOptions"
                  placeholder="状态"
                  style="width: 140px"
                />
                <Button :loading="orderLoading" type="primary" @click="handleOrderSearch">
                  查询
                </Button>
              </Space>

              <Table
                :columns="orderColumns"
                :data-source="orderList"
                :loading="orderLoading"
                :pagination="false"
                row-key="id"
                :scroll="{ x: 1370 }"
              >
                <template #bodyCell="{ column, record }">
                  <template v-if="column.key === 'orderType'">
                    {{ orderTypeText(record.orderType) }}
                  </template>
                  <template v-else-if="column.key === 'levelChange'">
                    VIP {{ record.fromLevel || 0 }} → VIP {{ record.toLevel || 0 }}
                  </template>
                  <template v-else-if="column.key === 'status'">
                    <Tag :color="statusColor(record.status)">
                      {{ record.status || '-' }}
                    </Tag>
                  </template>
                  <template v-else-if="column.key === 'errorMessage'">
                    <Tooltip v-if="errorMessageText(record)" :title="errorMessageText(record)">
                      <span class="failure-icon">!</span>
                    </Tooltip>
                    <span v-else>-</span>
                  </template>
                </template>
              </Table>

              <div v-if="orderTotal > 0" class="pager">
                <Pagination
                  :current="orderQuery.cursor"
                  :page-size="orderQuery.limit"
                  :total="orderTotal"
                  show-size-changer
                  @change="handleOrderPageChange"
                  @show-size-change="handleOrderPageChange"
                />
              </div>
            </TabPane>
          </Tabs>

          <Button class="config-entry" @click="vipConfigDrawerOpen = true">
            VIP配置
          </Button>
        </div>
      </Card>

      <Drawer
        :open="vipConfigDrawerOpen"
        destroy-on-close
        title="VIP配置"
        width="1180"
        @close="vipConfigDrawerOpen = false"
      >
        <Table
          :columns="levelColumns"
          :data-source="form.levels"
          :pagination="false"
          row-key="level"
          :scroll="{ x: 1050 }"
        >
          <template #bodyCell="{ column, record }">
            <template v-if="column.key === 'level'">
              <Tag color="gold">VIP {{ record.level }}</Tag>
            </template>
            <template v-else-if="column.key === 'enabled'">
              <Switch v-model:checked="record.enabled" />
            </template>
            <template v-else-if="column.key === 'displayName'">
              <Input v-model:value="record.displayName" placeholder="等级名称" />
            </template>
            <template v-else-if="column.key === 'priceGold'">
              <InputNumber
                v-model:value="record.priceGold"
                :min="0"
                :precision="0"
                addon-after="金币"
                style="width: 130px"
              />
            </template>
            <template v-else-if="column.key === 'resources'">
              <div class="resource-cell">
                <div class="resource-cell-main">
                  <div v-if="selectedResourceSummaries(record).length > 0" class="resource-chip-list">
                    <span
                      v-for="item in selectedResourceSummaries(record)"
                      :key="item.key"
                      class="resource-chip"
                    >
                      <img v-if="item.cover" :src="item.cover" alt="" class="resource-chip-cover" />
                      <span class="resource-chip-text">{{ item.title }}: {{ item.name }}</span>
                    </span>
                  </div>
                  <span v-else class="empty-resource">未选择素材</span>
                  <div class="resource-cell-count">
                    已选 {{ selectedResourceCount(record) }} / {{ resourceFields.length }}
                  </div>
                </div>
                <Button size="small" type="primary" @click="openResourceDrawer(record)">
                  配置素材
                </Button>
              </div>
            </template>
          </template>
        </Table>

        <div class="tips">
          <div>固定 5 个等级，每次购买 30 天；同等级再次购买会续 30 天。</div>
          <div>资源从道具资源配置选择，保存时会记录当前资源快照。</div>
          <div>用户只能升级，升级时支付目标等级和当前等级的差价，到期后资源自动失效。</div>
          <div>升级成功后会把长徽章、短徽章、头像框、进场特效、聊天气泡、飘窗和背景卡替换成新等级资源；动效图只用于 VIP 页面展示。</div>
        </div>

        <Space class="actions" wrap>
          <Button :loading="saving" type="primary" @click="handleSave">
            保存配置
          </Button>
          <span v-if="form.updateTime" class="field-desc">最后更新时间：{{ form.updateTime }}</span>
        </Space>
      </Drawer>

      <Drawer
        :open="resourceDrawerOpen"
        destroy-on-close
        :title="activeResourceTitle"
        width="960"
        @close="closeResourceDrawer"
      >
        <div class="resource-drawer">
          <aside class="resource-field-list">
            <button
              v-for="field in resourceFields"
              :key="field.key"
              class="resource-field-item"
              :class="{
                'is-active': activeResourceField.key === field.key,
                'is-selected': activeResourceLevel && resourceFieldSelected(activeResourceLevel, field),
              }"
              type="button"
              @click="selectResourceField(field)"
            >
              <span>{{ field.title }}</span>
              <Tag
                v-if="activeResourceLevel && resourceFieldSelected(activeResourceLevel, field)"
                color="success"
              >
                已选
              </Tag>
            </button>
          </aside>

          <section class="resource-picker">
            <div class="resource-picker-head">
              <div>
                <div class="resource-picker-title">{{ activeResourceField.title }}</div>
                <div class="resource-picker-subtitle">
                  当前：{{ activeResource.name || activeResource.resourceId || '未选择' }}
                </div>
              </div>
              <Space>
                <Input
                  v-model:value="resourceKeyword"
                  allow-clear
                  placeholder="资源名称 / ID"
                  style="width: 240px"
                />
                <Button
                  :disabled="!activeResource.resourceId"
                  size="small"
                  @click="clearActiveResource"
                >
                  清空
                </Button>
              </Space>
            </div>

            <Spin :spinning="resourceLoading[activeResourceField.key]">
              <div v-if="activeResourceList.length > 0" class="resource-grid">
                <button
                  v-for="item in activeResourceList"
                  :key="`${activeResourceField.key}-${resourceOptionId(item)}`"
                  class="resource-card"
                  :class="{ 'is-active': isActiveResource(item) }"
                  type="button"
                  @click="selectResource(item)"
                >
                  <div class="resource-cover-wrap">
                    <img
                      v-if="resourceOptionCover(item)"
                      :src="resourceOptionCover(item)"
                      alt=""
                      class="resource-cover"
                    />
                    <span v-else class="resource-cover-empty">无图</span>
                  </div>
                  <div class="resource-name">{{ resourceOptionName(item) || '-' }}</div>
                  <div class="resource-meta">{{ resourceOptionMeta(item) || '-' }}</div>
                </button>
              </div>
              <Empty v-else description="暂无可选资源" />
            </Spin>
          </section>
        </div>
      </Drawer>
    </Spin>
  </Page>
</template>

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

.tabs-head {
  position: relative;
}

.vip-tabs {
  width: 100%;
}

.config-entry {
  position: absolute;
  right: 0;
  top: 0;
  z-index: 1;
}

.field-desc {
  color: rgb(100 116 139);
}

.tips {
  background: rgb(248 250 252);
  border-radius: 8px;
  color: rgb(71 85 105);
  line-height: 1.8;
  margin-top: 16px;
  padding: 16px;
}

.actions {
  margin-top: 20px;
}

.resource-cell {
  align-items: center;
  display: flex;
  gap: 12px;
  justify-content: space-between;
}

.resource-cell-main {
  min-width: 0;
}

.resource-chip-list {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  max-height: 58px;
  overflow: hidden;
}

.resource-chip {
  align-items: center;
  background: rgb(248 250 252);
  border: 1px solid rgb(226 232 240);
  border-radius: 6px;
  color: rgb(51 65 85);
  display: inline-flex;
  gap: 5px;
  max-width: 180px;
  padding: 3px 6px;
}

.resource-chip-cover {
  border-radius: 4px;
  height: 18px;
  object-fit: cover;
  width: 18px;
}

.resource-chip-text {
  font-size: 12px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.resource-cell-count,
.empty-resource {
  color: rgb(100 116 139);
  font-size: 12px;
}

.resource-cell-count {
  margin-top: 6px;
}

.failure-icon {
  align-items: center;
  background: rgb(254 226 226);
  border: 1px solid rgb(248 113 113);
  border-radius: 50%;
  color: rgb(220 38 38);
  cursor: help;
  display: inline-flex;
  font-size: 12px;
  font-weight: 700;
  height: 20px;
  justify-content: center;
  width: 20px;
}

.resource-drawer {
  align-items: start;
  display: grid;
  gap: 20px;
  grid-template-columns: 180px minmax(0, 1fr);
}

.resource-field-list {
  align-self: start;
  border-right: 1px solid rgb(226 232 240);
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-right: 16px;
}

.resource-field-item {
  align-items: center;
  background: #fff;
  border: 1px solid rgb(226 232 240);
  border-radius: 8px;
  color: rgb(51 65 85);
  cursor: pointer;
  display: flex;
  flex: 0 0 auto;
  justify-content: space-between;
  min-height: 40px;
  padding: 0 10px;
  text-align: left;
}

.resource-field-item.is-active {
  background: rgb(239 246 255);
  border-color: rgb(37 99 235);
  color: rgb(29 78 216);
}

.resource-field-item.is-selected:not(.is-active) {
  border-color: rgb(34 197 94);
}

.resource-picker {
  max-height: calc(100vh - 160px);
  min-width: 0;
  overflow-y: auto;
  padding-right: 4px;
}

.resource-picker-head {
  align-items: center;
  display: flex;
  gap: 16px;
  justify-content: space-between;
  margin-bottom: 16px;
}

.resource-picker-title {
  color: rgb(15 23 42);
  font-size: 16px;
  font-weight: 600;
}

.resource-picker-subtitle {
  color: rgb(100 116 139);
  font-size: 12px;
  margin-top: 4px;
  max-width: 360px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.resource-grid {
  display: grid;
  gap: 14px;
  grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
}

.resource-card {
  background: #fff;
  border: 1px solid rgb(226 232 240);
  border-radius: 8px;
  cursor: pointer;
  min-height: 160px;
  padding: 12px;
  text-align: center;
  transition:
    border-color 0.2s ease,
    box-shadow 0.2s ease,
    transform 0.2s ease;
}

.resource-card:hover,
.resource-card.is-active {
  border-color: rgb(37 99 235);
  box-shadow: 0 12px 24px rgb(37 99 235 / 12%);
}

.resource-card:hover {
  transform: translateY(-2px);
}

.resource-cover-wrap {
  align-items: center;
  display: flex;
  height: 88px;
  justify-content: center;
  margin-bottom: 10px;
}

.resource-cover {
  border-radius: 8px;
  max-height: 88px;
  max-width: 104px;
  object-fit: contain;
}

.resource-cover-empty {
  align-items: center;
  background: rgb(241 245 249);
  border-radius: 8px;
  color: rgb(100 116 139);
  display: inline-flex;
  height: 72px;
  justify-content: center;
  width: 72px;
}

.resource-name {
  color: rgb(15 23 42);
  font-weight: 600;
  line-height: 1.4;
  min-height: 40px;
  word-break: break-all;
}

.resource-meta {
  color: rgb(100 116 139);
  font-size: 12px;
  margin-top: 6px;
  word-break: break-all;
}

.pager {
  display: flex;
  justify-content: flex-end;
  margin-top: 16px;
}
</style>
