<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref } from 'vue';
import { SButton, SButtonLoading, SCard, SConfigProvider, SInput, STag } from '@soybeanjs/ui';

type ChatItem = Record<string, any> & { _deleting?: boolean };
type BanItem = Record<string, any> & { _loading?: boolean };
type LogItem = { time?: string; level?: string; message?: string };
type GroupItem = { chat_id: number; name: string };
type ShopItem = Record<string, any> & { _loading?: boolean };
type LotteryItem = Record<string, any> & { _loading?: boolean };

const sseConnected = ref(false);
const chatLogs = ref<ChatItem[]>([]);
const bans = ref<BanItem[]>([]);
const logs = ref<LogItem[]>([]);
const groups = ref<GroupItem[]>([]);
const selectedChatId = ref<number | null>(null);
const shopItems = ref<ShopItem[]>([]);
const shopTransactions = ref<Record<string, any>[]>([]);
const lotteries = ref<LotteryItem[]>([]);
const lotteryParticipants = ref<Record<string, any>[]>([]);
const shopForm = reactive({ item_key: '', name: '', cost: 0, desc: '', enabled: true });
const lotteryForm = reactive({ id: 0, title: '', prize: '', description: '', condition_type: 'all', condition_value: 0, draw_type: 'people', end_type: 'people', end_value: 3, max_participants: 3, draw_at: '', status: 'active' });
const activePage = ref<'dashboard' | 'shop' | 'lottery' | 'bans'>('dashboard');
const activeTab = reactive({ shop: 'items', lottery: 'events' });
const toast = ref('');
const confirmState = reactive({ open: false, title: '', message: '', confirmText: '确认', danger: false, resolver: null as null | ((value: boolean) => void) });
const authReady = ref(false);
const authenticated = ref(false);
const loginForm = reactive({ username: '', password: '' });
const loggingIn = ref(false);
const shopSaving = ref(false);
const lotterySaving = ref(false);
const logsRefreshing = ref(false);
const copyingLogs = ref(false);
const newMessage = ref('');
const sending = ref(false);
const restarting = ref(false);
const restartingBot = ref(false);
const stats = reactive({ today_bans: 0, total_bans: 0, active_bans: 0 });
const logScroll = ref<HTMLElement | null>(null);
let es: EventSource | null = null;
let statTimer: number | undefined;

const chatCount = computed(() => chatLogs.value.length);
const selectedQuery = computed(() => selectedChatId.value ? `?chat_id=${selectedChatId.value}` : '');

const conditionOptions = [
  { value: 'all', label: '所有人', desc: '无需门槛' },
  { value: 'checkin', label: '已签到', desc: '今日签到后可参与' },
  { value: 'points', label: '积分门槛', desc: '达到指定积分' }
];
const drawOptions = [
  { value: 'people', label: '满人开奖', desc: '达到人数自动开奖' },
  { value: 'time', label: '定时开奖', desc: '到指定时间自动开奖' }
];
const statusOptions = [
  { value: 'draft', label: '草稿' },
  { value: 'active', label: '启用' }
];
const lotteryEditorTitle = computed(() => lotteryForm.id ? '编辑抽奖' : '新建抽奖');
const lotteryConditionSummary = computed(() => {
  if (lotteryForm.condition_type === 'checkin') return '今日已签到用户可参与';
  if (lotteryForm.condition_type === 'points') return `积分 ≥ ${Number(lotteryForm.condition_value || 0)} 可参与`;
  return '所有群成员均可参与';
});
const lotteryDrawSummary = computed(() => lotteryForm.draw_type === 'time'
  ? `定时开奖：${lotteryForm.draw_at ? formatTime(lotteryForm.draw_at) : '未设置时间'}`
  : `满 ${Number(lotteryForm.end_value || lotteryForm.max_participants || 1)} 人自动开奖`);
