import { useEffect, useMemo, useState } from 'react';
import Taro from '@tarojs/taro';
import { Button, Input, Text, View } from '@tarojs/components';
import {
  createCommissionSettlementProof,
  exportCommissionSettlement,
  generateCommissionSettlement,
  loadActivationCodes,
  loadCodeBatches,
  loadCommissionOrders,
  loadCommissionSettlementProofs,
  loadCommissionSettings,
  loadCommissionSettlements,
  loadCommissionSummary,
  loadCouponRedemptions,
  loadCouponReport,
  loadCoupons,
  loadCrmConfig,
  loadCrmDeadLetters,
  loadCrmQueue,
  loadCrmQueueLogs,
  loadFeedbackReport,
  loadPointActivityClaims,
  loadPointActivityTasks,
  loadPointExchangeItems,
  loadPointExchangeOrders,
  loadPointsRiskReport,
  loadReferralConversionReport,
  loadTenantMembers,
  loadUserNotifications,
  updateCommissionSettings,
  updateCommissionSettlementProofStatus,
  updateCommissionSettlementStatus,
  updateCrmQueueAction,
  updateMemberCommissionRate,
  upsertCoupon,
  upsertCrmConfig,
  upsertPointActivityTask,
  upsertPointExchangeItem,
  type CodeBatchItem,
  type CommissionOrderItem,
  type CommissionSettlementProofItem,
  type CommissionSettlementItem,
  type CommissionSettingsItem,
  type CommissionSummaryItem,
  type CouponItem,
  type CouponRedemptionItem,
  type CouponReport,
  type CrmConfigItem,
  type CrmDeadLettersPayload,
  type CrmQueueLogItem,
  type CrmQueueItem,
  type FeedbackReport,
  type PointActivityTaskItem,
  type PointExchangeItem,
  type PointsRiskReport,
  type ReferralConversionReport,
  type TenantMemberItem,
  type UserNotificationAdminItem,
} from '@/services/tenantAdmin';
import '../admin.css';

function money(cents: unknown) {
  return `¥${(Number(cents || 0) / 100).toFixed(2)}`;
}

function yuanInput(cents: unknown) {
  if (cents === undefined || cents === null || cents === '') return '';
  return String((Number(cents || 0) / 100).toFixed(2)).replace(/\.00$/, '');
}

function yuanToCents(value: string) {
  const parsed = Number(value || 0);
  return Number.isFinite(parsed) ? Math.max(0, Math.round(parsed * 100)) : 0;
}

function csvValues(value: string) {
  return value.split(/[\n,，]/).map(item => item.trim()).filter(Boolean);
}

function percentLabel(value: unknown) {
  return `${(Number(value || 0) * 100).toFixed(1).replace(/\.0$/, '')}%`;
}

function today() {
  return new Date().toISOString().slice(0, 10);
}

