<script lang="ts" setup>
/**
 * VIP 升级/开通弹窗
 * 等级/时长用两行横向滑动单选卡片，预览丝带随所选时长自动切换
 */
import { computed, ref, watch } from 'vue';

import { useVbenModal } from '@vben/common-ui';

import { Input, InputNumber, message } from 'ant-design-vue';

import VipBadgeCombo from '#/components/vip/VipBadgeCombo.vue';
import { getVipDurationOption } from '#/views/system/vip/duration/api';
import { getVipLevelOption } from '#/views/system/vip/level/api';
import {
  getVipDurationPresets,
  openVipStore,
} from '#/views/system/vip/store/api';

/** 时长预设 → 时效类型 code（与后端 VipDurationHelper 对齐；keep 走当前会员 duration_type） */
const PRESET_TO_DURATION: Record<string, string> = {
  '7d': 'trial',
  '1w': 'week',
  '1m': 'month',
  '3m': 'month',
  '6m': 'month',
  '1y': 'year',
  xy: 'year',
  lifetime: 'lifetime',
};

const gridApi = ref<any>();
const onSuccess = ref<null | (() => void)>(null);
const storeId = ref(0);
const storeName = ref('');
/** 打开弹窗时带入的当前门店 VIP，用于判断能否「继承剩余时间」 */
const currentVip = ref<Record<string, any> | null>(null);
const levelId = ref<number | undefined>();
const durationPreset = ref<string | undefined>();
const customYears = ref<number | undefined>();
const remark = ref('');

const levels = ref<any[]>([]);
const presets = ref<{ label: string; value: string }[]>([]);
const durationTypes = ref<any[]>([]);

const selectedLevel = computed(() =>
  levels.value.find((i) => i.id === levelId.value),
);

/**
 * 是否可继承剩余时间：非普通会员（非 V0）且未过期
 * 普通会员没有可继承的到期时间，不展示/不默认 keep
 */
const canKeepDuration = computed(() => {
  const v = currentVip.value;
  if (!v) return false;
  const code = String(v.level_code || 'V0').trim() || 'V0';
  if (code === 'V0') return false;
  const expireAt = Number(v.expire_at || 0);
  // expire_at=0 表示终身，视为可继承；>0 须未过期
  if (expireAt > 0 && expireAt * 1000 <= Date.now()) return false;
  return true;
});

/** 按时长预设解析对应丝带资源 */
function ribbonByPreset(preset?: string) {
  if (preset === 'keep') {
    const code = String(currentVip.value?.duration_type || '').trim();
    if (!code) return null;
    return durationTypes.value.find((i) => i.code === code) || null;
  }
  const code = PRESET_TO_DURATION[preset || ''] || '';
  if (!code) return null;
  return durationTypes.value.find((i) => i.code === code) || null;
}

const selectedDurationType = computed(() =>
  ribbonByPreset(durationPreset.value),
);

/** 时长卡片：普通会员隐藏 keep；其它预设照常 */
const durationCards = computed(() =>
  presets.value
    .filter((p) => p.value !== 'keep' || canKeepDuration.value)
    .map((p) => {
      const ribbon = ribbonByPreset(p.value);
      return {
        ...p,
        ribbonUrl: ribbon?.badge_url || '',
        ribbonName: ribbon?.name || '',
      };
    }),
);

const previewBadgeUrl = computed(() => selectedLevel.value?.badge_url || '');
const previewRibbonUrl = computed(
  () => selectedDurationType.value?.badge_url || '',
);
const previewLevelName = computed(
  () => selectedLevel.value?.name || '请选择等级',
);
const previewDurationLabel = computed(() => {
  if (!durationPreset.value) return '请选择时长';
  if (durationPreset.value === 'keep') {
    const label = String(currentVip.value?.duration_label || '').trim();
    const expireHint = formatVipExpireHint(currentVip.value);
    if (label && expireHint) return `继承剩余：${label}（${expireHint}）`;
    if (label) return `继承剩余：${label}`;
    return '继承剩余时间（不改动）';
  }
  const preset = presets.value.find((p) => p.value === durationPreset.value);
  return preset?.label || selectedDurationType.value?.name || '请选择时长';
});