function selectLotteryCondition(type: string) {
  lotteryForm.condition_type = type;
  if (type !== 'points') lotteryForm.condition_value = 0;
}
function selectLotteryDraw(type: string) {
  lotteryForm.draw_type = type;
  lotteryForm.end_type = type;
  if (type === 'people') {
    lotteryForm.draw_at = '';
    lotteryForm.end_value = Number(lotteryForm.end_value || lotteryForm.max_participants || 3);
    lotteryForm.max_participants = Number(lotteryForm.end_value || 3);
  } else {
    lotteryForm.end_value = 0;
    lotteryForm.max_participants = 0;
    if (!lotteryForm.draw_at) setLotteryDrawAtMinutes(10);
  }
}
function setLotteryDrawAtMinutes(minutes: number) {
  const d = new Date(Date.now() + minutes * 60000);
  d.setSeconds(0, 0);
  lotteryForm.draw_at = toDatetimeLocal(d);
}
function toDatetimeLocal(date: Date) {
  const pad = (n: number) => String(n).padStart(2, '0');
  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`;
}

const pageTitle = computed(() => ({
  dashboard: '群组安全总览', shop: '商城管理', lottery: '抽奖管理', bans: '封禁记录'
}[activePage.value]));
const pageDesc = computed(() => ({
  dashboard: '实时审计聊天、封禁、日志和运营活动', shop: '商品 CRUD 与兑换交易记录', lottery: '抽奖项目、开奖条件与参与记录', bans: '封禁审计与手动解封'
}[activePage.value]));
function goPage(page: typeof activePage.value) {
  activePage.value = page;
  if (page === 'shop') { loadShopItems(); loadShopTransactions(); }
  if (page === 'lottery') loadLotteries();
  if (page === 'bans') loadBans();
}


function formatTime(value?: string) {
  if (!value) return '-';
  const text = String(value);
  const m = text.match(/(\d{2}:\d{2}:\d{2})/);
  if (m) return m[1];
  return text.replace('T', ' ').slice(11, 19) || text;
}
function compactLog(msg?: string) {
  const text = String(msg || '').replace(/\s+/g, ' ').trim();
  return text.length > 260 ? `${text.slice(0, 260)}...` : text;
}
function chatStatusText(item: ChatItem) {
  if (item.deleted) return '已删';
  if (item.spam) return '垃圾';
  if (Number(item.user_id || 0) === 0) return 'Web';
  return '正常';
}
function chatStatusClass(item: ChatItem) {
  if (item.deleted) return 'deleted';
  if (item.spam) return 'spam';
  if (Number(item.user_id || 0) === 0) return 'web';
  return 'normal';
}
function chatItemClass(item: ChatItem) {
  return ['chat-item', chatStatusClass(item)];
}
function tagColor(item: ChatItem) {
  if (item.deleted) return 'secondary';
  if (item.spam) return 'destructive';
  if (Number(item.user_id || 0) === 0) return 'accent';
  return 'primary';
}

function showToast(message: string) {
  toast.value = message;
  window.setTimeout(() => { if (toast.value === message) toast.value = ''; }, 2600);
}
function askConfirm(title: string, message: string, options: { confirmText?: string; danger?: boolean } = {}) {
  confirmState.open = true;
  confirmState.title = title;
  confirmState.message = message;
  confirmState.confirmText = options.confirmText || '确认';
  confirmState.danger = Boolean(options.danger);
  return new Promise<boolean>((resolve) => { confirmState.resolver = resolve; });
}
function closeConfirm(value: boolean) {
  confirmState.open = false;
  confirmState.resolver?.(value);
  confirmState.resolver = null;
}

async function checkAuth() {
  try {
    const data = await fetchJson('/api/auth/status');
    authenticated.value = Boolean(data.authenticated);
  } catch {
    authenticated.value = false;
  } finally {
    authReady.value = true;
  }
}
async function login() {
  if (!loginForm.username || !loginForm.password) return showToast('请输入账号和密码');
  loggingIn.value = true;
  try {
    await fetchJson('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(loginForm) });
    authenticated.value = true;
    await bootstrapData();
  } catch (e: any) {
    showToast(e.message || '登录失败');
  } finally { loggingIn.value = false; }
}
async function logout() {
  await fetchJson('/api/auth/logout', { method: 'POST' }).catch(() => {});
  authenticated.value = false;
}

async function fetchJson(url: string, options?: RequestInit) {
  const res = await fetch(url, options);
  const data = await res.json().catch(() => ({}));
  if (!res.ok || data.ok === false) throw new Error(data.error || data.message || `HTTP ${res.status}`);
  return data;
}
async function loadChatHistory() {
  try {
    const data = await fetchJson(`/api/chat${selectedQuery.value}`);
    chatLogs.value = data.map((m: ChatItem) => ({ ...m, _deleting: false }));
  } catch {}
}
async function loadBans() {
  try {
    const data = await fetchJson(`/api/bans${selectedQuery.value}`);
    bans.value = data.map((b: BanItem) => ({ ...b, _loading: false }));
  } catch {}
}
async function loadStats() {
  try {
    Object.assign(stats, await fetchJson(`/api/stats${selectedQuery.value}`));
  } catch {}
}
async function loadLogs() {
  try {
    logs.value = await fetchJson('/api/logs');
    scrollLogBottom();
  } catch {}
}
function scrollLogBottom() {
  nextTick(() => {
    if (logScroll.value) logScroll.value.scrollTop = logScroll.value.scrollHeight;
  });
}

async function loadGroups() {
  try {
    groups.value = await fetchJson('/api/groups');
    if (!selectedChatId.value && groups.value.length) selectedChatId.value = groups.value[0].chat_id;
  } catch {}
}
async function loadShopItems() {
  try { shopItems.value = (await fetchJson(`/api/shop-items${selectedQuery.value}`)).map((x: ShopItem) => ({ ...x, _loading: false })); } catch {}
}
async function loadShopTransactions() {
  try { shopTransactions.value = await fetchJson(`/api/shop-transactions${selectedQuery.value}`); } catch {}
}
async function saveShopItem() {
  if (!shopForm.item_key || !shopForm.name) return showToast('请填写商品编号和名称');
  if (shopSaving.value) return;
  shopSaving.value = true;
  try {
    await fetchJson('/api/shop-items', {
      method: 'POST', headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ ...shopForm, chat_id: selectedChatId.value || 0 })
    });
    Object.assign(shopForm, { item_key: '', name: '', cost: 0, desc: '', enabled: true });
    await loadShopItems();
  } catch (e: any) { showToast(`保存商品失败：${e.message}`); }
  finally { shopSaving.value = false; }
}
async function editShopItem(item: ShopItem) { Object.assign(shopForm, item); }
async function deleteShopItem(item: ShopItem) {
  if (item._loading) return;
  if (!await askConfirm('删除商品', `确认删除商品「${item.name}」？交易记录会保留。`, { confirmText: '删除', danger: true })) return;
  item._loading = true;
  try {
    await fetchJson(`/api/shop-items/${item.item_key}${selectedQuery.value}`, { method: 'DELETE' });
    await loadShopItems();
  } catch (e: any) { showToast(`删除商品失败：${e.message}`); }
  finally { item._loading = false; }
}
async function loadLotteries() {
  try { lotteries.value = (await fetchJson(`/api/lotteries${selectedQuery.value}`)).map((x: LotteryItem) => ({ ...x, _loading: false })); } catch {}
}
function resetLotteryForm() { Object.assign(lotteryForm, { id: 0, title: '', prize: '', description: '', condition_type: 'all', condition_value: 0, draw_type: 'people', end_type: 'people', end_value: 3, max_participants: 3, draw_at: '', status: 'active' }); }
function editLottery(item: LotteryItem) { Object.assign(lotteryForm, { ...item, id: item.id || 0 }); }
async function saveLottery() {
  if (!lotteryForm.title || !lotteryForm.prize) return showToast('请填写抽奖标题和奖品');
  if (lotteryForm.condition_type === 'points' && Number(lotteryForm.condition_value || 0) <= 0) return showToast('请填写积分门槛');
  if (lotteryForm.draw_type === 'people' && Number(lotteryForm.end_value || 0) <= 0) return showToast('请填写开奖人数');
  if (lotteryForm.draw_type === 'time' && !lotteryForm.draw_at) return showToast('请选择开奖时间');
  if (lotterySaving.value) return;
  lotterySaving.value = true;
  try {
    const payload = { ...lotteryForm, chat_id: selectedChatId.value || 0 };
    payload.end_type = payload.draw_type;
    if (payload.draw_type === 'people') {
      payload.end_value = Number(payload.end_value || payload.max_participants || 1);
      payload.max_participants = payload.end_value;
      payload.draw_at = '';
    } else {
      payload.end_value = 0;
      payload.max_participants = 0;
    }
    const url = lotteryForm.id ? `/api/lotteries/${lotteryForm.id}` : '/api/lotteries';
    await fetchJson(url, { method: lotteryForm.id ? 'PUT' : 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) });
    showToast(lotteryForm.id ? '抽奖已更新' : '抽奖已创建');
    resetLotteryForm();
    await loadLotteries();
  } catch (e: any) { showToast(`保存抽奖失败：${e.message}`); }
  finally { lotterySaving.value = false; }
}
async function deleteLottery(item: LotteryItem) {
  if (item._loading) return;
  if (!await askConfirm('删除抽奖', `确认删除抽奖「${item.title || item.prize}」？参与记录会一并移除。`, { confirmText: '删除', danger: true })) return;
  item._loading = true;
  try { await fetchJson(`/api/lotteries/${item.id}`, { method: 'DELETE' }); await loadLotteries(); }
  catch (e: any) { showToast(`删除抽奖失败：${e.message}`); }
  finally { item._loading = false; }
}
async function loadLotteryParticipants(item: LotteryItem) {
  lotteryParticipants.value = await fetchJson(`/api/lotteries/${item.id}/participants`);
}
async function cancelLottery(item: LotteryItem) {
  if (item._loading) return;
  if (!await askConfirm('取消抽奖', '确认取消这个抽奖？已参与用户不会自动补偿。', { confirmText: '取消抽奖', danger: true })) return;
  item._loading = true;
  try { await fetchJson(`/api/lotteries/${item.id}/cancel`, { method: 'POST' }); await loadLotteries(); }
  catch (e: any) { showToast(`取消抽奖失败：${e.message}`); }
  finally { item._loading = false; }
}
async function drawLottery(item: LotteryItem) {
  if (item._loading) return;
  if (!await askConfirm('立即开奖', '确认现在开奖？开奖后状态会变更。', { confirmText: '立即开奖' })) return;
  item._loading = true;
  try { await fetchJson(`/api/lotteries/${item.id}/draw`, { method: 'POST' }); await loadLotteries(); }
  catch (e: any) { showToast(`开奖失败：${e.message}`); }
  finally { item._loading = false; }
}
function reloadByGroup() { loadChatHistory(); loadBans(); loadStats(); loadShopItems(); loadShopTransactions(); loadLotteries(); }

function connectSSE() {
  if (es) es.close();
  es = new EventSource('/api/stream');
  es.addEventListener('connected', () => { sseConnected.value = true; });
  es.addEventListener('chat', (e) => {
    const data = JSON.parse((e as MessageEvent).data);
    chatLogs.value.unshift(data);
    if (chatLogs.value.length > 120) chatLogs.value.pop();
  });
  es.addEventListener('spam', (e) => {
    const data = JSON.parse((e as MessageEvent).data);
    const item = chatLogs.value.find(x => x.msg_id === data.msg_id);
    if (item) Object.assign(item, data);
  });
  es.addEventListener('chat_delete', (e) => {
    const data = JSON.parse((e as MessageEvent).data);
    const item = chatLogs.value.find(x => x.id === data.id);
    if (item) Object.assign(item, { deleted: true, manually_deleted: true, deleted_at: data.deleted_at });
    loadStats();
  });
  es.addEventListener('ban', (e) => {
    const data = JSON.parse((e as MessageEvent).data);
    bans.value.unshift({ ...data, _loading: false });
    if (bans.value.length > 80) bans.value.pop();
    loadStats();
  });
  es.addEventListener('unban', (e) => {
    const data = JSON.parse((e as MessageEvent).data);
    const ban = bans.value.find(b => b.user_id === data.user_id);
    if (ban) ban.auto_unban = false;
    loadStats();
  });
  es.addEventListener('log', (e) => {
    logs.value.push(JSON.parse((e as MessageEvent).data));
    if (logs.value.length > 300) logs.value.shift();
    scrollLogBottom();
  });
  es.onerror = () => {
    sseConnected.value = false;
    window.setTimeout(connectSSE, 3000);
  };
}

async function sendMessage() {
  const text = newMessage.value.trim();
  if (!text || sending.value) return;
  sending.value = true;
  try {
    await fetchJson('/api/send', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ text, chat_id: selectedChatId.value })
    });
    newMessage.value = '';
  } catch (e: any) {
    showToast(`发送失败：${e.message}`);
  } finally {
    sending.value = false;
  }
}
async function deleteChatMessage(item: ChatItem, ban = false) {
  const action = ban ? '删除并封禁' : '删除';
  if (!await askConfirm(action, `确认${action}这条消息？`, { confirmText: action, danger: true })) return;
  item._deleting = true;
  try {
    await fetchJson(`/api/messages/${item.id}/delete`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ ban_user: ban })
    });
    Object.assign(item, { deleted: true, manually_deleted: true });
    await loadStats();
  } catch (e: any) {
    showToast(`${action}失败：${e.message}`);
  } finally {
    item._deleting = false;
  }
}
async function unban(item: BanItem) {
  if (!await askConfirm('解除封禁', `确认解封 ${item.username || item.user_id}？`, { confirmText: '解封' })) return;
  item._loading = true;
  try {
    await fetchJson(`/api/unban/${item.user_id}`, { method: 'POST' });
    item.auto_unban = false;
    await loadStats();
  } catch (e: any) {
    showToast(`解封失败：${e.message}`);
  } finally {
    item._loading = false;
  }
}
async function restartBot() {
  if (!await askConfirm('重启 Bot', '确认只重启 Telegram Bot？Web 管理页不会重启。', { confirmText: '重启 Bot' })) return;
  restartingBot.value = true;
  try {
    await fetchJson('/api/restart-bot', { method: 'POST' });
    window.setTimeout(() => { restartingBot.value = false; loadLogs(); }, 5000);
  } catch (e: any) {
    restartingBot.value = false;
    showToast(`重启 Bot 失败：${e.message}`);
  }
}
async function restartService() {
  if (!await askConfirm('重启服务', '确认重启 TG Spam Guard 服务？重启期间 Web 和 Bot 会短暂不可用。', { confirmText: '重启服务', danger: true })) return;
  restarting.value = true;
  try {
    await fetchJson('/api/restart', { method: 'POST' });
  } catch {}
  window.setTimeout(() => location.reload(), 8000);
}
async function refreshLogs() {
  if (logsRefreshing.value) return;
  logsRefreshing.value = true;
  try { await loadLogs(); } finally { logsRefreshing.value = false; }
}
async function copyLogs() {
  if (copyingLogs.value) return;
  copyingLogs.value = true;
  try {
    const text = logs.value.map(l => `${formatTime(l.time)} ${l.level || ''} ${compactLog(l.message)}`).join('\n');
    await navigator.clipboard?.writeText(text);
    showToast('日志已复制');
  } finally { copyingLogs.value = false; }
}

async function bootstrapData() {
  await loadGroups().then(reloadByGroup);
  loadChatHistory();
  loadBans();
  loadStats();
  loadShopItems();
  loadLotteries();
  loadLogs();
  connectSSE();
  if (!statTimer) statTimer = window.setInterval(loadStats, 30000);
}
onMounted(async () => {
  await checkAuth();
  if (authenticated.value) await bootstrapData();
});
onBeforeUnmount(() => {
  if (es) es.close();
  if (statTimer) window.clearInterval(statTimer);
});
</script>

<template>
  <SConfigProvider :theme="{ theme: { primary: 'indigo' }, size: 'sm' }">
    <div v-if="!authReady" class="login-page"><div class="login-card">正在检查登录状态...</div></div>
    <div v-else-if="!authenticated" class="login-page"><form class="login-card" @submit.prevent="login"><img class="brand-icon" src="/icon.svg" alt="TG Spam Guard" /><h1>TG Spam Guard</h1><p>请输入管理账号密码</p><SInput v-model="loginForm.username" placeholder="账号" /><SInput v-model="loginForm.password" type="password" placeholder="密码" /><SButton type="submit" color="primary" :loading="loggingIn" :disabled="loggingIn">登录</SButton></form></div>
    <main v-else class="admin-shell">
      <aside class="sidebar">
        <div class="side-brand">
          <img class="brand-icon" src="/icon.svg" alt="TG Spam Guard" />
          <div>
            <h1>TG Spam Guard</h1>
            <p>群组安全后台</p>
          </div>
        </div>
        <div class="side-section">
          <span class="side-label">当前群组</span>
          <select v-model="selectedChatId" class="form-select side-select" @change="reloadByGroup"><option :value="null">全部群组</option><option v-for="g in groups" :key="g.chat_id" :value="g.chat_id">{{ g.label || g.name }}</option></select>
        </div>
        <nav class="side-nav">
          <button :class="{active: activePage === 'dashboard'}" @click="goPage('dashboard')"><span>总览</span><em>Dashboard</em></button>
          <button :class="{active: activePage === 'shop'}" @click="goPage('shop')"><span>商城管理</span><em>Shop</em></button>
          <button :class="{active: activePage === 'lottery'}" @click="goPage('lottery')"><span>抽奖管理</span><em>Lottery</em></button>
          <button :class="{active: activePage === 'bans'}" @click="goPage('bans')"><span>封禁记录</span><em>Bans</em></button>
        </nav>
        <div class="side-footer">
          <span class="conn" :class="sseConnected ? 'on' : 'off'">{{ sseConnected ? '实时连接' : '连接中' }}</span>
          <small>Bot 管理控制台</small>
        </div>
      </aside>

      <section class="admin-main">
        <header class="admin-header" id="dashboard">
          <div>
            <span class="eyebrow">CONTROL CENTER</span>
            <h2>{{ pageTitle }}</h2>
            <p>{{ pageDesc }}</p>
          </div>
          <div class="top-actions compact-actions">
            <SButton size="xs" variant="outline" @click="logout">退出</SButton>
            <SButton size="xs" variant="outline" color="primary" :loading="restartingBot" :disabled="restartingBot" @click="restartBot">重启 Bot</SButton>
            <SButton size="xs" variant="soft" color="warning" :loading="restarting" :disabled="restarting" @click="restartService">重启服务</SButton>
          </div>
        </header>

      <section v-show="activePage === 'dashboard'" class="stats-grid compact-stats">
        <SCard class="stat-card blue"><div class="stat-row"><span class="stat-icon">今</span><div><span>今日封禁</span><strong>{{ stats.today_bans || 0 }}</strong></div></div></SCard>
        <SCard class="stat-card red"><div class="stat-row"><span class="stat-icon">总</span><div><span>累计封禁</span><strong>{{ stats.total_bans || 0 }}</strong></div></div></SCard>
        <SCard class="stat-card orange"><div class="stat-row"><span class="stat-icon">封</span><div><span>当前封禁</span><strong>{{ stats.active_bans || 0 }}</strong></div></div></SCard>
        <SCard class="stat-card green"><div class="stat-row"><span class="stat-icon">聊</span><div><span>聊天记录</span><strong>{{ chatCount }}</strong></div></div></SCard>
      </section>

      <section v-show="activePage === 'dashboard'" class="main-grid">
        <SCard id="chat" class="panel chat-panel" title="聊天记录" :description="`${chatCount} 条`">
          <div class="scroll-area chat-scroll">
            <div v-if="!chatLogs.length" class="empty">等待消息...</div>
            <article v-for="(item, i) in chatLogs" :key="item.id || i" :class="chatItemClass(item)">
              <div class="chat-head">
                <div class="chat-user-line">
                  <STag size="xs" :color="tagColor(item)" variant="soft">{{ chatStatusText(item) }}</STag>
                  <span class="chat-user">@{{ item.username || item.first_name || 'unknown' }}</span>
                  <span class="chat-uid">{{ item.user_id }}</span>
                </div>
                <div class="chat-meta-line">
                  <span class="chat-id">#{{ item.msg_id || '-' }}</span>
                  <span class="chat-time">{{ formatTime(item.time) }}</span>
                </div>
              </div>
              <div class="chat-text">{{ item.text || '（空消息）' }}</div>
              <div class="chat-foot">
                <STag v-if="item.spam" class="reason-tag" size="xs" color="destructive" variant="soft" :title="item.reason || item.spam_reason">{{ item.reason || item.spam_reason }}</STag>
                <span v-else />
                <div class="chat-actions">
                  <STag v-if="item.deleted" size="xs" color="secondary" variant="soft">已删 · {{ formatTime(item.deleted_at) }}</STag>
                  <template v-else>
                    <SButton size="xs" variant="link" color="destructive" :loading="item._deleting" :disabled="item._deleting" @click="deleteChatMessage(item, false)">删除</SButton>
                    <SButton v-if="item.user_id" size="xs" variant="link" color="warning" :loading="item._deleting" :disabled="item._deleting" @click="deleteChatMessage(item, true)">删封</SButton>
                  </template>
                </div>
              </div>
            </article>
          </div>
        </SCard>

        <SCard class="panel log-panel" title="📜 实时日志" :description="`${logs.length} 条`">
          <template #extra>
            <div class="panel-extra">
              <SButtonLoading size="xs" variant="outline" :loading="copyingLogs" :disabled="copyingLogs" @click="copyLogs">复制</SButtonLoading>
              <SButtonLoading size="xs" variant="outline" :loading="logsRefreshing" :disabled="logsRefreshing" @click="refreshLogs">刷新</SButtonLoading>
            </div>
          </template>
          <div ref="logScroll" class="log-window">
            <div v-if="!logs.length" class="empty dark">等待日志...</div>
            <div v-for="(item, i) in logs" :key="i" class="log-line" :class="item.level" :title="compactLog(item.message)">
              <span class="log-time">{{ formatTime(item.time) }}</span>
              <span class="log-level">{{ item.level }}</span>
              <span class="log-msg">{{ compactLog(item.message) }}</span>
            </div>
          </div>
        </SCard>
      </section>

      <section v-show="activePage === 'shop'" class="tab-page manager-layout">
        <aside class="editor-panel"><div class="section-title"><span>商品编辑</span><small>{{ shopForm.item_key ? '编辑中' : '新建' }}</small></div><label>商品编号<SInput v-model="shopForm.item_key" placeholder="如 1 / vip_card" /></label><label>商品名称<SInput v-model="shopForm.name" placeholder="补签卡" /></label><label>积分价格<SInput v-model="shopForm.cost" placeholder="50" /></label><label>说明<SInput v-model="shopForm.desc" placeholder="展示在 /shop 内" /></label><label class="switch-line"><input v-model="shopForm.enabled" type="checkbox" /> 上架销售</label><div class="editor-actions"><SButton size="xs" color="primary" :loading="shopSaving" :disabled="shopSaving" @click="saveShopItem">保存商品</SButton><SButton size="xs" variant="outline" @click="Object.assign(shopForm, { item_key: '', name: '', cost: 0, desc: '', enabled: true })">新建</SButton></div></aside>
        <section class="data-panel"><div class="tabbar"><button :class="{active: activeTab.shop === 'items'}" @click="activeTab.shop='items'">商品列表</button><button :class="{active: activeTab.shop === 'tx'}" @click="activeTab.shop='tx'; loadShopTransactions()">交易记录</button></div><div v-if="activeTab.shop === 'items'" class="data-table"><div class="table-head"><span>编号/商品</span><span>价格</span><span>状态</span><span>操作</span></div><div v-for="item in shopItems" :key="item.item_key" class="table-row"><span><b>{{ item.item_key }}</b><em>{{ item.name }}</em><small>{{ item.desc || '无说明' }}</small></span><span>{{ item.cost }} 分</span><span><STag size="xs" :color="item.enabled ? 'success' : 'secondary'" variant="soft">{{ item.enabled ? '上架' : '下架' }}</STag></span><span class="row-actions"><SButton size="xs" variant="outline" @click="editShopItem(item)">编辑</SButton><SButton size="xs" variant="soft" color="destructive" :loading="item._loading" :disabled="item._loading" @click="deleteShopItem(item)">删除</SButton></span></div></div><div v-else class="data-table tx-table"><div class="table-head"><span>用户</span><span>商品</span><span>积分</span><span>时间</span></div><div v-for="tx in shopTransactions" :key="tx.id" class="table-row"><span>@{{ tx.username || tx.user_id }}</span><span>{{ tx.item_name }}</span><span>{{ tx.cost }} 分</span><span>{{ formatTime(tx.created_at) }}</span></div></div></section>
      </section>

      <section v-show="activePage === 'lottery'" class="tab-page manager-layout lottery-page">
        <aside class="editor-panel lottery-editor">
          <div class="section-title"><span>{{ lotteryEditorTitle }}</span><small>{{ lotteryConditionSummary }} · {{ lotteryDrawSummary }}</small></div>

          <label>抽奖标题<SInput v-model="lotteryForm.title" placeholder="如 月卡福利抽奖" /></label>
          <label>奖品内容<SInput v-model="lotteryForm.prize" placeholder="月卡一张 / 邀请码一个" /></label>
          <label>活动说明<SInput v-model="lotteryForm.description" placeholder="可选，显示在抽奖卡片中" /></label>

          <div class="form-block">
            <span class="field-label">参与条件</span>
            <div class="seg-buttons">
              <SButton v-for="option in conditionOptions" :key="option.value" size="sm" :variant="lotteryForm.condition_type === option.value ? 'solid' : 'outline'" :color="lotteryForm.condition_type === option.value ? 'primary' : 'secondary'" @click="selectLotteryCondition(option.value)">
                {{ option.label }}
              </SButton>
            </div>
            <p class="field-help">{{ conditionOptions.find(o => o.value === lotteryForm.condition_type)?.desc }}</p>
            <label v-if="lotteryForm.condition_type === 'points'" class="inline-field">最低积分<SInput v-model="lotteryForm.condition_value" type="number" placeholder="100" /></label>
          </div>

          <div class="form-block">
            <span class="field-label">开奖方式</span>
            <div class="seg-buttons">
              <SButton v-for="option in drawOptions" :key="option.value" size="sm" :variant="lotteryForm.draw_type === option.value ? 'solid' : 'outline'" :color="lotteryForm.draw_type === option.value ? 'primary' : 'secondary'" @click="selectLotteryDraw(option.value)">
                {{ option.label }}
              </SButton>
            </div>
            <p class="field-help">{{ drawOptions.find(o => o.value === lotteryForm.draw_type)?.desc }}</p>
            <label v-if="lotteryForm.draw_type === 'people'" class="inline-field">开奖人数<SInput v-model="lotteryForm.end_value" type="number" placeholder="3" /></label>
            <div v-else class="time-picker-block">
              <label class="inline-field">开奖时间<input v-model="lotteryForm.draw_at" class="form-input native-time" type="datetime-local" /></label>
              <div class="quick-times">
                <SButton size="xs" variant="outline" @click="setLotteryDrawAtMinutes(10)">10 分钟后</SButton>
                <SButton size="xs" variant="outline" @click="setLotteryDrawAtMinutes(30)">30 分钟后</SButton>
                <SButton size="xs" variant="outline" @click="setLotteryDrawAtMinutes(60)">1 小时后</SButton>
              </div>
            </div>
          </div>

          <div class="form-block">
            <span class="field-label">发布状态</span>
            <div class="seg-buttons">
              <SButton v-for="option in statusOptions" :key="option.value" size="sm" :variant="lotteryForm.status === option.value ? 'solid' : 'outline'" :color="lotteryForm.status === option.value ? 'success' : 'secondary'" @click="lotteryForm.status = option.value">
                {{ option.label }}
              </SButton>
            </div>
            <p class="field-help">新建建议直接启用；草稿仅保存后台配置。</p>
          </div>

          <div class="form-preview">
            <strong>{{ lotteryForm.title || '抽奖标题' }}</strong>
            <span>{{ lotteryForm.prize || '奖品内容' }}</span>
            <small>{{ lotteryConditionSummary }} / {{ lotteryDrawSummary }}</small>
          </div>

          <div class="form-actions">
            <SButtonLoading color="primary" :loading="lotterySaving" :disabled="lotterySaving" @click="saveLottery">{{ lotteryForm.id ? '保存修改' : '创建抽奖' }}</SButtonLoading>
            <SButton variant="outline" :disabled="lotterySaving" @click="resetLotteryForm">清空</SButton>
          </div>
        </aside>

        <SCard class="panel data-panel" title="🎁 抽奖项目" :description="`${lotteries.length} 个活动`">
          <div class="table-list lottery-list">
            <div v-for="item in lotteries" :key="item.id" class="data-row lottery-row">
              <div class="data-main">
                <div class="row-title">
                  <strong>{{ item.title || item.prize }}</strong>
                  <STag size="xs" :color="item.status === 'active' ? 'success' : item.status === 'drawn' ? 'primary' : item.status === 'cancelled' ? 'destructive' : 'secondary'" variant="soft">{{ item.status }}</STag>
                </div>
                <div class="row-desc">奖品：{{ item.prize }} · 条件：{{ item.condition_type }}{{ item.condition_value ? ':' + item.condition_value : '' }}</div>
                <div class="row-desc">开奖：{{ item.draw_type === 'time' ? formatTime(item.draw_at) : `满 ${item.max_participants || item.end_value || '-'} 人` }}</div>
              </div>
              <div class="row-actions">
                <SButton size="xs" variant="outline" @click="editLottery(item)">编辑</SButton>
                <SButton size="xs" variant="outline" @click="showParticipants(item)">参与</SButton>
                <SButton size="xs" variant="outline" color="primary" :loading="item._loading" :disabled="item._loading || item.status !== 'active'" @click="drawLottery(item)">开奖</SButton>
                <SButton size="xs" variant="outline" color="warning" :loading="item._loading" :disabled="item._loading || item.status !== 'active'" @click="cancelLottery(item)">取消</SButton>
                <SButton size="xs" variant="link" color="destructive" :loading="item._loading" :disabled="item._loading" @click="deleteLottery(item)">删除</SButton>
              </div>
            </div>
            <div v-if="!lotteries.length" class="empty">暂无抽奖项目</div>
          </div>
          <div v-if="lotteryParticipants.length" class="sub-list"><h4>参与记录</h4><div v-for="p in lotteryParticipants" :key="p.id" class="mini-row">用户 {{ p.user_id }} · {{ formatTime(p.created_at) }}</div></div>
        </SCard>
      </section>

      <section v-show="activePage === 'bans'" class="tab-page">
        <SCard class="ban-panel page-panel" title="封禁记录" :description="`${bans.length} 条`"><template #extra><SButton size="xs" variant="outline" @click="loadBans">刷新</SButton></template><div class="ban-list"><div v-if="!bans.length" class="empty">暂无封禁记录</div><div v-for="(item, i) in bans" :key="item.id || i" class="ban-item"><div class="ban-main"><b>@{{ item.username || item.first_name || item.user_id }}</b><span>{{ item.reason || '-' }}</span></div><div class="ban-meta"><span>{{ formatTime(item.time) }}</span><SButton v-if="item.auto_unban" size="xs" variant="outline" color="success" :loading="item._loading" :disabled="item._loading" @click="unban(item)">解封</SButton><STag v-else size="xs" color="secondary" variant="soft">已处理</STag></div></div></div></SCard>
      </section>

      <form v-if="activePage === 'dashboard'" class="send-box floating-chat-send" @submit.prevent="sendMessage">
        <SInput v-model="newMessage" placeholder="发送消息到 TG 群" />
        <SButton type="submit" color="primary" :loading="sending" :disabled="sending || !newMessage.trim()">发送</SButton>
      </form>

      <div v-if="toast" class="toast">{{ toast }}</div>
      <div v-if="confirmState.open" class="confirm-mask" @click.self="closeConfirm(false)">
        <div class="confirm-card">
          <h3>{{ confirmState.title }}</h3>
          <p>{{ confirmState.message }}</p>
          <div class="confirm-actions">
            <SButton size="xs" variant="outline" @click="closeConfirm(false)">取消</SButton>
            <SButton size="xs" :color="confirmState.danger ? 'destructive' : 'primary'" @click="closeConfirm(true)">{{ confirmState.confirmText }}</SButton>
          </div>
        </div>
      </div>
      </section>
    </main>
  </SConfigProvider>
</template>