function monthStart() {
  const now = new Date();
  return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-01`;
}

function percentToRate(value: string) {
  const numberValue = Number(value || 0);
  return Number.isFinite(numberValue) ? numberValue / 100 : 0;
}

function rateToPercent(value: unknown) {
  return String((Number(value || 0) * 100).toFixed(2)).replace(/\.00$/, '');
}

function intText(value: unknown, fallback = '') {
  if (value === undefined || value === null || value === '') return fallback;
  return String(value);
}

function metadataNumberText(metadata: Record<string, unknown> | undefined, key: string) {
  const value = metadata?.[key];
  if (value === null || value === undefined || value === '') return '';
  const parsed = Number(value);
  return Number.isFinite(parsed) ? String(Math.trunc(parsed)) : '';
}

function optionalPositiveIntText(value: string) {
  if (!value.trim()) return undefined;
  const parsed = Number(value);
  if (!Number.isFinite(parsed)) return undefined;
  return Math.max(1, Math.trunc(parsed));
}

function taskTypeLabel(type?: string | null) {
  if (type === 'daily_check_in') return '每日签到';
  if (type === 'feedback_submit') return '提交反馈';
  if (type === 'feedback_resolved') return '反馈采纳';
  if (type === 'practice_complete') return '完成练习';
  if (type === 'vocabulary_review') return '背单词';
  if (type === 'mock_exam_submit') return '提交模考';
  return '手动领取';
}

function periodLabel(type?: string | null) {
  if (type === 'daily') return '每日';
  if (type === 'weekly') return '每周';
  if (type === 'monthly') return '每月';
  if (type === 'unlimited') return '不限次数';
  return '一次性';
}

function exchangeTypeLabel(type?: string | null) {
  if (type === 'coupon') return '优惠券';
  if (type === 'asset') return '资料';
  if (type === 'custom') return '自定义';
  return '人工发放';
}

function notificationTypeLabel(type?: string | null) {
  if (type === 'feedback_status_updated') return '反馈处理';
  if (type === 'feedback_reward_granted') return '反馈奖励';
  if (type === 'badge_granted') return '勋章发放';
  if (type === 'point_exchange_completed') return '兑换完成';
  if (type === 'point_exchange_pending_fulfillment') return '待履约兑换';
  return '系统通知';
}

function notificationStatusLabel(status?: string | null) {
  if (status === 'unread') return '未读';
  if (status === 'read') return '已读';
  if (status === 'dismissed') return '已忽略';
  if (status === 'archived') return '已归档';
  return '通知';
}

function shortDate(value?: string | null) {
  if (!value) return '-';
  return value.replace('T', ' ').slice(0, 16);
}

function downloadBase64File(filename: string, contentBase64: string, mimeType: string) {
  if (typeof document === 'undefined') return false;
  const link = document.createElement('a');
  link.href = `data:${mimeType};base64,${contentBase64}`;
  link.download = filename;
  document.body.appendChild(link);
  link.click();
  link.remove();
  return true;
}

interface CouponFormState {
  id: string;
  code: string;
  status: 'active' | 'disabled' | 'archived';
  campaignName: string;
  planId: string;
  discountType: 'fixed' | 'percent';
  discountValue: string;
  minOrderYuan: string;
  maxDiscountYuan: string;
  perUserLimit: string;
  maxUses: string;
  firstOrderOnly: boolean;
  allowedPlanIdsText: string;
  allowedRegionIdsText: string;
  validFrom: string;
  validTo: string;
  remark: string;
}

function defaultCouponForm(): CouponFormState {
  return {
    id: '',
    code: '',
    status: 'active',
    campaignName: '',
    planId: '',
    discountType: 'fixed',
    discountValue: '',
    minOrderYuan: '0',
    maxDiscountYuan: '',
    perUserLimit: '1',
    maxUses: '',
    firstOrderOnly: false,
    allowedPlanIdsText: '',
    allowedRegionIdsText: '',
    validFrom: '',
    validTo: '',
    remark: '',
  };
}

interface PointTaskFormState {
  id: string;
  code: string;
  title: string;
  description: string;
  taskType: NonNullable<PointActivityTaskItem['taskType']>;
  rewardPoints: string;
  claimLimitPerUser: string;
  periodType: NonNullable<PointActivityTaskItem['periodType']>;
  status: NonNullable<PointActivityTaskItem['status']>;
  validFrom: string;
  validTo: string;
  order: string;
  requiredStreak: string;
  minStreak: string;
  maxStreak: string;
  streakMultipleOf: string;
}

function defaultPointTaskForm(): PointTaskFormState {
  return {
    id: '',
    code: '',
    title: '',
    description: '',
    taskType: 'manual',
    rewardPoints: '10',
    claimLimitPerUser: '1',
    periodType: 'once',
    status: 'active',
    validFrom: '',
    validTo: '',
    order: '0',
    requiredStreak: '',
    minStreak: '',
    maxStreak: '',
    streakMultipleOf: '',
  };
}

interface PointExchangeFormState {
  id: string;
  code: string;
  title: string;
  description: string;
  costPoints: string;
  itemType: NonNullable<PointExchangeItem['itemType']>;
  couponId: string;
  assetId: string;
  stockTotal: string;
  perUserLimit: string;
  status: NonNullable<PointExchangeItem['status']>;
  validFrom: string;
  validTo: string;
  order: string;
}

function defaultPointExchangeForm(): PointExchangeFormState {
  return {
    id: '',
    code: '',
    title: '',
    description: '',
    costPoints: '100',
    itemType: 'manual',
    couponId: '',
    assetId: '',
    stockTotal: '',
    perUserLimit: '1',
    status: 'active',
    validFrom: '',
    validTo: '',
    order: '0',
  };
}

export default function TenantMarketingPage() {
  const [coupons, setCoupons] = useState<CouponItem[]>([]);
  const [couponReport, setCouponReport] = useState<CouponReport | null>(null);
  const [couponRedemptions, setCouponRedemptions] = useState<CouponRedemptionItem[]>([]);
  const [batches, setBatches] = useState<CodeBatchItem[]>([]);
  const [codes, setCodes] = useState<Record<string, unknown>[]>([]);
  const [crmConfig, setCrmConfig] = useState<CrmConfigItem | null>(null);
  const [crmQueue, setCrmQueue] = useState<CrmQueueItem[]>([]);
  const [crmDeadLetters, setCrmDeadLetters] = useState<CrmDeadLettersPayload>({});
  const [crmLogs, setCrmLogs] = useState<Record<string, CrmQueueLogItem[]>>({});
  const [commissionSettings, setCommissionSettings] = useState<CommissionSettingsItem | null>(null);
  const [commission, setCommission] = useState<CommissionSummaryItem | null>(null);
  const [conversionReport, setConversionReport] = useState<ReferralConversionReport | null>(null);
  const [commissionOrders, setCommissionOrders] = useState<CommissionOrderItem[]>([]);
  const [settlements, setSettlements] = useState<CommissionSettlementItem[]>([]);
  const [settlementProofs, setSettlementProofs] = useState<Record<string, CommissionSettlementProofItem[]>>({});
  const [members, setMembers] = useState<TenantMemberItem[]>([]);
  const [userNotifications, setUserNotifications] = useState<UserNotificationAdminItem[]>([]);
  const [userNotificationSummary, setUserNotificationSummary] = useState<Record<string, number>>({});
  const [pointTasks, setPointTasks] = useState<PointActivityTaskItem[]>([]);
  const [pointClaims, setPointClaims] = useState<Record<string, unknown>[]>([]);
  const [pointExchangeItems, setPointExchangeItems] = useState<PointExchangeItem[]>([]);
  const [pointExchangeOrders, setPointExchangeOrders] = useState<Record<string, unknown>[]>([]);
  const [pointsRiskReport, setPointsRiskReport] = useState<PointsRiskReport | null>(null);
  const [feedbackReport, setFeedbackReport] = useState<FeedbackReport | null>(null);
  const [crmForm, setCrmForm] = useState({
    enabled: false,
    url: '',
    secretRef: '',
    formName: '刷题题库',
    examType: '专升本',
    timeoutSec: '10',
    delaySec: '60',
    assignmentMode: 'none' as 'none' | 'direct' | 'round_robin' | 'referrer',
    assignmentPoolText: '',
  });
  const [couponForm, setCouponForm] = useState<CouponFormState>(() => defaultCouponForm());
  const [pointTaskForm, setPointTaskForm] = useState<PointTaskFormState>(() => defaultPointTaskForm());
  const [pointExchangeForm, setPointExchangeForm] = useState<PointExchangeFormState>(() => defaultPointExchangeForm());
  const [couponFilter, setCouponFilter] = useState({
    status: '',
    campaignName: '',
    selectedCouponId: '',
    redemptionStatus: 'used' as 'claimed' | 'pending' | 'used' | 'cancelled' | 'expired',
    startDate: monthStart(),
    endDate: today(),
  });
  const [commissionForm, setCommissionForm] = useState({ defaultRatePercent: '20', minSettlementYuan: '0', settlementCycle: 'monthly' });
  const [period, setPeriod] = useState({ startDate: monthStart(), endDate: today(), referrerUserId: '' });
  const [memberRate, setMemberRate] = useState({ userId: '', ratePercent: '' });
  const [settlementPay, setSettlementPay] = useState({ paymentMethod: 'offline_bank', paymentAccount: '' });
  const [proofForm, setProofForm] = useState({ externalUrl: '', title: '', reviewNote: '' });
  const [userNotificationFilter, setUserNotificationFilter] = useState({
    status: 'unread' as '' | NonNullable<UserNotificationAdminItem['status']>,
    notificationType: '',
    userId: '',
  });
  const [pointFilter, setPointFilter] = useState({
    taskStatus: 'active' as '' | NonNullable<PointActivityTaskItem['status']>,
    selectedTaskId: '',
    exchangeStatus: 'active' as '' | NonNullable<PointExchangeItem['status']>,
    selectedExchangeItemId: '',
    exchangeOrderStatus: 'pending_fulfillment' as '' | 'completed' | 'pending_fulfillment' | 'cancelled',
    userId: '',
  });
  const [crmStatus, setCrmStatus] = useState('');
  const [busy, setBusy] = useState('');
  const [error, setError] = useState('');

  const salesMembers = useMemo(
    () => members.filter(item => ['sales', 'agent', 'tenant_operator', 'teacher', 'tenant_admin', 'tenant_owner'].includes(item.role) && item.status === 'active'),
    [members],
  );

  async function reloadAll() {
    setError('');
    try {
      const [
        couponPayload,
        couponReportPayload,
        couponRedemptionPayload,
        batchPayload,
        codePayload,
        crmConfigPayload,
        crmPayload,
        crmDeadLettersPayload,
        commissionSettingsPayload,
        commissionPayload,
        conversionReportPayload,
        orderPayload,
        settlementPayload,
        memberPayload,
        notificationPayload,
        pointTaskPayload,
        pointClaimPayload,
        pointExchangePayload,
        pointExchangeOrderPayload,
        pointsRiskPayload,
        feedbackReportPayload,
      ] = await Promise.all([
        loadCoupons({ status: couponFilter.status || undefined, campaignName: couponFilter.campaignName || undefined }).catch(() => ({ items: [] })),
        loadCouponReport({
          startDate: couponFilter.startDate,
          endDate: couponFilter.endDate,
          couponId: couponFilter.selectedCouponId || undefined,
          campaignName: couponFilter.campaignName || undefined,
        }).catch(() => ({ item: null })),
        loadCouponRedemptions({
          couponId: couponFilter.selectedCouponId || undefined,
          status: couponFilter.redemptionStatus,
          limit: 20,
        }).catch(() => ({ items: [] })),
        loadCodeBatches().catch(() => ({ items: [] })),
        loadActivationCodes(20).catch(() => ({ items: [] })),
        loadCrmConfig().catch(() => ({ item: null })),
        loadCrmQueue(20, crmStatus || undefined).catch(() => ({ items: [] })),
        loadCrmDeadLetters({ limit: 20 }).catch(() => ({ items: [], summary: {} })),
        loadCommissionSettings().catch(() => ({ item: null })),
        loadCommissionSummary(period).catch(() => ({ item: null })),
        loadReferralConversionReport({ ...period, limit: 20 }).catch(() => ({ item: null })),
        loadCommissionOrders({ ...period, limit: 20 }).catch(() => ({ items: [] })),
        loadCommissionSettlements({ limit: 20 }).catch(() => ({ items: [] })),
        loadTenantMembers({ limit: 100 }).catch(() => ({ items: [] })),
        loadUserNotifications({ status: userNotificationFilter.status || undefined, limit: 30 }).catch(() => ({ items: [], summary: {} })),
        loadPointActivityTasks({ status: pointFilter.taskStatus || undefined, limit: 50 }).catch(() => ({ items: [] })),
        loadPointActivityClaims({ taskId: pointFilter.selectedTaskId || undefined, userId: pointFilter.userId || undefined, limit: 30 }).catch(() => ({ items: [] })),
        loadPointExchangeItems({ status: pointFilter.exchangeStatus || undefined, limit: 50 }).catch(() => ({ items: [] })),
        loadPointExchangeOrders({
          itemId: pointFilter.selectedExchangeItemId || undefined,
          userId: pointFilter.userId || undefined,
          status: pointFilter.exchangeOrderStatus || undefined,
          limit: 30,
        }).catch(() => ({ items: [] })),
        loadPointsRiskReport({ timeRange: '30d', limit: 20 }).catch(() => ({ item: null })),
        loadFeedbackReport({ timeRange: '30d', limit: 20 }).catch(() => ({ item: null })),
      ]);
      const nextCrm = crmConfigPayload.item || null;
      const nextSettings = commissionSettingsPayload.item || null;
      setCoupons(couponPayload.items || []);
      setCouponReport(couponReportPayload.item || null);
      setCouponRedemptions(couponRedemptionPayload.items || []);
      setBatches(batchPayload.items || []);
      setCodes(codePayload.items || []);
      setCrmConfig(nextCrm);
      setCrmQueue(crmPayload.items || []);
      setCrmDeadLetters(crmDeadLettersPayload || {});
      setCommissionSettings(nextSettings);
      setCommission(commissionPayload.item || null);
      setConversionReport(conversionReportPayload.item || null);
      setCommissionOrders(orderPayload.items || []);
      setSettlements(settlementPayload.items || []);
      setMembers(memberPayload.items || []);
      setUserNotifications(notificationPayload.items || []);
      setUserNotificationSummary(notificationPayload.summary || {});
      setPointTasks(pointTaskPayload.items || []);
      setPointClaims(pointClaimPayload.items || []);
      setPointExchangeItems(pointExchangePayload.items || []);
      setPointExchangeOrders(pointExchangeOrderPayload.items || []);
      setPointsRiskReport(pointsRiskPayload.item || null);
      setFeedbackReport(feedbackReportPayload.item || null);
      if (nextCrm) {
        setCrmForm({
          enabled: nextCrm.enabled === true,
          url: nextCrm.url || '',
          secretRef: nextCrm.secretRef || '',
          formName: nextCrm.formName || '刷题题库',
          examType: nextCrm.examType || '专升本',
          timeoutSec: String(nextCrm.timeoutSec || 10),
          delaySec: String(nextCrm.delaySec || 60),
          assignmentMode: nextCrm.assignmentMode || 'none',
          assignmentPoolText: (nextCrm.assignmentPool || []).join(','),
        });
      }
      if (nextSettings) {
        setCommissionForm({
          defaultRatePercent: rateToPercent(nextSettings.defaultRate),
          minSettlementYuan: String((Number(nextSettings.minSettlementCents || 0) / 100).toFixed(2)).replace(/\.00$/, ''),
          settlementCycle: nextSettings.settlementCycle || 'monthly',
        });
      }
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '营销数据加载失败');
    }
  }

  useEffect(() => {
    void reloadAll();
  }, []);

  async function reloadCoupons(override: Partial<typeof couponFilter> = {}) {
    const nextFilter = { ...couponFilter, ...override };
    setBusy('coupons');
    setError('');
    try {
      const [couponPayload, reportPayload, redemptionPayload] = await Promise.all([
        loadCoupons({ status: nextFilter.status || undefined, campaignName: nextFilter.campaignName || undefined }),
        loadCouponReport({
          startDate: nextFilter.startDate,
          endDate: nextFilter.endDate,
          couponId: nextFilter.selectedCouponId || undefined,
          campaignName: nextFilter.campaignName || undefined,
        }).catch(() => ({ item: null })),
        loadCouponRedemptions({
          couponId: nextFilter.selectedCouponId || undefined,
          status: nextFilter.redemptionStatus,
          limit: 30,
        }).catch(() => ({ items: [] })),
      ]);
      setCoupons(couponPayload.items || []);
      setCouponReport(reportPayload.item || null);
      setCouponRedemptions(redemptionPayload.items || []);
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '优惠券数据加载失败');
    } finally {
      setBusy('');
    }
  }

  function editCoupon(item: CouponItem) {
    setCouponForm({
      id: item.id,
      code: item.code,
      status: item.status === 'disabled' || item.status === 'archived' ? item.status : 'active',
      campaignName: item.campaignName || '',
      planId: item.planId || '',
      discountType: item.discountType === 'percent' ? 'percent' : 'fixed',
      discountValue: item.discountType === 'percent' ? String(item.discountValue || '') : yuanInput(item.discountValue),
      minOrderYuan: yuanInput(item.minOrderAmountCents),
      maxDiscountYuan: yuanInput(item.maxDiscountCents),
      perUserLimit: String(item.perUserLimit || 1),
      maxUses: item.maxUses === null || item.maxUses === undefined ? '' : String(item.maxUses),
      firstOrderOnly: item.firstOrderOnly === true,
      allowedPlanIdsText: (item.allowedPlanIds || []).join(','),
      allowedRegionIdsText: (item.allowedRegionIds || []).join(','),
      validFrom: item.validFrom ? item.validFrom.slice(0, 10) : '',
      validTo: item.validTo ? item.validTo.slice(0, 10) : '',
      remark: item.remark || '',
    });
    setCouponFilter(prev => ({ ...prev, selectedCouponId: item.id, campaignName: item.campaignName || prev.campaignName }));
  }

  function editPointTask(item: PointActivityTaskItem) {
    setPointTaskForm({
      id: item.id,
      code: item.code || '',
      title: item.title || '',
      description: item.description || '',
      taskType: item.taskType || 'manual',
      rewardPoints: intText(item.rewardPoints, '10'),
      claimLimitPerUser: intText(item.claimLimitPerUser, '1'),
      periodType: item.periodType || 'once',
      status: item.status || 'active',
      validFrom: item.validFrom ? item.validFrom.slice(0, 10) : '',
      validTo: item.validTo ? item.validTo.slice(0, 10) : '',
      order: intText(item.order, '0'),
      requiredStreak: metadataNumberText(item.metadata, 'requiredStreak'),
      minStreak: metadataNumberText(item.metadata, 'minStreak'),
      maxStreak: metadataNumberText(item.metadata, 'maxStreak'),
      streakMultipleOf: metadataNumberText(item.metadata, 'streakMultipleOf'),
    });
    setPointFilter(prev => ({ ...prev, selectedTaskId: item.id }));
  }

  function editPointExchangeItem(item: PointExchangeItem) {
    setPointExchangeForm({
      id: item.id,
      code: item.code || '',
      title: item.title || '',
      description: item.description || '',
      costPoints: intText(item.costPoints, '100'),
      itemType: item.itemType || 'manual',
      couponId: item.couponId || '',
      assetId: item.assetId || '',
      stockTotal: item.stockTotal === null || item.stockTotal === undefined ? '' : String(item.stockTotal),
      perUserLimit: intText(item.perUserLimit, '1'),
      status: item.status || 'active',
      validFrom: item.validFrom ? item.validFrom.slice(0, 10) : '',
      validTo: item.validTo ? item.validTo.slice(0, 10) : '',
      order: intText(item.order, '0'),
    });
    setPointFilter(prev => ({ ...prev, selectedExchangeItemId: item.id }));
  }

  async function saveCoupon() {
    if (!couponForm.code.trim()) {
      Taro.showToast({ title: '请填写优惠券码', icon: 'none' });
      return;
    }
    setBusy('coupon-save');
    setError('');
    try {
      const discountValue = couponForm.discountType === 'fixed'
        ? yuanToCents(couponForm.discountValue)
        : Math.max(0, Number(couponForm.discountValue || 0));
      const result = await upsertCoupon({
        id: couponForm.id || undefined,
        code: couponForm.code.trim().toUpperCase(),
        status: couponForm.status,
        campaignName: couponForm.campaignName.trim() || null,
        planId: couponForm.planId.trim() || null,
        discountType: couponForm.discountType,
        discountValue,
        minOrderAmountCents: yuanToCents(couponForm.minOrderYuan),
        maxDiscountCents: couponForm.maxDiscountYuan.trim() ? yuanToCents(couponForm.maxDiscountYuan) : null,
        perUserLimit: Math.max(1, Math.min(100, Math.trunc(Number(couponForm.perUserLimit || 1)))),
        maxUses: couponForm.maxUses.trim() ? Math.max(0, Math.trunc(Number(couponForm.maxUses || 0))) : null,
        firstOrderOnly: couponForm.firstOrderOnly,
        allowedPlanIds: csvValues(couponForm.allowedPlanIdsText),
        allowedRegionIds: csvValues(couponForm.allowedRegionIdsText),
        validFrom: couponForm.validFrom || null,
        validTo: couponForm.validTo || null,
        remark: couponForm.remark.trim() || null,
        source: 'taro-tenant-admin',
        metadata: { source: 'taro-tenant-admin' },
      });
      if (result.item?.id) {
        setCouponFilter(prev => ({
          ...prev,
          selectedCouponId: result.item?.id || prev.selectedCouponId,
          campaignName: result.item?.campaignName || prev.campaignName,
        }));
      }
      Taro.showToast({ title: '优惠券已保存', icon: 'success' });
      await reloadCoupons();
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '优惠券保存失败');
    } finally {
      setBusy('');
    }
  }

  async function refreshPoints(override: Partial<typeof pointFilter> = {}) {
    const nextFilter = { ...pointFilter, ...override };
    setPointFilter(nextFilter);
    setBusy('points');
    setError('');
    try {
      const [taskPayload, claimPayload, exchangePayload, orderPayload, riskPayload, feedbackReportPayload] = await Promise.all([
        loadPointActivityTasks({ status: nextFilter.taskStatus || undefined, limit: 100 }),
        loadPointActivityClaims({
          taskId: nextFilter.selectedTaskId || undefined,
          userId: nextFilter.userId.trim() || undefined,
          limit: 50,
        }).catch(() => ({ items: [] })),
        loadPointExchangeItems({ status: nextFilter.exchangeStatus || undefined, limit: 100 }),
        loadPointExchangeOrders({
          itemId: nextFilter.selectedExchangeItemId || undefined,
          userId: nextFilter.userId.trim() || undefined,
          status: nextFilter.exchangeOrderStatus || undefined,
          limit: 50,
        }).catch(() => ({ items: [] })),
        loadPointsRiskReport({ timeRange: '30d', limit: 20 }).catch(() => ({ item: null })),
        loadFeedbackReport({ timeRange: '30d', limit: 20 }).catch(() => ({ item: null })),
      ]);
      setPointTasks(taskPayload.items || []);
      setPointClaims(claimPayload.items || []);
      setPointExchangeItems(exchangePayload.items || []);
      setPointExchangeOrders(orderPayload.items || []);
      setPointsRiskReport(riskPayload.item || null);
      setFeedbackReport(feedbackReportPayload.item || null);
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '积分运营数据加载失败');
    } finally {
      setBusy('');
    }
  }

  async function savePointTask() {
    if (!pointTaskForm.code.trim() || !pointTaskForm.title.trim()) {
      Taro.showToast({ title: '请填写任务编码和名称', icon: 'none' });
      return;
    }
    setBusy('point-task-save');
    setError('');
    try {
      const metadata: Record<string, unknown> = { source: 'taro-tenant-admin' };
      if (pointTaskForm.taskType === 'daily_check_in') {
        const requiredStreak = optionalPositiveIntText(pointTaskForm.requiredStreak);
        const minStreak = optionalPositiveIntText(pointTaskForm.minStreak);
        const maxStreak = optionalPositiveIntText(pointTaskForm.maxStreak);
        const streakMultipleOf = optionalPositiveIntText(pointTaskForm.streakMultipleOf);
        if (requiredStreak !== undefined) metadata.requiredStreak = requiredStreak;
        if (minStreak !== undefined) metadata.minStreak = minStreak;
        if (maxStreak !== undefined) metadata.maxStreak = maxStreak;
        if (streakMultipleOf !== undefined) metadata.streakMultipleOf = streakMultipleOf;
      }
      const result = await upsertPointActivityTask({
        id: pointTaskForm.id || undefined,
        code: pointTaskForm.code.trim(),
        title: pointTaskForm.title.trim(),
        description: pointTaskForm.description.trim() || null,
        taskType: pointTaskForm.taskType,
        rewardPoints: Math.max(1, Math.trunc(Number(pointTaskForm.rewardPoints || 1))),
        claimLimitPerUser: Math.max(1, Math.trunc(Number(pointTaskForm.claimLimitPerUser || 1))),
        periodType: pointTaskForm.periodType,
        status: pointTaskForm.status,
        validFrom: pointTaskForm.validFrom || null,
        validTo: pointTaskForm.validTo || null,
        order: Math.trunc(Number(pointTaskForm.order || 0)),
        metadata,
      });
      Taro.showToast({ title: '积分任务已保存', icon: 'success' });
      if (result.item?.id) setPointFilter(prev => ({ ...prev, selectedTaskId: result.item?.id || prev.selectedTaskId }));
      await refreshPoints({ selectedTaskId: result.item?.id || pointFilter.selectedTaskId });
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '积分任务保存失败');
    } finally {
      setBusy('');
    }
  }

  async function savePointExchange() {
    if (!pointExchangeForm.code.trim() || !pointExchangeForm.title.trim()) {
      Taro.showToast({ title: '请填写兑换编码和名称', icon: 'none' });
      return;
    }
    setBusy('point-exchange-save');
    setError('');
    try {
      const result = await upsertPointExchangeItem({
        id: pointExchangeForm.id || undefined,
        code: pointExchangeForm.code.trim(),
        title: pointExchangeForm.title.trim(),
        description: pointExchangeForm.description.trim() || null,
        costPoints: Math.max(1, Math.trunc(Number(pointExchangeForm.costPoints || 1))),
        itemType: pointExchangeForm.itemType,
        couponId: pointExchangeForm.couponId.trim() || null,
        assetId: pointExchangeForm.assetId.trim() || null,
        stockTotal: pointExchangeForm.stockTotal.trim() ? Math.max(0, Math.trunc(Number(pointExchangeForm.stockTotal || 0))) : null,
        perUserLimit: Math.max(1, Math.trunc(Number(pointExchangeForm.perUserLimit || 1))),
        status: pointExchangeForm.status,
        validFrom: pointExchangeForm.validFrom || null,
        validTo: pointExchangeForm.validTo || null,
        order: Math.trunc(Number(pointExchangeForm.order || 0)),
        metadata: { source: 'taro-tenant-admin' },
      });
      Taro.showToast({ title: '兑换商品已保存', icon: 'success' });
      if (result.item?.id) setPointFilter(prev => ({ ...prev, selectedExchangeItemId: result.item?.id || prev.selectedExchangeItemId }));
      await refreshPoints({ selectedExchangeItemId: result.item?.id || pointFilter.selectedExchangeItemId });
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '兑换商品保存失败');
    } finally {
      setBusy('');
    }
  }

  async function saveCrmConfig() {
    setBusy('crm');
    setError('');
    try {
      const result = await upsertCrmConfig({
        enabled: crmForm.enabled,
        url: crmForm.url.trim() || null,
        secretRef: crmForm.secretRef.trim() || null,
        formName: crmForm.formName.trim() || '刷题题库',
        examType: crmForm.examType.trim() || '专升本',
        timeoutSec: Math.max(1, Math.trunc(Number(crmForm.timeoutSec || 10))),
        delaySec: Math.max(0, Math.trunc(Number(crmForm.delaySec || 60))),
        assignmentMode: crmForm.assignmentMode,
        assignmentPool: crmForm.assignmentPoolText.split(',').map(item => item.trim()).filter(Boolean),
      });
      setCrmConfig(result.item || null);
      Taro.showToast({ title: 'CRM 已保存', icon: 'success' });
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : 'CRM 保存失败');
    } finally {
      setBusy('');
    }
  }

  async function refreshCrmQueue(status = crmStatus) {
    setBusy('crm-queue');
    setError('');
    try {
      const [payload, deadLettersPayload] = await Promise.all([
        loadCrmQueue(30, status || undefined),
        loadCrmDeadLetters({ limit: 20 }),
      ]);
      setCrmQueue(payload.items || []);
      setCrmDeadLetters(deadLettersPayload || {});
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : 'CRM 队列加载失败');
    } finally {
      setBusy('');
    }
  }

  async function showCrmQueueLogs(item: CrmQueueItem) {
    setBusy(`crm-log:${item.id}`);
    setError('');
    try {
      const payload = await loadCrmQueueLogs(item.id, 20);
      setCrmLogs(prev => ({ ...prev, [item.id]: payload.items || [] }));
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : 'CRM 日志加载失败');
    } finally {
      setBusy('');
    }
  }

  async function handleCrmQueueAction(item: CrmQueueItem, action: 'retry' | 'ignore') {
    const confirmed = await Taro.showModal({
      title: action === 'retry' ? '重试 CRM 任务' : '忽略 CRM 任务',
      content: `${item.source || 'CRM'} · ${item.status || 'pending'} · 尝试 ${item.attempts || 0} 次`,
      confirmText: action === 'retry' ? '重试' : '忽略',
      cancelText: '取消',
    });
    if (!confirmed.confirm) return;
    setBusy(`crm-action:${item.id}:${action}`);
    setError('');
    try {
      await updateCrmQueueAction({
        queueId: item.id,
        action,
        note: action === 'retry' ? '租户后台手动重试' : '租户后台确认忽略',
        metadata: { source: 'taro-tenant-admin' },
      });
      Taro.showToast({ title: action === 'retry' ? '已重新入队' : '已忽略', icon: 'success' });
      await refreshCrmQueue(crmStatus);
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : 'CRM 队列操作失败');
    } finally {
      setBusy('');
    }
  }

  async function refreshUserNotifications(override: Partial<typeof userNotificationFilter> = {}) {
    const nextFilter = { ...userNotificationFilter, ...override };
    setUserNotificationFilter(nextFilter);
    setBusy('user-notifications');
    setError('');
    try {
      const payload = await loadUserNotifications({
        status: nextFilter.status || undefined,
        notificationType: nextFilter.notificationType || undefined,
        userId: nextFilter.userId.trim() || undefined,
        limit: 50,
      });
      setUserNotifications(payload.items || []);
      setUserNotificationSummary(payload.summary || {});
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '用户通知加载失败');
    } finally {
      setBusy('');
    }
  }

  async function saveCommissionSettings() {
    setBusy('commission-settings');
    setError('');
    try {
      const result = await updateCommissionSettings({
        defaultRate: percentToRate(commissionForm.defaultRatePercent),
        minSettlementCents: Math.max(0, Math.round(Number(commissionForm.minSettlementYuan || 0) * 100)),
        settlementCycle: commissionForm.settlementCycle,
        config: { source: 'taro-tenant-admin' },
      });
      setCommissionSettings(result.item || null);
      Taro.showToast({ title: '分佣规则已保存', icon: 'success' });
      await reloadCommission();
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '分佣规则保存失败');
    } finally {
      setBusy('');
    }
  }

  async function saveMemberRate() {
    if (!memberRate.userId) {
      Taro.showToast({ title: '请选择成员', icon: 'none' });
      return;
    }
    setBusy('member-rate');
    setError('');
    try {
      await updateMemberCommissionRate({
        userId: memberRate.userId,
        commissionRate: memberRate.ratePercent.trim() ? percentToRate(memberRate.ratePercent) : null,
        commissionConfig: { source: 'taro-tenant-admin' },
      });
      Taro.showToast({ title: '成员比例已保存', icon: 'success' });
      await reloadCommission();
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '成员比例保存失败');
    } finally {
      setBusy('');
    }
  }

  async function reloadCommission() {
    setBusy('commission');
    setError('');
    try {
      const [summaryPayload, conversionPayload, orderPayload, settlementPayload] = await Promise.all([
        loadCommissionSummary(period),
        loadReferralConversionReport({ ...period, limit: 30 }),
        loadCommissionOrders({ ...period, limit: 30 }),
        loadCommissionSettlements({ limit: 30 }),
      ]);
      setCommission(summaryPayload.item || null);
      setConversionReport(conversionPayload.item || null);
      setCommissionOrders(orderPayload.items || []);
      setSettlements(settlementPayload.items || []);
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '分佣数据加载失败');
    } finally {
      setBusy('');
    }
  }

  async function generateSettlement() {
    if (!period.referrerUserId) {
      Taro.showToast({ title: '请选择销售/代理成员', icon: 'none' });
      return;
    }
    setBusy('settlement-generate');
    setError('');
    try {
      await generateCommissionSettlement({
        referrerUserId: period.referrerUserId,
        startDate: period.startDate,
        endDate: period.endDate,
        status: 'pending_review',
        remark: 'Taro 管理台生成',
      });
      Taro.showToast({ title: '结算单已生成', icon: 'success' });
      await reloadCommission();
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '结算单生成失败');
    } finally {
      setBusy('');
    }
  }

  async function updateSettlement(item: CommissionSettlementItem, status: string) {
    const confirmed = await Taro.showModal({
      title: status === 'paid' ? '确认已打款' : '更新结算状态',
      content: `${item.referrerName || item.referrerPhone || item.referrerUserId || '成员'} · ${money(item.commissionAmountCents)}`,
      confirmText: status === 'paid' ? '已打款' : '确认',
      cancelText: '取消',
    });
    if (!confirmed.confirm) return;
    setBusy(`settlement:${item.id}:${status}`);
    setError('');
    try {
      await updateCommissionSettlementStatus({
        settlementId: item.id,
        status,
        paymentMethod: status === 'paid' ? settlementPay.paymentMethod : undefined,
        paymentAccount: status === 'paid' ? settlementPay.paymentAccount || null : undefined,
        remark: `Taro 管理台标记 ${status}`,
        metadata: { source: 'taro-tenant-admin' },
      });
      Taro.showToast({ title: '状态已更新', icon: 'success' });
      await reloadCommission();
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '状态更新失败');
    } finally {
      setBusy('');
    }
  }

  async function exportSettlement(item: CommissionSettlementItem) {
    setBusy(`settlement:${item.id}:export`);
    setError('');
    try {
      const payload = await exportCommissionSettlement(item.id, 'csv');
      const exportItem = payload.item;
      if (exportItem?.contentBase64 && exportItem.filename) {
        const downloaded = downloadBase64File(exportItem.filename, exportItem.contentBase64, exportItem.mimeType || 'text/csv');
        Taro.showToast({ title: downloaded ? '导出已下载' : '导出已生成', icon: 'success' });
      }
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '结算导出失败');
    } finally {
      setBusy('');
    }
  }

  async function submitProof(item: CommissionSettlementItem) {
    if (!proofForm.externalUrl.trim()) {
      Taro.showToast({ title: '请填写凭证链接', icon: 'none' });
      return;
    }
    setBusy(`settlement:${item.id}:proof`);
    setError('');
    try {
      await createCommissionSettlementProof({
        settlementId: item.id,
        proofType: 'payment',
        title: proofForm.title.trim() || `${item.settlementNo || item.id} 打款凭证`,
        externalUrl: proofForm.externalUrl.trim(),
        amountCents: item.commissionAmountCents || null,
        paymentMethod: settlementPay.paymentMethod || null,
        paymentAccount: settlementPay.paymentAccount || null,
        metadata: { source: 'taro-tenant-admin' },
      });
      Taro.showToast({ title: '凭证已登记', icon: 'success' });
      await refreshProofs(item.id);
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '凭证登记失败');
    } finally {
      setBusy('');
    }
  }

  async function refreshProofs(settlementId: string) {
    setBusy(`settlement:${settlementId}:proofs`);
    setError('');
    try {
      const payload = await loadCommissionSettlementProofs(settlementId);
      setSettlementProofs(prev => ({ ...prev, [settlementId]: payload.items || [] }));
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '凭证加载失败');
    } finally {
      setBusy('');
    }
  }

  async function reviewProof(proof: CommissionSettlementProofItem, status: string) {
    setBusy(`proof:${proof.id}:${status}`);
    setError('');
    try {
      await updateCommissionSettlementProofStatus({
        proofId: proof.id,
        status,
        reviewNote: proofForm.reviewNote.trim() || null,
        metadata: { source: 'taro-tenant-admin' },
      });
      Taro.showToast({ title: '凭证状态已更新', icon: 'success' });
      if (proof.settlementId) await refreshProofs(proof.settlementId);
    } catch (nextError) {
      setError(nextError instanceof Error ? nextError.message : '凭证复核失败');
    } finally {
      setBusy('');
    }
  }

  return (
    <View className='admin-page'>
      <View className='admin-shell'>
        <View className='admin-header'>
          <Text className='admin-kicker'>Marketing</Text>
          <Text className='admin-title'>营销中心</Text>
          <Text className='admin-subtitle'>套餐优惠、激活码、CRM 队列和销售/代理分佣。</Text>
        </View>

        <View className='admin-grid'>
          <View className='admin-metric'><Text className='admin-metric-label'>优惠券</Text><Text className='admin-metric-value'>{String(coupons.length)}</Text></View>
          <View className='admin-metric'><Text className='admin-metric-label'>激活码批次</Text><Text className='admin-metric-value'>{String(batches.length)}</Text></View>
          <View className='admin-metric'><Text className='admin-metric-label'>CRM 队列</Text><Text className='admin-metric-value'>{String(crmQueue.length)}</Text></View>
          <View className='admin-metric'><Text className='admin-metric-label'>预计佣金</Text><Text className='admin-metric-value'>{money(commission?.commissionAmountCents)}</Text></View>
        </View>

        <View className='admin-section'>
          <Text className='admin-section-title'>反馈运营摘要</Text>
          <View className='admin-grid'>
            <View className='admin-metric'><Text className='admin-metric-label'>30 天反馈</Text><Text className='admin-metric-value'>{String(feedbackReport?.summary?.total || 0)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>待处理积压</Text><Text className='admin-metric-value'>{String(feedbackReport?.summary?.pendingBacklog || 0)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>处理率</Text><Text className='admin-metric-value'>{percentLabel(feedbackReport?.summary?.handledRate)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>奖励积分</Text><Text className='admin-metric-value'>{String(feedbackReport?.summary?.rewardPoints || 0)}</Text></View>
          </View>
          <View className='admin-grid'>
            <View className='admin-metric'><Text className='admin-metric-label'>高优先级</Text><Text className='admin-metric-value'>{String(feedbackReport?.summary?.highPriority || 0)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>已解决</Text><Text className='admin-metric-value'>{String((feedbackReport?.summary?.resolved || 0) + (feedbackReport?.summary?.closed || 0))}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>平均处理</Text><Text className='admin-metric-value'>{String(feedbackReport?.summary?.avgHandleHours || 0)}h</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>采纳率</Text><Text className='admin-metric-value'>{percentLabel(feedbackReport?.summary?.resolutionRate)}</Text></View>
          </View>
          <View className='admin-list'>
            {(feedbackReport?.recentUnhandled || []).slice(0, 4).map((item, index) => (
              <View className='admin-row' key={String(item.id || index)}>
                <Text className='admin-row-main'>{String(item.title || item.type || '待处理反馈')} · {String(item.priority || 'normal')}</Text>
                <Text className='admin-row-meta'>{String(item.userName || item.userPhone || item.userId || '学生')} · {String(item.status || '-')} · 已等待 {String(item.ageHours || 0)}h</Text>
              </View>
            ))}
            {(feedbackReport?.topQuestions || []).slice(0, 4).map((item, index) => (
              <View className='admin-row' key={String(item.questionId || index)}>
                <Text className='admin-row-main'>{String(item.questionPreview || item.questionId || '高频反馈题目')}</Text>
                <Text className='admin-row-meta'>{String(item.questionType || '题目')} · 反馈 {String(item.reportCount || 0)} 次 · 高优先级 {String(item.highPriority || 0)}</Text>
              </View>
            ))}
          </View>
          {!(feedbackReport?.recentUnhandled?.length || feedbackReport?.topQuestions?.length) ? (
            <View className='admin-empty'>当前 30 天暂无反馈运营风险项，或当前角色没有 `feedback:read` 权限。</View>
          ) : null}
        </View>

        <View className='admin-section'>
          <Text className='admin-section-title'>积分任务与兑换</Text>
          <View className='admin-grid'>
            <View className='admin-metric'><Text className='admin-metric-label'>任务数</Text><Text className='admin-metric-value'>{String(pointTasks.length)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>领取记录</Text><Text className='admin-metric-value'>{String(pointClaims.length)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>兑换商品</Text><Text className='admin-metric-value'>{String(pointExchangeItems.length)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>兑换订单</Text><Text className='admin-metric-value'>{String(pointExchangeOrders.length)}</Text></View>
          </View>
          <View className='admin-grid'>
            <View className='admin-metric'><Text className='admin-metric-label'>30 天风险分</Text><Text className='admin-metric-value'>{String(pointsRiskReport?.summary?.riskScore || 0)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>异常用户</Text><Text className='admin-metric-value'>{String(pointsRiskReport?.summary?.suspiciousUserCount || 0)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>异常事件</Text><Text className='admin-metric-value'>{String(pointsRiskReport?.summary?.suspiciousEventCount || 0)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>净积分</Text><Text className='admin-metric-value'>{String(pointsRiskReport?.summary?.netPoints || 0)}</Text></View>
          </View>
          <View className='admin-list'>
            {(pointsRiskReport?.suspiciousUsers || []).slice(0, 4).map((item, index) => (
              <View className='admin-row' key={String(item.userId || index)}>
                <Text className='admin-row-main'>{String(item.userName || item.userPhone || item.userId || '异常用户')} · 获得 {String(item.earnedPoints || 0)} 积分</Text>
                <Text className='admin-row-meta'>领取 {String(item.claimCount || 0)} 次 · 兑换 {String(item.exchangePoints || 0)} 积分 · 标记 {Array.isArray(item.riskFlags) ? item.riskFlags.join(', ') : '-'}</Text>
              </View>
            ))}
            {(pointsRiskReport?.suspiciousEvents || []).slice(0, 3).map((item, index) => (
              <View className='admin-row' key={String(item.id || index)}>
                <Text className='admin-row-main'>{String(item.eventType || '积分事件')} · {Number(item.points || 0) > 0 ? '+' : ''}{String(item.points || 0)} 积分</Text>
                <Text className='admin-row-meta'>{String(item.userName || item.userPhone || item.userId || '学生')} · 来源 {String(item.sourceType || '-')} · {shortDate(String(item.createdAt || ''))}</Text>
              </View>
            ))}
            {(pointsRiskReport?.suspiciousTasks || []).slice(0, 3).map((item, index) => (
              <View className='admin-row' key={String(item.taskId || index)}>
                <Text className='admin-row-main'>{String(item.title || item.taskCode || '积分任务')} · 发放 {String(item.issuedPoints || 0)} 积分</Text>
                <Text className='admin-row-meta'>{taskTypeLabel(String(item.taskType || ''))} · 领取 {String(item.claimCount || 0)} 次 / {String(item.claimUserCount || 0)} 人</Text>
              </View>
            ))}
          </View>
          {!(pointsRiskReport?.suspiciousUsers?.length || pointsRiskReport?.suspiciousEvents?.length || pointsRiskReport?.suspiciousTasks?.length) ? (
            <View className='admin-empty'>当前 30 天暂无命中阈值的积分风控项。</View>
          ) : null}
          <View className='admin-form-grid'>
            <Input
              className='admin-input'
              placeholder='按学生用户 ID 查看领取/兑换记录，可留空'
              value={pointFilter.userId}
              onInput={event => setPointFilter(prev => ({ ...prev, userId: String(event.detail.value || '') }))}
            />
          </View>
          <View className='admin-actions compact'>
            {([
              ['', '全部任务'],
              ['active', '启用任务'],
              ['disabled', '停用任务'],
              ['archived', '归档任务'],
            ] as Array<[typeof pointFilter.taskStatus, string]>).map(([status, label]) => (
              <Button key={status || 'all-tasks'} className={`admin-button ${pointFilter.taskStatus === status ? 'active' : ''}`} onClick={() => void refreshPoints({ taskStatus: status })}>{label}</Button>
            ))}
            {([
              ['', '全部商品'],
              ['active', '启用商品'],
              ['disabled', '停用商品'],
              ['archived', '归档商品'],
            ] as Array<[typeof pointFilter.exchangeStatus, string]>).map(([status, label]) => (
              <Button key={status || 'all-exchange-items'} className={`admin-button ${pointFilter.exchangeStatus === status ? 'active' : ''}`} onClick={() => void refreshPoints({ exchangeStatus: status })}>{label}</Button>
            ))}
            <Button className='admin-button primary' loading={busy === 'points'} onClick={() => void refreshPoints()}>刷新积分运营</Button>
          </View>

          <View className='admin-form-grid'>
            <Input className='admin-input' placeholder='任务编码，例如 daily-share' value={pointTaskForm.code} onInput={event => setPointTaskForm(prev => ({ ...prev, code: String(event.detail.value || '') }))} />
            <Input className='admin-input' placeholder='任务名称' value={pointTaskForm.title} onInput={event => setPointTaskForm(prev => ({ ...prev, title: String(event.detail.value || '') }))} />
            <Input className='admin-input' placeholder='任务说明' value={pointTaskForm.description} onInput={event => setPointTaskForm(prev => ({ ...prev, description: String(event.detail.value || '') }))} />
            <Input className='admin-input' type='number' placeholder='奖励积分' value={pointTaskForm.rewardPoints} onInput={event => setPointTaskForm(prev => ({ ...prev, rewardPoints: String(event.detail.value || '') }))} />
            <Input className='admin-input' type='number' placeholder='单用户领取上限' value={pointTaskForm.claimLimitPerUser} onInput={event => setPointTaskForm(prev => ({ ...prev, claimLimitPerUser: String(event.detail.value || '') }))} />
          </View>
          {pointTaskForm.taskType === 'daily_check_in' ? (
            <View className='admin-form-grid'>
              <Input className='admin-input' type='number' placeholder='仅第 N 天奖励' value={pointTaskForm.requiredStreak} onInput={event => setPointTaskForm(prev => ({ ...prev, requiredStreak: String(event.detail.value || '') }))} />
              <Input className='admin-input' type='number' placeholder='最小连续天数' value={pointTaskForm.minStreak} onInput={event => setPointTaskForm(prev => ({ ...prev, minStreak: String(event.detail.value || '') }))} />
              <Input className='admin-input' type='number' placeholder='最大连续天数' value={pointTaskForm.maxStreak} onInput={event => setPointTaskForm(prev => ({ ...prev, maxStreak: String(event.detail.value || '') }))} />
              <Input className='admin-input' type='number' placeholder='每 N 天循环奖励' value={pointTaskForm.streakMultipleOf} onInput={event => setPointTaskForm(prev => ({ ...prev, streakMultipleOf: String(event.detail.value || '') }))} />
            </View>
          ) : null}
          <View className='admin-actions compact'>
            {(['manual', 'practice_complete', 'vocabulary_review', 'mock_exam_submit', 'daily_check_in', 'feedback_submit', 'feedback_resolved'] as const).map(type => (
              <Button key={type} className={`admin-button ${pointTaskForm.taskType === type ? 'active' : ''}`} onClick={() => setPointTaskForm(prev => ({ ...prev, taskType: type }))}>{taskTypeLabel(type)}</Button>
            ))}
          </View>
          <View className='admin-actions compact'>
            {(['once', 'daily', 'weekly', 'monthly', 'unlimited'] as const).map(type => (
              <Button key={type} className={`admin-button ${pointTaskForm.periodType === type ? 'active' : ''}`} onClick={() => setPointTaskForm(prev => ({ ...prev, periodType: type }))}>{periodLabel(type)}</Button>
            ))}
            {(['active', 'disabled', 'archived'] as const).map(status => (
              <Button key={status} className={`admin-button ${pointTaskForm.status === status ? 'active' : ''}`} onClick={() => setPointTaskForm(prev => ({ ...prev, status }))}>{status}</Button>
            ))}
            <Button className='admin-button primary' loading={busy === 'point-task-save'} onClick={savePointTask}>保存积分任务</Button>
            <Button className='admin-button' onClick={() => setPointTaskForm(defaultPointTaskForm())}>新建任务</Button>
          </View>

          <View className='admin-list'>
            {pointTasks.slice(0, 8).map(item => (
              <View className={`admin-row ${pointFilter.selectedTaskId === item.id ? 'active' : ''}`} key={item.id}>
                <Text className='admin-row-main'>{item.title || item.code} · {String(item.rewardPoints || 0)} 积分</Text>
                <Text className='admin-row-meta'>{taskTypeLabel(item.taskType)} · {periodLabel(item.periodType)} · {item.status || 'active'} · 领取 {String(item.claimCount || 0)} 次 / {String(item.claimUserCount || 0)} 人</Text>
                {item.description ? <Text className='admin-row-meta'>{item.description}</Text> : null}
                <View className='admin-row-actions'>
                  <Button className='admin-mini-button primary' onClick={() => editPointTask(item)}>编辑任务</Button>
                  <Button className='admin-mini-button' onClick={() => void refreshPoints({ selectedTaskId: item.id })}>查看领取</Button>
                </View>
              </View>
            ))}
          </View>
          {!pointTasks.length ? <View className='admin-empty'>暂无积分任务，或当前角色没有 `marketing:points:read` 权限。</View> : null}

          <View className='admin-form-grid'>
            <Input className='admin-input' placeholder='兑换编码，例如 coupon-100' value={pointExchangeForm.code} onInput={event => setPointExchangeForm(prev => ({ ...prev, code: String(event.detail.value || '') }))} />
            <Input className='admin-input' placeholder='兑换名称' value={pointExchangeForm.title} onInput={event => setPointExchangeForm(prev => ({ ...prev, title: String(event.detail.value || '') }))} />
            <Input className='admin-input' placeholder='兑换说明' value={pointExchangeForm.description} onInput={event => setPointExchangeForm(prev => ({ ...prev, description: String(event.detail.value || '') }))} />
            <Input className='admin-input' type='number' placeholder='消耗积分' value={pointExchangeForm.costPoints} onInput={event => setPointExchangeForm(prev => ({ ...prev, costPoints: String(event.detail.value || '') }))} />
            <Input className='admin-input' placeholder='优惠券 ID，类型为优惠券时必填' value={pointExchangeForm.couponId} onInput={event => setPointExchangeForm(prev => ({ ...prev, couponId: String(event.detail.value || '') }))} />
            <Input className='admin-input' placeholder='资源 ID，类型为资料时必填' value={pointExchangeForm.assetId} onInput={event => setPointExchangeForm(prev => ({ ...prev, assetId: String(event.detail.value || '') }))} />
            <Input className='admin-input' type='number' placeholder='总库存，留空不限' value={pointExchangeForm.stockTotal} onInput={event => setPointExchangeForm(prev => ({ ...prev, stockTotal: String(event.detail.value || '') }))} />
            <Input className='admin-input' type='number' placeholder='单用户限购次数' value={pointExchangeForm.perUserLimit} onInput={event => setPointExchangeForm(prev => ({ ...prev, perUserLimit: String(event.detail.value || '') }))} />
          </View>
          <View className='admin-actions compact'>
            {(['manual', 'coupon', 'asset', 'custom'] as const).map(type => (
              <Button key={type} className={`admin-button ${pointExchangeForm.itemType === type ? 'active' : ''}`} onClick={() => setPointExchangeForm(prev => ({ ...prev, itemType: type }))}>{exchangeTypeLabel(type)}</Button>
            ))}
            {(['active', 'disabled', 'archived'] as const).map(status => (
              <Button key={status} className={`admin-button ${pointExchangeForm.status === status ? 'active' : ''}`} onClick={() => setPointExchangeForm(prev => ({ ...prev, status }))}>{status}</Button>
            ))}
            <Button className='admin-button primary' loading={busy === 'point-exchange-save'} onClick={savePointExchange}>保存兑换商品</Button>
            <Button className='admin-button' onClick={() => setPointExchangeForm(defaultPointExchangeForm())}>新建商品</Button>
          </View>

          <View className='admin-list'>
            {pointExchangeItems.slice(0, 8).map(item => (
              <View className={`admin-row ${pointFilter.selectedExchangeItemId === item.id ? 'active' : ''}`} key={item.id}>
                <Text className='admin-row-main'>{item.title || item.code} · {String(item.costPoints || 0)} 积分</Text>
                <Text className='admin-row-meta'>{exchangeTypeLabel(item.itemType)} · {item.status || 'active'} · 库存 {item.stockRemaining === null || item.stockRemaining === undefined ? '不限' : String(item.stockRemaining)} · 已兑 {String(item.orderCount || 0)}</Text>
                {item.description ? <Text className='admin-row-meta'>{item.description}</Text> : null}
                <View className='admin-row-actions'>
                  <Button className='admin-mini-button primary' onClick={() => editPointExchangeItem(item)}>编辑商品</Button>
                  <Button className='admin-mini-button' onClick={() => void refreshPoints({ selectedExchangeItemId: item.id })}>查看兑换</Button>
                </View>
              </View>
            ))}
          </View>
          {!pointExchangeItems.length ? <View className='admin-empty'>暂无兑换商品，或当前角色没有 `marketing:points:read` 权限。</View> : null}

          <View className='admin-actions compact'>
            {(['', 'completed', 'pending_fulfillment', 'cancelled'] as const).map(status => (
              <Button key={status || 'all-orders'} className={`admin-button ${pointFilter.exchangeOrderStatus === status ? 'active' : ''}`} onClick={() => void refreshPoints({ exchangeOrderStatus: status })}>{status || '全部兑换单'}</Button>
            ))}
          </View>
          <View className='admin-list'>
            {pointExchangeOrders.slice(0, 10).map((item, index) => (
              <View className='admin-row' key={String(item.id || index)}>
                <Text className='admin-row-main'>{String(item.itemTitle || item.itemCode || '兑换订单')} · {String(item.status || '-')}</Text>
                <Text className='admin-row-meta'>{String(item.userName || item.userPhone || item.userId || '学生')} · 消耗 {String(item.costPoints || 0)} 积分 · {shortDate(String(item.exchangedAt || item.createdAt || ''))}</Text>
                <Text className='admin-row-meta break-line'>优惠券 {String(item.couponCode || item.couponRedemptionId || '-')} · 资源 {String(item.assetId || '-')}</Text>
              </View>
            ))}
          </View>
          {!pointExchangeOrders.length ? <View className='admin-empty'>当前筛选下暂无兑换订单。</View> : null}

          <View className='admin-list'>
            {pointClaims.slice(0, 8).map((item, index) => (
              <View className='admin-row' key={String(item.id || index)}>
                <Text className='admin-row-main'>{String(item.taskTitle || item.taskCode || '积分任务')} · {String(item.rewardPoints || 0)} 积分</Text>
                <Text className='admin-row-meta'>{String(item.userName || item.userPhone || item.userId || '学生')} · {String(item.periodKey || '-')} · {shortDate(String(item.claimedAt || item.createdAt || ''))}</Text>
                <Text className='admin-row-meta break-line'>来源 {String(item.sourceType || '-')} · {String(item.sourceId || '-')}</Text>
              </View>
            ))}
          </View>
          {!pointClaims.length ? <View className='admin-empty'>当前筛选下暂无任务领取记录。</View> : null}
        </View>

        <View className='admin-section'>
          <Text className='admin-section-title'>用户通知</Text>
          <View className='admin-grid'>
            <View className='admin-metric'><Text className='admin-metric-label'>未读</Text><Text className='admin-metric-value'>{String(userNotificationSummary.unread || 0)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>已归档</Text><Text className='admin-metric-value'>{String(userNotificationSummary.archived || 0)}</Text></View>
          </View>
          <View className='admin-form-grid'>
            <Input
              className='admin-input'
              placeholder='按学生用户 ID 筛选，可留空'
              value={userNotificationFilter.userId}
              onInput={event => setUserNotificationFilter(prev => ({ ...prev, userId: String(event.detail.value || '') }))}
            />
          </View>
          <View className='admin-actions compact'>
            {([
              ['', '全部状态'],
              ['unread', '未读'],
              ['read', '已读'],
              ['dismissed', '已忽略'],
              ['archived', '已归档'],
            ] as Array<[typeof userNotificationFilter.status, string]>).map(([status, label]) => (
              <Button
                key={status || 'all'}
                className={`admin-button ${userNotificationFilter.status === status ? 'active' : ''}`}
                onClick={() => void refreshUserNotifications({ status })}
              >
                {label}
              </Button>
            ))}
          </View>
          <View className='admin-actions compact'>
            {([
              ['', '全部类型'],
              ['feedback_status_updated', '反馈处理'],
              ['feedback_reward_granted', '反馈奖励'],
              ['badge_granted', '勋章发放'],
              ['point_exchange_completed', '兑换完成'],
              ['point_exchange_pending_fulfillment', '待履约兑换'],
            ] as Array<[string, string]>).map(([type, label]) => (
              <Button
                key={type || 'all-types'}
                className={`admin-button ${userNotificationFilter.notificationType === type ? 'active' : ''}`}
                onClick={() => void refreshUserNotifications({ notificationType: type })}
              >
                {label}
              </Button>
            ))}
            <Button className='admin-button primary' loading={busy === 'user-notifications'} onClick={() => void refreshUserNotifications()}>刷新通知</Button>
          </View>
          <View className='admin-list'>
            {userNotifications.map(item => (
              <View className={`admin-row ${item.status === 'unread' ? 'active' : ''}`} key={item.id}>
                <Text className='admin-row-main'>{item.title || notificationTypeLabel(item.notificationType)} · {notificationStatusLabel(item.status)}</Text>
                <Text className='admin-row-meta'>{item.userName || item.userPhone || item.userId || '学生'} · {notificationTypeLabel(item.notificationType)} · {shortDate(item.createdAt)}</Text>
                <Text className='admin-row-meta'>{item.message || '暂无消息内容'}</Text>
                <Text className='admin-row-meta break-line'>来源 {item.sourceType || '-'} · {item.sourceId || '-'}{item.createdByName ? ` · 操作人 ${item.createdByName}` : ''}</Text>
              </View>
            ))}
          </View>
          {!userNotifications.length ? <View className='admin-empty'>暂无用户通知，或当前角色没有 `notifications:read` 权限。</View> : null}
        </View>

        <View className='admin-section'>
          <Text className='admin-section-title'>优惠券规则</Text>
          <View className='admin-form-grid'>
            <Input className='admin-input' placeholder='优惠券码，例如 SUMMER80' value={couponForm.code} onInput={event => setCouponForm(prev => ({ ...prev, code: String(event.detail.value || '').toUpperCase() }))} />
            <Input className='admin-input' placeholder='活动分组，例如 暑期活动' value={couponForm.campaignName} onInput={event => setCouponForm(prev => ({ ...prev, campaignName: String(event.detail.value || '') }))} />
            <Input className='admin-input' placeholder='默认套餐 ID，可留空' value={couponForm.planId} onInput={event => setCouponForm(prev => ({ ...prev, planId: String(event.detail.value || '') }))} />
            <Input className='admin-input' type='digit' placeholder={couponForm.discountType === 'fixed' ? '优惠金额，单位元' : '优惠百分比，例如 80 表示 80%'} value={couponForm.discountValue} onInput={event => setCouponForm(prev => ({ ...prev, discountValue: String(event.detail.value || '') }))} />
            <Input className='admin-input' type='digit' placeholder='最低订单金额，单位元' value={couponForm.minOrderYuan} onInput={event => setCouponForm(prev => ({ ...prev, minOrderYuan: String(event.detail.value || '') }))} />
            <Input className='admin-input' type='digit' placeholder='最高优惠封顶，单位元，留空不限' value={couponForm.maxDiscountYuan} onInput={event => setCouponForm(prev => ({ ...prev, maxDiscountYuan: String(event.detail.value || '') }))} />
            <Input className='admin-input' type='number' placeholder='单用户可用次数' value={couponForm.perUserLimit} onInput={event => setCouponForm(prev => ({ ...prev, perUserLimit: String(event.detail.value || '') }))} />
            <Input className='admin-input' type='number' placeholder='总可用次数，留空不限' value={couponForm.maxUses} onInput={event => setCouponForm(prev => ({ ...prev, maxUses: String(event.detail.value || '') }))} />
            <Input className='admin-input' placeholder='适用套餐 ID，多个用逗号分隔' value={couponForm.allowedPlanIdsText} onInput={event => setCouponForm(prev => ({ ...prev, allowedPlanIdsText: String(event.detail.value || '') }))} />
            <Input className='admin-input' placeholder='适用地区 ID，多个用逗号分隔' value={couponForm.allowedRegionIdsText} onInput={event => setCouponForm(prev => ({ ...prev, allowedRegionIdsText: String(event.detail.value || '') }))} />
            <Input className='admin-input' placeholder='开始日期 YYYY-MM-DD，可留空' value={couponForm.validFrom} onInput={event => setCouponForm(prev => ({ ...prev, validFrom: String(event.detail.value || '') }))} />
            <Input className='admin-input' placeholder='结束日期 YYYY-MM-DD，可留空' value={couponForm.validTo} onInput={event => setCouponForm(prev => ({ ...prev, validTo: String(event.detail.value || '') }))} />
            <Input className='admin-input' placeholder='内部备注' value={couponForm.remark} onInput={event => setCouponForm(prev => ({ ...prev, remark: String(event.detail.value || '') }))} />
          </View>
          <View className='admin-actions compact'>
            {(['fixed', 'percent'] as const).map(type => (
              <Button key={type} className={`admin-button ${couponForm.discountType === type ? 'active' : ''}`} onClick={() => setCouponForm(prev => ({ ...prev, discountType: type, discountValue: '' }))}>{type === 'fixed' ? '固定金额' : '百分比'}</Button>
            ))}
            {(['active', 'disabled', 'archived'] as const).map(status => (
              <Button key={status} className={`admin-button ${couponForm.status === status ? 'active' : ''}`} onClick={() => setCouponForm(prev => ({ ...prev, status }))}>{status === 'active' ? '启用' : status === 'disabled' ? '停用' : '归档'}</Button>
            ))}
            <Button className={`admin-button ${couponForm.firstOrderOnly ? 'active' : ''}`} onClick={() => setCouponForm(prev => ({ ...prev, firstOrderOnly: !prev.firstOrderOnly }))}>{couponForm.firstOrderOnly ? '仅首单' : '不限首单'}</Button>
            <Button className='admin-button primary' loading={busy === 'coupon-save'} onClick={saveCoupon}>保存优惠券</Button>
            <Button className='admin-button' onClick={() => setCouponForm(defaultCouponForm())}>新建空表单</Button>
          </View>
        </View>

        <View className='admin-section'>
          <Text className='admin-section-title'>优惠券核销报表</Text>
          <View className='admin-form-grid'>
            <Input className='admin-input' placeholder='报表开始日期 YYYY-MM-DD' value={couponFilter.startDate} onInput={event => setCouponFilter(prev => ({ ...prev, startDate: String(event.detail.value || '') }))} />
            <Input className='admin-input' placeholder='报表结束日期 YYYY-MM-DD' value={couponFilter.endDate} onInput={event => setCouponFilter(prev => ({ ...prev, endDate: String(event.detail.value || '') }))} />
            <Input className='admin-input' placeholder='活动分组筛选' value={couponFilter.campaignName} onInput={event => setCouponFilter(prev => ({ ...prev, campaignName: String(event.detail.value || '') }))} />
          </View>
          <View className='admin-actions compact'>
            <Button className={`admin-button ${couponFilter.status ? '' : 'active'}`} onClick={() => setCouponFilter(prev => ({ ...prev, status: '' }))}>全部券</Button>
            {['active', 'disabled', 'archived'].map(status => (
              <Button key={status} className={`admin-button ${couponFilter.status === status ? 'active' : ''}`} onClick={() => setCouponFilter(prev => ({ ...prev, status }))}>{status}</Button>
            ))}
            {(['claimed', 'pending', 'used', 'cancelled', 'expired'] as const).map(status => (
              <Button key={status} className={`admin-button ${couponFilter.redemptionStatus === status ? 'active' : ''}`} onClick={() => setCouponFilter(prev => ({ ...prev, redemptionStatus: status }))}>{status}</Button>
            ))}
            <Button className='admin-button primary' loading={busy === 'coupons'} onClick={() => reloadCoupons()}>刷新报表</Button>
          </View>
          <View className='admin-grid'>
            <View className='admin-metric'><Text className='admin-metric-label'>领取数</Text><Text className='admin-metric-value'>{String(couponReport?.claimCount || 0)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>核销数</Text><Text className='admin-metric-value'>{String(couponReport?.usedCount || 0)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>抵扣金额</Text><Text className='admin-metric-value'>{money(couponReport?.discountCents)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>转化率</Text><Text className='admin-metric-value'>{percentLabel(couponReport?.conversionRate)}</Text></View>
          </View>
          <View className='admin-list compact'>
            {(couponReport?.byCampaign || []).slice(0, 5).map((item, index) => (
              <View className='admin-row' key={`${String(item.campaignName || 'campaign')}:${index}`}>
                <Text className='admin-row-main'>{String(item.campaignName || '未分组')}</Text>
                <Text className='admin-row-meta'>领取 {String(item.claimCount || 0)} · 核销 {String(item.usedCount || 0)} · 抵扣 {money(item.discountCents)}</Text>
              </View>
            ))}
          </View>
        </View>

        <View className='admin-section'>
          <Text className='admin-section-title'>CRM 配置</Text>
          <View className='admin-form-grid'>
            <Button className={`admin-button ${crmForm.enabled ? 'active' : ''}`} onClick={() => setCrmForm(prev => ({ ...prev, enabled: !prev.enabled }))}>{crmForm.enabled ? '已启用' : '已停用'}</Button>
            <Input className='admin-input' placeholder='Webhook URL' value={crmForm.url} onInput={event => setCrmForm(prev => ({ ...prev, url: String(event.detail.value || '') }))} />
            <Input className='admin-input' placeholder='secretRef，例如 app_private.tenant_secrets:crm:webhook' value={crmForm.secretRef} onInput={event => setCrmForm(prev => ({ ...prev, secretRef: String(event.detail.value || '') }))} />
            <Input className='admin-input' placeholder='表单名称' value={crmForm.formName} onInput={event => setCrmForm(prev => ({ ...prev, formName: String(event.detail.value || '') }))} />
            <Input className='admin-input' placeholder='考试类型' value={crmForm.examType} onInput={event => setCrmForm(prev => ({ ...prev, examType: String(event.detail.value || '') }))} />
            <Input className='admin-input' type='number' placeholder='超时秒数' value={crmForm.timeoutSec} onInput={event => setCrmForm(prev => ({ ...prev, timeoutSec: String(event.detail.value || '') }))} />
            <Input className='admin-input' type='number' placeholder='延迟推送秒数' value={crmForm.delaySec} onInput={event => setCrmForm(prev => ({ ...prev, delaySec: String(event.detail.value || '') }))} />
            <View className='admin-actions compact'>
              {(['none', 'referrer', 'direct', 'round_robin'] as const).map(mode => (
                <Button
                  key={mode}
                  className={`admin-button ${crmForm.assignmentMode === mode ? 'active' : ''}`}
                  onClick={() => setCrmForm(prev => ({ ...prev, assignmentMode: mode }))}
                >
                  {mode === 'none' ? '不分配' : mode === 'referrer' ? '归属人跟进' : mode === 'direct' ? '固定负责人' : '轮询分配'}
                </Button>
              ))}
            </View>
            <Input className='admin-input' placeholder='负责人用户ID，多个用英文逗号分隔' value={crmForm.assignmentPoolText} onInput={event => setCrmForm(prev => ({ ...prev, assignmentPoolText: String(event.detail.value || '') }))} />
          </View>
          <View className='admin-actions compact'>
            <Button className='admin-button primary' loading={busy === 'crm'} onClick={saveCrmConfig}>保存 CRM</Button>
            <Button className='admin-button' onClick={() => void reloadAll()}>刷新</Button>
          </View>
          <View className='admin-row'>
            <Text className='admin-row-main'>{crmConfig?.enabled ? 'CRM 已启用' : 'CRM 未启用'}</Text>
            <Text className='admin-row-meta'>{crmConfig?.url || '未配置 URL'} · {crmConfig?.secretRef || '未配置 secretRef'}</Text>
            <Text className='admin-row-meta'>分配策略 {crmConfig?.assignmentMode || 'none'} · 候选 {crmConfig?.assignmentPool?.length || 0} 人 · 游标 {crmConfig?.assignmentCursor || 0}</Text>
          </View>
          <View className='admin-list compact'>
            {salesMembers.slice(0, 8).map(item => (
              <View className='admin-row' key={item.userId}>
                <Text className='admin-row-main'>{item.name || item.username || item.userId}</Text>
                <Text className='admin-row-meta'>{item.role} · {item.userId}</Text>
              </View>
            ))}
          </View>
        </View>

        <View className='admin-section'>
          <Text className='admin-section-title'>CRM 队列</Text>
          <View className='admin-metric-grid compact'>
            <View className='admin-metric'><Text className='admin-metric-label'>失败池</Text><Text className='admin-metric-value'>{String(crmDeadLetters.summary?.total || 0)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>待重试</Text><Text className='admin-metric-value'>{String(crmDeadLetters.summary?.failed || 0)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>已忽略</Text><Text className='admin-metric-value'>{String(crmDeadLetters.summary?.ignored || 0)}</Text></View>
          </View>
          <View className='admin-actions compact'>
            {['', 'pending', 'sent', 'failed', 'discarded'].map(status => (
              <Button
                key={status || 'all'}
                className={`admin-button ${crmStatus === status ? 'active' : ''}`}
                onClick={() => {
                  setCrmStatus(status);
                  void refreshCrmQueue(status);
                }}
              >
                {status || '全部'}
              </Button>
            ))}
          </View>
          <View className='admin-list'>
            {crmQueue.slice(0, 10).map(item => (
              <View className='admin-row' key={item.id}>
                <Text className='admin-row-main'>{item.status || 'pending'} · {item.source || 'lead'}</Text>
                <Text className='admin-row-meta'>尝试 {item.attempts || 0} 次 · HTTP {item.lastHttpCode || '-'} · {shortDate(item.sentAt || item.nextAttemptAt || item.deadLetteredAt || item.createdAt)}</Text>
                {item.provider || item.targetUrl ? <Text className='admin-row-meta break-line'>{item.provider || 'webhook'} · {item.targetUrl || '未配置目标'}</Text> : null}
                {item.lastError ? <Text className='admin-row-meta break-line'>{item.lastError}</Text> : null}
                {item.lastOperatorAction ? <Text className='admin-row-meta'>人工处理 {item.lastOperatorAction} · {shortDate(item.lastOperatorAt)} · {item.lastOperatorNote || '无备注'}</Text> : null}
                <View className='admin-actions compact'>
                  <Button className='admin-mini-button' loading={busy === `crm-log:${item.id}`} onClick={() => showCrmQueueLogs(item)}>日志</Button>
                  {item.status === 'failed' || item.status === 'discarded' ? <Button className='admin-mini-button primary' loading={busy === `crm-action:${item.id}:retry`} onClick={() => handleCrmQueueAction(item, 'retry')}>重试</Button> : null}
                  {item.status === 'failed' ? <Button className='admin-mini-button danger' loading={busy === `crm-action:${item.id}:ignore`} onClick={() => handleCrmQueueAction(item, 'ignore')}>忽略</Button> : null}
                </View>
                {crmLogs[item.id]?.length ? (
                  <View className='admin-sub-list'>
                    {crmLogs[item.id].slice(0, 5).map(log => (
                      <View className='admin-sub-row' key={log.id}>
                        <Text className='admin-row-meta'>{log.outcome || log.operation || 'log'} · 第 {log.attempt || 0} 次 · HTTP {log.httpCode || '-'} · {shortDate(log.createdAt || log.signedAt)}</Text>
                        {log.errorMessage ? <Text className='admin-row-meta break-line'>{log.errorMessage}</Text> : null}
                        {log.responseSummary ? <Text className='admin-row-meta break-line'>{log.responseSummary}</Text> : null}
                      </View>
                    ))}
                  </View>
                ) : null}
              </View>
            ))}
          </View>
          {!crmQueue.length ? <View className='admin-empty'>暂无 CRM 队列。</View> : null}
          {crmDeadLetters.items?.length ? (
            <View className='admin-list'>
              <Text className='admin-section-title'>近期失败任务</Text>
              {crmDeadLetters.items.slice(0, 5).map(item => (
                <View className='admin-row' key={`dead-${item.id}`}>
                  <Text className='admin-row-main'>{item.status || 'failed'} · {item.source || 'CRM'}</Text>
                  <Text className='admin-row-meta'>进入失败池 {shortDate(item.deadLetteredAt || item.updatedAt || item.createdAt)} · 尝试 {item.attempts || 0} 次</Text>
                  {item.lastError ? <Text className='admin-row-meta break-line'>{item.lastError}</Text> : null}
                  <View className='admin-actions compact'>
                    <Button className='admin-mini-button' loading={busy === `crm-log:${item.id}`} onClick={() => showCrmQueueLogs(item)}>日志</Button>
                    <Button className='admin-mini-button primary' loading={busy === `crm-action:${item.id}:retry`} onClick={() => handleCrmQueueAction(item, 'retry')}>重试</Button>
                    {item.status === 'failed' ? <Button className='admin-mini-button danger' loading={busy === `crm-action:${item.id}:ignore`} onClick={() => handleCrmQueueAction(item, 'ignore')}>忽略</Button> : null}
                  </View>
                </View>
              ))}
            </View>
          ) : null}
        </View>

        <View className='admin-section'>
          <Text className='admin-section-title'>分佣规则</Text>
          <View className='admin-form-grid'>
            <Input className='admin-input' type='digit' placeholder='默认分佣比例，例如 20 表示 20%' value={commissionForm.defaultRatePercent} onInput={event => setCommissionForm(prev => ({ ...prev, defaultRatePercent: String(event.detail.value || '') }))} />
            <Input className='admin-input' type='digit' placeholder='最低结算金额，单位元' value={commissionForm.minSettlementYuan} onInput={event => setCommissionForm(prev => ({ ...prev, minSettlementYuan: String(event.detail.value || '') }))} />
          </View>
          <View className='admin-actions compact'>
            {['manual', 'weekly', 'monthly'].map(item => (
              <Button key={item} className={`admin-button ${commissionForm.settlementCycle === item ? 'active' : ''}`} onClick={() => setCommissionForm(prev => ({ ...prev, settlementCycle: item }))}>{item}</Button>
            ))}
            <Button className='admin-button primary' loading={busy === 'commission-settings'} onClick={saveCommissionSettings}>保存规则</Button>
          </View>
          <View className='admin-row'>
            <Text className='admin-row-main'>默认 {rateToPercent(commissionSettings?.defaultRate)}%</Text>
            <Text className='admin-row-meta'>最低结算 {money(commissionSettings?.minSettlementCents)} · {commissionSettings?.settlementCycle || 'monthly'}</Text>
          </View>
        </View>

        <View className='admin-section'>
          <Text className='admin-section-title'>成员分佣比例</Text>
          <View className='admin-actions compact'>
            {salesMembers.map(item => (
              <Button
                key={item.userId}
                className={`admin-button ${memberRate.userId === item.userId ? 'active' : ''}`}
                onClick={() => setMemberRate(prev => ({ ...prev, userId: item.userId }))}
              >
                {item.name || item.phone || item.username || item.role}
              </Button>
            ))}
          </View>
          <View className='admin-form-grid'>
            <Input className='admin-input' type='digit' placeholder='成员专属比例，例如 35 表示 35%，留空清除' value={memberRate.ratePercent} onInput={event => setMemberRate(prev => ({ ...prev, ratePercent: String(event.detail.value || '') }))} />
            <Button className='admin-button primary' loading={busy === 'member-rate'} onClick={saveMemberRate}>保存成员比例</Button>
          </View>
        </View>

        <View className='admin-section'>
          <Text className='admin-section-title'>分佣统计</Text>
          <View className='admin-form-grid'>
            <Input className='admin-input' placeholder='开始日期 YYYY-MM-DD' value={period.startDate} onInput={event => setPeriod(prev => ({ ...prev, startDate: String(event.detail.value || '') }))} />
            <Input className='admin-input' placeholder='结束日期 YYYY-MM-DD' value={period.endDate} onInput={event => setPeriod(prev => ({ ...prev, endDate: String(event.detail.value || '') }))} />
          </View>
          <View className='admin-actions compact'>
            <Button className={`admin-button ${period.referrerUserId ? '' : 'active'}`} onClick={() => setPeriod(prev => ({ ...prev, referrerUserId: '' }))}>全部成员</Button>
            {salesMembers.map(item => (
              <Button key={item.userId} className={`admin-button ${period.referrerUserId === item.userId ? 'active' : ''}`} onClick={() => setPeriod(prev => ({ ...prev, referrerUserId: item.userId }))}>{item.name || item.phone || item.role}</Button>
            ))}
            <Button className='admin-button primary' loading={busy === 'commission'} onClick={reloadCommission}>刷新统计</Button>
            <Button className='admin-button primary' loading={busy === 'settlement-generate'} onClick={generateSettlement}>生成结算单</Button>
          </View>
          <View className='admin-grid'>
            <View className='admin-metric'><Text className='admin-metric-label'>来源数</Text><Text className='admin-metric-value'>{String(commission?.sourceCount || 0)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>成交金额</Text><Text className='admin-metric-value'>{money(commission?.grossAmountCents)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>应结佣金</Text><Text className='admin-metric-value'>{money(commission?.commissionAmountCents)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>成交用户</Text><Text className='admin-metric-value'>{String(commission?.paidUserCount || 0)}</Text></View>
          </View>
        </View>

        <View className='admin-section'>
          <Text className='admin-section-title'>销售/代理转化</Text>
          <View className='admin-grid'>
            <View className='admin-metric'><Text className='admin-metric-label'>新增客资</Text><Text className='admin-metric-value'>{String(conversionReport?.summary?.leadCount || 0)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>有效成交</Text><Text className='admin-metric-value'>{String(conversionReport?.summary?.convertedLeadCount || 0)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>转化率</Text><Text className='admin-metric-value'>{percentLabel(conversionReport?.summary?.conversionRate)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>成交金额</Text><Text className='admin-metric-value'>{money(conversionReport?.summary?.grossAmountCents)}</Text></View>
          </View>
          <View className='admin-grid'>
            <View className='admin-metric'><Text className='admin-metric-label'>预计佣金</Text><Text className='admin-metric-value'>{money(conversionReport?.summary?.commissionAmountCents)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>CRM 失败</Text><Text className='admin-metric-value'>{String(conversionReport?.summary?.crmFailedCount || 0)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>跟进积压</Text><Text className='admin-metric-value'>{String(conversionReport?.summary?.openFollowupCount || 0)}</Text></View>
            <View className='admin-metric'><Text className='admin-metric-label'>首付均时</Text><Text className='admin-metric-value'>{String(conversionReport?.summary?.avgFirstPayHours || 0)}h</Text></View>
          </View>
          <View className='admin-list compact'>
            {(conversionReport?.funnel || []).map(item => (
              <View className='admin-row' key={String(item.key || item.label)}>
                <Text className='admin-row-main'>{String(item.label || item.key || '漏斗')}</Text>
                <Text className='admin-row-meta'>{String(item.count || 0)}</Text>
              </View>
            ))}
          </View>
          <View className='admin-list'>
            {(conversionReport?.byReferrer || []).slice(0, 8).map(item => (
              <View className='admin-row' key={item.referrerUserId}>
                <Text className='admin-row-main'>{item.name || item.phone || item.username || item.referrerUserId} · {money(item.grossAmountCents)}</Text>
                <Text className='admin-row-meta'>
                  {item.role || 'member'} · 客资 {String(item.leadCount || 0)} · 成交 {String(item.convertedLeadCount || 0)} · 转化 {percentLabel(item.conversionRate)} · 佣金 {money(item.commissionAmountCents)}
                </Text>
                <Text className='admin-row-meta'>
                  CRM 失败 {String(item.crmFailedCount || 0)} · 待跟进 {String(item.openFollowupCount || 0)} · 超期 {String(item.overdueFollowupCount || 0)} · 首付均时 {String(item.avgFirstPayHours || 0)}h
                </Text>
              </View>
            ))}
          </View>
          {(conversionReport?.dailyTrend || []).length ? (
            <View className='admin-list compact'>
              {(conversionReport?.dailyTrend || []).slice(-7).map(item => (
                <View className='admin-row' key={String(item.date)}>
                  <Text className='admin-row-main'>{String(item.date || '-')}</Text>
                  <Text className='admin-row-meta'>客资 {String(item.leadCount || 0)} · 成交来源 {String(item.paidSourceCount || 0)} · 金额 {money(item.grossAmountCents)}</Text>
                </View>
              ))}
            </View>
          ) : null}
          {(conversionReport?.recentUnconvertedLeads || []).length ? (
            <View className='admin-list'>
              <Text className='admin-section-title'>近期未成交客资</Text>
              {(conversionReport?.recentUnconvertedLeads || []).slice(0, 6).map(item => (
                <View className='admin-row' key={String(item.id)}>
                  <Text className='admin-row-main'>{item.studentName || item.studentPhone || item.studentUserId || '学生'} · {item.referrerName || item.referrerPhone || item.referrerUserId || '归属人'}</Text>
                  <Text className='admin-row-meta'>跟进 {item.assignedToName || item.assignedToUserId || '-'} · 来源 {item.source || '-'} · {shortDate(item.boundAt)}</Text>
                </View>
              ))}
            </View>
          ) : null}
          {!(conversionReport?.byReferrer?.length || conversionReport?.recentUnconvertedLeads?.length) ? (
            <View className='admin-empty'>当前筛选范围暂无转化数据，或当前角色只能查看本人客资。</View>
          ) : null}
        </View>

        <View className='admin-section'>
          <Text className='admin-section-title'>分佣订单明细</Text>
          <View className='admin-list'>
            {commissionOrders.slice(0, 10).map((item, index) => (
              <View className='admin-row' key={`${item.sourceType || 'source'}:${item.sourceId || index}`}>
                <Text className='admin-row-main'>{item.referrerName || item.referrerPhone || item.referrerUserId || '成员'} · {money(item.commissionAmountCents)}</Text>
                <Text className='admin-row-meta'>{item.sourceType || '-'} · 成交 {money(item.grossAmountCents)} · 比例 {rateToPercent(item.commissionRate)}% · {item.rateSource || '-'}</Text>
              </View>
            ))}
          </View>
          {!commissionOrders.length ? <View className='admin-empty'>当前筛选范围暂无可结算订单。</View> : null}
        </View>

        <View className='admin-section'>
          <Text className='admin-section-title'>结算单</Text>
          <View className='admin-form-grid'>
            <Input className='admin-input' placeholder='打款方式，例如 offline_bank' value={settlementPay.paymentMethod} onInput={event => setSettlementPay(prev => ({ ...prev, paymentMethod: String(event.detail.value || '') }))} />
            <Input className='admin-input' placeholder='打款账户备注，可填脱敏账号' value={settlementPay.paymentAccount} onInput={event => setSettlementPay(prev => ({ ...prev, paymentAccount: String(event.detail.value || '') }))} />
            <Input className='admin-input' placeholder='凭证链接，例如对象存储签名后的公开凭证或内部 URL' value={proofForm.externalUrl} onInput={event => setProofForm(prev => ({ ...prev, externalUrl: String(event.detail.value || '') }))} />
            <Input className='admin-input' placeholder='凭证标题' value={proofForm.title} onInput={event => setProofForm(prev => ({ ...prev, title: String(event.detail.value || '') }))} />
            <Input className='admin-input' placeholder='复核备注' value={proofForm.reviewNote} onInput={event => setProofForm(prev => ({ ...prev, reviewNote: String(event.detail.value || '') }))} />
          </View>
          <View className='admin-list'>
            {settlements.map(item => {
              const proofs = settlementProofs[item.id] || [];
              return (
                <View className='admin-row' key={item.id}>
                  <Text className='admin-row-main'>{item.settlementNo || item.id}</Text>
                  <Text className='admin-row-meta'>{item.referrerName || item.referrerPhone || item.referrerUserId} · {item.status || 'draft'} · {money(item.commissionAmountCents)}</Text>
                  <Text className='admin-row-meta'>{item.periodStart || '-'} 至 {item.periodEnd || '-'} · 来源 {item.sourceCount || 0} · 凭证 {proofs.length}</Text>
                  <View className='admin-row-actions'>
                    <Button className='admin-mini-button' loading={busy === `settlement:${item.id}:export`} onClick={() => exportSettlement(item)}>导出 CSV</Button>
                    <Button className='admin-mini-button' loading={busy === `settlement:${item.id}:proofs`} onClick={() => refreshProofs(item.id)}>查看凭证</Button>
                    {item.status === 'pending_review' ? <Button className='admin-mini-button primary' loading={busy === `settlement:${item.id}:approved`} onClick={() => updateSettlement(item, 'approved')}>审核通过</Button> : null}
                    {item.status === 'pending_review' ? <Button className='admin-mini-button' loading={busy === `settlement:${item.id}:rejected`} onClick={() => updateSettlement(item, 'rejected')}>驳回</Button> : null}
                    {item.status === 'approved' ? <Button className='admin-mini-button primary' loading={busy === `settlement:${item.id}:paid`} onClick={() => updateSettlement(item, 'paid')}>标记打款</Button> : null}
                    {item.status === 'paid' ? <Button className='admin-mini-button primary' loading={busy === `settlement:${item.id}:proof`} onClick={() => submitProof(item)}>登记凭证</Button> : null}
                  </View>
                  {proofs.map(proof => (
                    <View className='admin-row nested' key={proof.id}>
                      <Text className='admin-row-main'>{proof.title || proof.proofType || '凭证'} · {proof.status || 'submitted'}</Text>
                      <Text className='admin-row-meta'>{proof.externalUrl || proof.assetId || '-'} · {money(proof.amountCents)}</Text>
                      <View className='admin-row-actions'>
                        {proof.status === 'submitted' ? <Button className='admin-mini-button primary' loading={busy === `proof:${proof.id}:approved`} onClick={() => reviewProof(proof, 'approved')}>凭证通过</Button> : null}
                        {proof.status === 'submitted' ? <Button className='admin-mini-button' loading={busy === `proof:${proof.id}:rejected`} onClick={() => reviewProof(proof, 'rejected')}>凭证驳回</Button> : null}
                      </View>
                    </View>
                  ))}
                </View>
              );
            })}
          </View>
          {!settlements.length ? <View className='admin-empty'>暂无结算单。</View> : null}
        </View>

        <View className='admin-section'>
          <Text className='admin-section-title'>优惠券</Text>
          <View className='admin-list'>
            {coupons.map(item => (
              <View className='admin-row' key={item.id}>
                <Text className='admin-row-main'>{item.code}</Text>
                <Text className='admin-row-meta'>
                  {item.status || 'active'} · {item.campaignName || '未分组'} · {item.discountType === 'fixed' ? money(item.discountValue) : `${String(item.discountValue ?? 0)}%`}
                </Text>
                <Text className='admin-row-meta'>
                  已用 {item.usedCount || 0}/{item.maxUses || '不限'} · 满 {money(item.minOrderAmountCents)} 可用 · 封顶 {item.maxDiscountCents === null || item.maxDiscountCents === undefined ? '不限' : money(item.maxDiscountCents)} · 每人 {item.perUserLimit || 1} 次
                </Text>
                <Text className='admin-row-meta break-line'>
                  套餐 {item.allowedPlanIds?.length ? item.allowedPlanIds.join(', ') : item.planId || '不限'} · 地区 {item.allowedRegionIds?.length ? item.allowedRegionIds.join(', ') : '不限'} {item.firstOrderOnly ? '· 仅首单' : ''}
                </Text>
                <View className='admin-row-actions'>
                  <Button className='admin-mini-button primary' onClick={() => editCoupon(item)}>编辑规则</Button>
                  <Button
                    className={`admin-mini-button ${couponFilter.selectedCouponId === item.id ? 'primary' : ''}`}
                    onClick={() => {
                      const override = { selectedCouponId: item.id, campaignName: item.campaignName || couponFilter.campaignName };
                      setCouponFilter(prev => ({ ...prev, ...override }));
                      void reloadCoupons(override);
                    }}
                  >
                    查看核销
                  </Button>
                </View>
              </View>
            ))}
          </View>
          {!coupons.length ? <View className='admin-empty'>暂无优惠券。</View> : null}
        </View>

        <View className='admin-section'>
          <Text className='admin-section-title'>核销明细</Text>
          <View className='admin-list'>
            {couponRedemptions.map(item => (
              <View className='admin-row' key={item.id}>
                <Text className='admin-row-main'>{item.couponCode || '优惠券'} · {item.status || 'claimed'} · {money(item.discountAppliedCents)}</Text>
                <Text className='admin-row-meta'>{item.userName || item.userPhone || item.userId || '学生'} · {item.planName || item.planId || '套餐'} · {item.regionName || item.regionId || '未指定地区'}</Text>
                <Text className='admin-row-meta'>{item.orderNo || '未绑定订单'} · 领取 {item.claimedAt || item.createdAt || '-'} · 核销 {item.usedAt || '-'}</Text>
              </View>
            ))}
          </View>
          {!couponRedemptions.length ? <View className='admin-empty'>当前筛选下暂无核销明细，或当前角色没有 `coupons:redemptions:read` 权限。</View> : null}
        </View>

        <View className='admin-section'>
          <Text className='admin-section-title'>激活码批次</Text>
          <View className='admin-list'>
            {batches.map(item => (
              <View className='admin-row' key={item.id}>
                <Text className='admin-row-main'>{item.name || item.batchNo || item.id}</Text>
                <Text className='admin-row-meta'>{item.status || 'active'} · {item.usedCount || 0}/{item.totalCount || 0} 已用</Text>
              </View>
            ))}
          </View>
          {!batches.length ? <View className='admin-empty'>暂无激活码批次。</View> : null}
        </View>

        <View className='admin-section'>
          <Text className='admin-section-title'>最近激活码</Text>
          <View className='admin-list'>
            {codes.slice(0, 8).map((item, index) => (
              <View className='admin-row' key={String(item.id || index)}>
                <Text className='admin-row-main'>{String(item.code || '激活码')}</Text>
                <Text className='admin-row-meta'>{String(item.isUsed ? '已使用' : '未使用')} · {String(item.days || 0)} 天</Text>
              </View>
            ))}
          </View>
        </View>

        {error ? <Text className='admin-error'>{error}</Text> : null}
      </View>
    </View>
  );
}