/**
 * 格式化当前会员到期提示（终身 / 已格式化字符串 / 时间戳）
 */
function formatVipExpireHint(v: Record<string, any> | null | undefined): string {
  if (!v) return '';
  if (v.is_lifetime) return '终身';
  const raw = v.expire_at;
  if (raw == null || raw === '' || raw === 0) return '';
  if (typeof raw === 'string' && Number.isNaN(Number(raw))) {
    return `至 ${raw}`;
  }
  const n = Number(raw);
  if (!(n > 0)) return '';
  const d = new Date(n * 1000);
  if (Number.isNaN(d.getTime())) return '';
  const pad = (x: number) => String(x).padStart(2, '0');
  return `至 ${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
}

const title = computed(() => `升级VIP - ${storeName.value || storeId.value}`);

/** 选择会员等级 */
function selectLevel(id: number) {
  levelId.value = id;
}

/** 选择开通时长（同步驱动预览丝带） */
function selectDuration(value: string) {
  durationPreset.value = value;
}

const [Modal, modalApi] = useVbenModal({
  fullscreenButton: false,
  draggable: true,
  onCancel() {
    modalApi.close();
  },
  onConfirm: async () => {
    if (!levelId.value) {
      message.warning('请选择会员等级');
      return;
    }
    if (!durationPreset.value) {
      message.warning('请选择开通时长');
      return;
    }
    if (
      durationPreset.value === 'xy' &&
      !(Number(customYears.value) > 0)
    ) {
      message.warning('请填写有效年数');
      return;
    }
    modalApi.setState({ confirmLoading: true });
    try {
      await openVipStore({
        store_id: storeId.value,
        level_id: levelId.value,
        duration_preset: durationPreset.value,
        custom_years: customYears.value || 0,
        remark: remark.value || '',
      });
      message.success('开通成功');
      gridApi.value?.query?.();
      onSuccess.value?.();
      modalApi.close();
    } finally {
      modalApi.setState({ confirmLoading: false });
    }
  },
  async onOpenChange(isOpen: boolean) {
    if (!isOpen) {
      levelId.value = undefined;
      durationPreset.value = undefined;
      customYears.value = undefined;
      remark.value = '';
      currentVip.value = null;
      return;
    }
    const data = modalApi.getData<Record<string, any>>() || {};
    gridApi.value = data.gridApi;
    onSuccess.value = data.onSuccess || null;
    storeId.value = Number(data.store_id || 0);
    storeName.value = data.store_name || '';
    currentVip.value = data.vip || null;
    const [levelList, presetList, durationList] = await Promise.all([
      getVipLevelOption(),
      getVipDurationPresets(),
      getVipDurationOption(),
    ]);
    levels.value = levelList || [];
    presets.value = (presetList || []).map((i: any) => ({
      label: i.label,
      value: i.value,
    }));
    durationTypes.value = durationList || [];
    // 回填当前 VIP 等级（便于续期/改档）
    if (data.vip?.level_id) {
      levelId.value = Number(data.vip.level_id);
    }
    // 非普通会员：默认选「继承剩余时间」，到期日不改动
    if (canKeepDuration.value) {
      durationPreset.value = 'keep';
    }
  },
});

watch(durationPreset, (v) => {
  if (v !== 'xy') customYears.value = undefined;
});
</script>
<template>
  <Modal :title="title" class="w-[50%]">
    <!-- 预览：黑金卡面 + 徽标丝带（随时长自动适配） -->
    <div class="vip-upgrade-preview mb-5">
      <div class="vip-upgrade-preview__glow"></div>
      <VipBadgeCombo
        size="xl"
        :badge-url="previewBadgeUrl"
        :duration-badge-url="previewRibbonUrl"
        :level-name="previewLevelName"
        :duration-label="previewDurationLabel"
      />
      <div class="vip-upgrade-preview__text">
        <div class="vip-upgrade-preview__title">{{ previewLevelName }}</div>
        <div class="vip-upgrade-preview__sub">{{ previewDurationLabel }}</div>
        <div v-if="selectedLevel" class="vip-upgrade-preview__price">
          原价 ¥{{ selectedLevel.price }} · 管理员开通实付 ¥0
        </div>
      </div>
    </div>
    <div class="space-y-4">
      <div>
        <div class="mb-2 text-sm font-medium text-gray-700 dark:text-gray-200">
          会员等级
        </div>
        <div class="vip-pick-scroll" role="listbox" aria-label="会员等级">
          <button
            v-for="item in levels"
            :key="item.id"
            type="button"
            class="vip-pick-card"
            :class="{ 'vip-pick-card--active': levelId === item.id }"
            role="option"
            :aria-selected="levelId === item.id"
            @click="selectLevel(item.id)"
          >
            <img
              v-if="item.badge_url"
              :src="item.badge_url"
              alt=""
              class="vip-pick-card__badge"
            />
            <div
              v-else
              class="vip-pick-card__badge vip-pick-card__badge--empty"
            >
              {{ item.code }}
            </div>
            <div class="vip-pick-card__name">{{ item.name }}</div>
            <div class="vip-pick-card__meta">
              {{ item.code }} · ¥{{ item.price }}
            </div>
          </button>
        </div>
      </div>
      <div>
        <div class="mb-2 text-sm font-medium text-gray-700 dark:text-gray-200">
          开通时长
        </div>
        <div class="vip-pick-scroll" role="listbox" aria-label="开通时长">
          <button
            v-for="item in durationCards"
            :key="item.value"
            type="button"
            class="vip-pick-card vip-pick-card--duration"
            :class="{ 'vip-pick-card--active': durationPreset === item.value }"
            role="option"
            :aria-selected="durationPreset === item.value"
            @click="selectDuration(item.value)"
          >
            <img
              v-if="item.ribbonUrl"
              :src="item.ribbonUrl"
              alt=""
              class="vip-pick-card__ribbon"
            />
            <div
              v-else
              class="vip-pick-card__ribbon vip-pick-card__ribbon--empty"
            >
              丝带
            </div>
            <div class="vip-pick-card__name">{{ item.label }}</div>
          </button>
        </div>
      </div>
      <div v-if="durationPreset === 'xy'">
        <div class="mb-1 text-sm text-gray-600 dark:text-gray-300">
          自定义年数
        </div>
        <InputNumber
          v-model:value="customYears"
          class="w-full"
          :min="1"
          :max="99"
          placeholder="请输入年数"
        />
      </div>
      <div>
        <div class="mb-1 text-sm text-gray-600 dark:text-gray-300">备注</div>
        <Input.TextArea v-model:value="remark" :rows="2" placeholder="选填" />
      </div>
    </div>
  </Modal>
</template>
<style scoped>
/* 黑金预览卡：与会员卡组件同一视觉语言，深色卡面亮暗主题通用 */
.vip-upgrade-preview {
  position: relative;
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 20px 24px;
  overflow: hidden;
  border-radius: 16px;
  background:
    radial-gradient(120% 160% at 100% 0%, rgba(212, 175, 55, 0.22) 0%, rgba(212, 175, 55, 0) 45%),
    linear-gradient(135deg, #23201a 0%, #12100c 55%, #1f1a12 100%);
  border: 1px solid rgba(220, 186, 110, 0.35);
  box-shadow:
    0 10px 26px rgba(0, 0, 0, 0.24),
    0 2px 8px rgba(212, 175, 55, 0.12);
  color: #f5e3bd;
}
.vip-upgrade-preview__glow {
  position: absolute;
  top: -60px;
  right: -40px;
  width: 200px;
  height: 200px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(240, 217, 168, 0.26) 0%, rgba(240, 217, 168, 0) 70%);
  pointer-events: none;
}
.vip-upgrade-preview__text {
  position: relative;
  min-width: 0;
}
.vip-upgrade-preview__title {
  font-size: 20px;
  font-weight: 700;
  margin-bottom: 4px;
  letter-spacing: 1px;
  text-shadow: 0 2px 6px rgba(0, 0, 0, 0.35);
}
.vip-upgrade-preview__sub {
  font-size: 13px;
  color: rgba(240, 224, 190, 0.85);
  margin-bottom: 6px;
}
.vip-upgrade-preview__price {
  font-size: 12px;
  color: rgba(240, 224, 190, 0.68);
}
/* 两行 + 横向滑动的单选卡片轨道 */
.vip-pick-scroll {
  display: grid;
  grid-auto-flow: column;
  grid-template-rows: repeat(2, minmax(0, auto));
  grid-auto-columns: 112px;
  gap: 10px;
  overflow-x: auto;
  overflow-y: hidden;
  padding: 4px 2px 10px;
  scroll-snap-type: x proximity;
  -webkit-overflow-scrolling: touch;
}
.vip-pick-scroll::-webkit-scrollbar {
  height: 6px;
}
.vip-pick-scroll::-webkit-scrollbar-thumb {
  background: rgba(0, 0, 0, 0.18);
  border-radius: 999px;
}
.vip-pick-card {
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 4px;
  min-height: 108px;
  padding: 10px 8px;
  border: 1.5px solid #e8e8e8;
  border-radius: 12px;
  background: #fff;
  cursor: pointer;
  transition:
    border-color 0.15s ease,
    box-shadow 0.15s ease,
    background 0.15s ease;
  text-align: center;
}
.vip-pick-card:hover {
  border-color: #6acdbb;
}
.vip-pick-card--active {
  border-color: #6acdbb;
  background: rgba(106, 205, 187, 0.1);
  box-shadow: 0 0 0 2px rgba(106, 205, 187, 0.25);
}
.vip-pick-card__badge {
  width: 48px;
  height: 48px;
  object-fit: contain;
}
.vip-pick-card__badge--empty {
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: #f5f5f5;
  color: #999;
  font-size: 12px;
  font-weight: 600;
}
.vip-pick-card__ribbon {
  width: 88px;
  height: 28px;
  object-fit: contain;
}
.vip-pick-card__ribbon--empty {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 88px;
  height: 28px;
  border-radius: 6px;
  background: #f5f5f5;
  color: #bbb;
  font-size: 11px;
}
.vip-pick-card--duration {
  min-height: 88px;
}
.vip-pick-card__name {
  font-size: 13px;
  font-weight: 600;
  color: #1f1f1f;
  line-height: 1.2;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.vip-pick-card__meta {
  font-size: 11px;
  color: #8c8c8c;
  line-height: 1.2;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* ========== 暗色适配（html.dark / .dark）========== */
/* 黑金预览卡为深色卡面，亮暗主题通用，无需暗色分支 */
html.dark .vip-pick-scroll::-webkit-scrollbar-thumb,
.dark .vip-pick-scroll::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.22);
}
html.dark .vip-pick-card,
.dark .vip-pick-card {
  border-color: #3f3f46;
  background: #1f1f23;
}
html.dark .vip-pick-card:hover,
.dark .vip-pick-card:hover {
  border-color: #6acdbb;
  background: #27272a;
}
html.dark .vip-pick-card--active,
.dark .vip-pick-card--active {
  border-color: #6acdbb;
  background: rgba(106, 205, 187, 0.16);
  box-shadow: 0 0 0 2px rgba(106, 205, 187, 0.28);
}
html.dark .vip-pick-card__badge--empty,
.dark .vip-pick-card__badge--empty,
html.dark .vip-pick-card__ribbon--empty,
.dark .vip-pick-card__ribbon--empty {
  background: #2a2a2e;
  color: #a1a1aa;
}
html.dark .vip-pick-card__name,
.dark .vip-pick-card__name {
  color: #f4f4f5;
}
html.dark .vip-pick-card__meta,
.dark .vip-pick-card__meta {
  color: #a1a1aa;
}
</style>
