import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
  Button, Card, Col, DatePicker, Dropdown, Form, Input, InputNumber, message, Modal, QRCode, Radio, Row, Select, Space, Table, Tag, Tooltip, Typography, theme,
} from 'antd';
import type { MenuProps, TableProps } from 'antd';
import type { Dayjs } from 'dayjs';
import type { MouseEvent as ReactMouseEvent } from 'react';
import {
  AppstoreOutlined, CheckCircleOutlined, CreditCardOutlined, FieldTimeOutlined,
  LinkOutlined, MoreOutlined, PlayCircleOutlined, QrcodeOutlined, ReloadOutlined, SearchOutlined, SettingOutlined, ShoppingOutlined,
} from '@ant-design/icons';
import {
  huyaApi,
  type HuyaAccountItem,
  type HuyaConfig,
  type HuyaGoodsItem,
  type HuyaRechargeGoodsItem,
  type HuyaTaskItem,
} from '../api/modules';
import RealtimeLogPanel from '../components/RealtimeLogPanel';
import { usePermissions } from '../hooks/usePermissions';
import { useWebSocketLogs } from '../hooks/useWebSocketLogs';
import { formatTime } from '../utils/time';
import { getErrorMessage } from '../utils/error';

const { Text } = Typography;

const FALLBACK_TASK_TYPES: Record<string, string> = {
  get_bind_qr: '获取绑定二维码',
  confirm_bind: '确认绑定',
  query_points: '一键查询积分',
  query_game_name: '一键查询游戏名',
  query_exchange_records: '一键查询兑换记录',
  refresh_goods: '刷新商品列表',
  refresh_recharge_goods: '刷新充值商品列表',
  exchange_goods: '兑换商品',
  create_recharge_order: '生成支付二维码',
};

const QUICK_ACTIONS = [
  { key: 'get_bind_qr', icon: <LinkOutlined /> },
  { key: 'confirm_bind', icon: <CheckCircleOutlined /> },
  { key: 'query_points', icon: <SearchOutlined /> },
  { key: 'query_game_name', icon: <AppstoreOutlined /> },
  { key: 'query_exchange_records', icon: <FieldTimeOutlined /> },
  { key: 'refresh_goods', icon: <ReloadOutlined /> },
  { key: 'refresh_recharge_goods', icon: <ShoppingOutlined /> },
  { key: 'exchange_goods', icon: <ShoppingOutlined /> },
  { key: 'create_recharge_order', icon: <CreditCardOutlined /> },
];

const STATUS_COLORS: Record<string, string> = {
  planned: 'default',
  pending: 'default',
  running: 'processing',
  success: 'success',
  failed: 'error',
  error: 'error',
};

const STATUS_LABELS: Record<string, string> = {
  planned: '已计划',
  pending: '等待中',
  running: '执行中',
  success: '成功',
  failed: '失败',
  error: '异常',
};

const ACCOUNT_STATUS_LABELS: Record<string, string> = {
  imported: '已导入',
  updated: '已更新',
  password_imported: '待登录',
  login_success: '登录成功',
  password_changed: '已改密',
  login_failed: '登录失败',
  active: '正常',
  invalid: '失效',
  points_queried: '已查积分',
  game_queried: '已查角色',
  game_not_bound: '未绑定',
  bind_qr_generated: '待扫码',
  bind_confirmed: '已绑定',
  goods_exchanged: '已兑换',
  recharge_order_created: '待支付',
};

const ACCOUNT_STATUS_COLORS: Record<string, string> = {
  imported: 'blue',
  updated: 'cyan',
  password_imported: 'warning',
  login_success: 'success',
  password_changed: 'success',
  login_failed: 'error',
  active: 'success',
  invalid: 'error',
  points_queried: 'success',
  game_queried: 'success',
  game_not_bound: 'default',
  bind_qr_generated: 'processing',
  bind_confirmed: 'success',
  goods_exchanged: 'success',
  recharge_order_created: 'processing',
};

function accountLabel(account: HuyaAccountItem): string {
  const name = account.nickname || account.username || account.uid || `#${account.id}`;
  const tag = account.tag ? ` [${account.tag}]` : '';
  const phone = account.game_phone ? ` / ${account.game_phone}` : '';
  return `${name}${tag}${phone}`;
}

function accountDisplayName(account: HuyaAccountItem): string {
  return account.nickname || account.username || account.uid || `#${account.id}`;
}

function resultText(result: Record<string, unknown> | null | undefined, key: string): string {
  const value = result?.[key];
  return typeof value === 'string' ? value : '';
}

function resultObject(result: Record<string, unknown> | null | undefined, key: string): Record<string, unknown> | null {
  const value = result?.[key];
  if (!value || typeof value !== 'object' || Array.isArray(value)) return null;
  return value as Record<string, unknown>;
}

function resultProfileNick(result: Record<string, unknown> | null | undefined): string {
  const profile = result?.profile;
  if (!profile || typeof profile !== 'object' || Array.isArray(profile)) return '';
  const nick = (profile as Record<string, unknown>).nick;
  return typeof nick === 'string' ? nick : '';
}

function taskPayloadValue(task: HuyaTaskItem | null | undefined, key: string): string {
  const payload = resultObject(task?.result, 'payload');
  const value = payload?.[key];
  if (typeof value === 'string') return value;
  if (typeof value === 'number') return String(value);
  return '';
}

function taskProductText(task: HuyaTaskItem | null | undefined): string {
  const result = task?.result;
  return (
    resultText(result, 'product_name')
    || taskPayloadValue(task, 'product_name')
    || resultText(result, 'product_id')
    || taskPayloadValue(task, 'product_id')
    || resultText(result, 'spu_id')
    || taskPayloadValue(task, 'spu_id')
  );
}

function isInteractiveTarget(target: EventTarget | null): boolean {
  const element = target instanceof HTMLElement ? target : null;
  if (!element) return false;
  return Boolean(element.closest([
    'a',
    'button',
    'input',
    'textarea',
    'select',
    '[role="button"]',
    '[role="menuitem"]',
    '.ant-btn',
    '.ant-checkbox',
    '.ant-checkbox-wrapper',
    '.ant-dropdown',
    '.ant-dropdown-menu',
    '.ant-input',
    '.ant-picker',
    '.ant-select',
    '.ant-table-column-sorter',
  ].join(',')));
}

function contextMenuPosition(x: number, y: number) {
  if (typeof window === 'undefined') return { left: x, top: y };
  return {
    left: Math.max(8, Math.min(x, window.innerWidth - 220)),
    top: Math.max(8, Math.min(y, window.innerHeight - 390)),
  };
}

function goodsRawString(item: HuyaGoodsItem, key: string): string {
  const value = item.raw?.[key];
  if (typeof value === 'string') return value;
  if (typeof value === 'number') return String(value);
  return '';
}

function goodsRawNumber(item: HuyaGoodsItem, key: string): number {
  const value = item.raw?.[key];
  if (typeof value === 'number') return value;
  if (typeof value === 'string' && value.trim()) return Number(value) || 0;
  return 0;
}

function goodsCategoryKey(item: HuyaGoodsItem): string {
  return goodsRawString(item, 'category_id') || goodsRawString(item, 'category_name') || 'uncategorized';
}

function goodsCategoryLabel(item: HuyaGoodsItem): string {
  return goodsRawString(item, 'category_name') || goodsRawString(item, 'category_id') || '未分类';
}

function formatRemainText(value: string): string {
  const text = String(value || '').trim();
  if (!text || text.endsWith('%')) return text;
  return /^-?\d+(\.\d+)?$/.test(text) ? `${text}%` : text;
}

function formatPriceText(value: number | null | undefined): string {
  if (!value) return '';
  return `¥${(value / 100).toFixed(2)}`;
}

function hasMiniQrcode(task: HuyaTaskItem): boolean {
  return task.task_type === 'get_bind_qr' && Boolean(resultText(task.result, 'mini_qrcode_image'));
}

function hasPaymentQrcode(task: HuyaTaskItem): boolean {
  return task.task_type === 'create_recharge_order' && Boolean(resultText(task.result, 'pay_url'));
}

function paymentStatus(task: HuyaTaskItem | null | undefined): string {
  const status = task?.result?.payment_status;
  return typeof status === 'string' ? status : '';
}

function paymentStatusLabel(task: HuyaTaskItem | null | undefined): string {
  const label = task?.result?.payment_status_label;
  return typeof label === 'string' ? label : '';
}

function isPaymentFinished(task: HuyaTaskItem | null | undefined): boolean {
  return task?.task_type === 'create_recharge_order' && paymentStatus(task) === 'paid';
}

export default function HuyaTasksPage() {
  const { token } = theme.useToken();
  const [form] = Form.useForm<HuyaConfig>();
  const [accounts, setAccounts] = useState<HuyaAccountItem[]>([]);
  const [tasks, setTasks] = useState<HuyaTaskItem[]>([]);
  const [goods, setGoods] = useState<HuyaGoodsItem[]>([]);
  const [rechargeGoods, setRechargeGoods] = useState<HuyaRechargeGoodsItem[]>([]);
  const [taskTypes, setTaskTypes] = useState<Record<string, string>>(FALLBACK_TASK_TYPES);
  const [tags, setTags] = useState<string[]>([]);
  const [selectedIds, setSelectedIds] = useState<number[]>([]);
  const [selectedTaskType, setSelectedTaskType] = useState('query_points');
  const [searchText, setSearchText] = useState('');
  const [tagFilter, setTagFilter] = useState('');
  const [accountContextMenu, setAccountContextMenu] = useState<{ account: HuyaAccountItem; accountIds: number[]; x: number; y: number } | null>(null);
  const [selectedGoodsCategory, setSelectedGoodsCategory] = useState('');
  const [selectedExchangeGoodsId, setSelectedExchangeGoodsId] = useState('');
  const [exchangeAt, setExchangeAt] = useState<Dayjs | null>(null);
  const [selectedRechargeGoodsId, setSelectedRechargeGoodsId] = useState<string>('');
  const [rechargeCount, setRechargeCount] = useState(1);
  const [rechargePayChannel, setRechargePayChannel] = useState('Weixin');
  const [concurrency, setConcurrency] = useState(() => {
    const v = localStorage.getItem('huya_task_concurrency');
    return v ? Math.max(1, Math.min(10, Number(v) || 3)) : 3;
  });
  const [loading, setLoading] = useState(false);
  const [starting, setStarting] = useState(false);
  const [savingConfig, setSavingConfig] = useState(false);
  const [batchId, setBatchId] = useState<string | null>(null);
  const [qrTask, setQrTask] = useState<HuyaTaskItem | null>(null);
  const [payTask, setPayTask] = useState<HuyaTaskItem | null>(null);
  const [exchangeRecordsTask, setExchangeRecordsTask] = useState<HuyaTaskItem | null>(null);
  const autoOpenedQrTaskIds = useRef<Set<number>>(new Set());
  const autoOpenQrReady = useRef(false);
  const autoOpenedPayTaskIds = useRef<Set<number>>(new Set());
  const autoOpenPayReady = useRef(false);
  const notifiedPaidTaskIds = useRef<Set<number>>(new Set());
  const { logs, connected: wsConnected, connect: connectLogs } = useWebSocketLogs();
  const { can } = usePermissions();

  const canTask = can('huya:task');
  const canConfig = can('huya:config');

  useEffect(() => {
    localStorage.setItem('huya_task_concurrency', String(concurrency));
  }, [concurrency]);

  const rememberExistingQrcodes = useCallback((items: HuyaTaskItem[]) => {
    if (autoOpenQrReady.current) return;
    items.filter(hasMiniQrcode).forEach((task) => autoOpenedQrTaskIds.current.add(task.id));
    autoOpenQrReady.current = true;
  }, []);

  const rememberExistingPaymentQrcodes = useCallback((items: HuyaTaskItem[]) => {
    if (autoOpenPayReady.current) return;
    items.filter(hasPaymentQrcode).forEach((task) => autoOpenedPayTaskIds.current.add(task.id));
    autoOpenPayReady.current = true;
  }, []);

  const openQrTask = useCallback((task: HuyaTaskItem) => {
    autoOpenedQrTaskIds.current.add(task.id);
    setQrTask(task);
  }, []);

  const openPayTask = useCallback((task: HuyaTaskItem) => {
    autoOpenedPayTaskIds.current.add(task.id);
    setPayTask(task);
  }, []);

  const loadAll = useCallback(async () => {
    setLoading(true);
    try {
      const [accountResult, taskResult, goodsResult, rechargeGoodsResult, configResult, taskTypeResult, tagResult] = await Promise.allSettled([
        huyaApi.listAccounts(),
        huyaApi.listTasks(),
        huyaApi.listGoods(),
        huyaApi.listRechargeGoods(),
        canConfig ? huyaApi.getConfig() : Promise.resolve(null),
        huyaApi.taskTypes(),
        huyaApi.listTags(),
      ]);

      if (accountResult.status === 'fulfilled') setAccounts(accountResult.value);
      if (taskResult.status === 'fulfilled') {
        rememberExistingQrcodes(taskResult.value);
        rememberExistingPaymentQrcodes(taskResult.value);
        setTasks(taskResult.value);
      }
      if (goodsResult.status === 'fulfilled') setGoods(goodsResult.value);
      if (rechargeGoodsResult.status === 'fulfilled') setRechargeGoods(rechargeGoodsResult.value);
      if (configResult.status === 'fulfilled' && configResult.value) form.setFieldsValue(configResult.value);
      if (taskTypeResult.status === 'fulfilled') setTaskTypes({ ...FALLBACK_TASK_TYPES, ...taskTypeResult.value });
      if (tagResult.status === 'fulfilled') setTags(tagResult.value);

      const failedLabels = [
        accountResult.status === 'rejected' ? `CK 列表: ${getErrorMessage(accountResult.reason)}` : '',
        taskResult.status === 'rejected' ? `任务记录: ${getErrorMessage(taskResult.reason)}` : '',
        goodsResult.status === 'rejected' ? `兑换商品: ${getErrorMessage(goodsResult.reason)}` : '',
        rechargeGoodsResult.status === 'rejected' ? `充值商品: ${getErrorMessage(rechargeGoodsResult.reason)}` : '',
        configResult.status === 'rejected' ? `虎牙配置: ${getErrorMessage(configResult.reason)}` : '',
        taskTypeResult.status === 'rejected' ? `任务类型: ${getErrorMessage(taskTypeResult.reason)}` : '',
        tagResult.status === 'rejected' ? `标签: ${getErrorMessage(tagResult.reason)}` : '',
      ].filter(Boolean);
      if (failedLabels.length > 0) {
        message.warning(`部分数据加载失败：${failedLabels.join('；')}`);
      }
    } finally {
      setLoading(false);
    }
  }, [canConfig, form, rememberExistingPaymentQrcodes, rememberExistingQrcodes]);

  const loadTasks = useCallback(async () => {
    try {
      const data = await huyaApi.listTasks();
      rememberExistingQrcodes(data);
      rememberExistingPaymentQrcodes(data);
      setTasks(data);
    } catch {
      // 轮询失败不打扰操作，下一轮继续刷新。
    }
  }, [rememberExistingPaymentQrcodes, rememberExistingQrcodes]);

  useEffect(() => {
    loadAll();
  }, [loadAll]);

  useEffect(() => {
    const timer = setInterval(loadTasks, 3000);
    return () => clearInterval(timer);
  }, [loadTasks]);

  useEffect(() => {
    if (!autoOpenQrReady.current || qrTask) return;
    const nextQrTask = tasks
      .filter((task) => hasMiniQrcode(task) && !autoOpenedQrTaskIds.current.has(task.id))
      .sort((a, b) => b.id - a.id)[0];
    if (nextQrTask) openQrTask(nextQrTask);
  }, [openQrTask, qrTask, tasks]);

  useEffect(() => {
    if (!autoOpenPayReady.current || payTask) return;
    const nextPayTask = tasks
      .filter((task) => hasPaymentQrcode(task) && !isPaymentFinished(task) && !autoOpenedPayTaskIds.current.has(task.id))
      .sort((a, b) => b.id - a.id)[0];
    if (nextPayTask) openPayTask(nextPayTask);
  }, [openPayTask, payTask, tasks]);

  useEffect(() => {
    if (!payTask) return;
    const latest = tasks.find((task) => task.id === payTask.id);
    if (!latest) return;
    if (isPaymentFinished(latest)) {
      if (!notifiedPaidTaskIds.current.has(latest.id)) {
        notifiedPaidTaskIds.current.add(latest.id);
        message.success('虎牙支付成功');
      }
      setPayTask(null);
      return;
    }
    if (latest !== payTask) setPayTask(latest);
  }, [payTask, tasks]);

  useEffect(() => {
    if (!accountContextMenu) return;
    const close = () => setAccountContextMenu(null);
    const closeOnEscape = (event: KeyboardEvent) => {
      if (event.key === 'Escape') close();
    };
    window.addEventListener('click', close);
    window.addEventListener('scroll', close, true);
    window.addEventListener('resize', close);
    window.addEventListener('keydown', closeOnEscape);
    return () => {
      window.removeEventListener('click', close);
      window.removeEventListener('scroll', close, true);
      window.removeEventListener('resize', close);
      window.removeEventListener('keydown', closeOnEscape);
    };
  }, [accountContextMenu]);

  const toggleAccountSelection = useCallback((accountId: number) => {
    setSelectedIds((prev) => (
      prev.includes(accountId)
        ? prev.filter((id) => id !== accountId)
        : [...prev, accountId]
    ));
  }, []);

  const tagColorMap = useMemo(() => {
    const map: Record<string, string> = {};
    tags.forEach((tag, index) => {
      map[tag] = ['blue', 'green', 'cyan', 'geekblue', 'purple', 'orange', 'magenta', 'volcano'][index % 8];
    });
    return map;
  }, [tags]);

  const filteredAccounts = useMemo(() => {
    const keyword = searchText.trim().toLowerCase();
    return accounts.filter((account) => {
      if (tagFilter && account.tag !== tagFilter) return false;
      if (!keyword) return true;
      return (
        account.uid.toLowerCase().includes(keyword)
        || account.yyuid.toLowerCase().includes(keyword)
        || account.username.toLowerCase().includes(keyword)
        || account.nickname.toLowerCase().includes(keyword)
        || account.tag.toLowerCase().includes(keyword)
        || account.game_name.toLowerCase().includes(keyword)
        || account.game_channel.toLowerCase().includes(keyword)
        || account.game_phone.toLowerCase().includes(keyword)
        || account.cookie_preview.toLowerCase().includes(keyword)
      );
    });
  }, [accounts, searchText, tagFilter]);

  const latestTaskByAccount = useMemo(() => {
    const map = new Map<number, HuyaTaskItem>();
    tasks.forEach((task) => {
      const current = map.get(task.account_id);
      if (!current || task.id > current.id) map.set(task.account_id, task);
    });
    return map;
  }, [tasks]);

  const latestGoodsTaskByAccount = useMemo(() => {
    const map = new Map<number, HuyaTaskItem>();
    tasks.forEach((task) => {
      if (!['exchange_goods', 'create_recharge_order'].includes(task.task_type)) return;
      const current = map.get(task.account_id);
      if (!current || task.id > current.id) map.set(task.account_id, task);
    });
    return map;
  }, [tasks]);

  const selectedAccounts = useMemo(() => {
    const selected = new Set(selectedIds);
    return accounts.filter((account) => selected.has(account.id));
  }, [accounts, selectedIds]);

  const selectedAccountText = selectedAccounts.length === 1
    ? accountLabel(selectedAccounts[0])
    : `已选 ${selectedIds.length} 个账号`;

  const sortedGoods = useMemo(() => {
    return [...goods].sort((a, b) => (
      goodsRawNumber(a, 'category_sort') - goodsRawNumber(b, 'category_sort')
      || goodsRawNumber(a, 'raw_order') - goodsRawNumber(b, 'raw_order')
      || a.id - b.id
    ));
  }, [goods]);

  const sortedRechargeGoods = useMemo(() => {
    return [...rechargeGoods].sort((a, b) => {
      const aOrder = goodsRawNumber({ raw: a.raw } as HuyaGoodsItem, 'raw_order');
      const bOrder = goodsRawNumber({ raw: b.raw } as HuyaGoodsItem, 'raw_order');
      return aOrder - bOrder || a.id - b.id;
    });
  }, [rechargeGoods]);

  const rechargeGoodsOptions = useMemo(() => {
    return sortedRechargeGoods.map((item) => ({
      value: item.spu_id,
      label: `${item.name || item.spu_id}${item.price ? ` / ${formatPriceText(item.price)}` : ''}`,
    }));
  }, [sortedRechargeGoods]);

  const selectedRechargeGoods = useMemo(() => {
    return sortedRechargeGoods.find((item) => item.spu_id === selectedRechargeGoodsId) || null;
  }, [sortedRechargeGoods, selectedRechargeGoodsId]);

  useEffect(() => {
    if (sortedRechargeGoods.length === 0) {
      if (selectedRechargeGoodsId) setSelectedRechargeGoodsId('');
      return;
    }
    if (!sortedRechargeGoods.some((item) => item.spu_id === selectedRechargeGoodsId)) {
      setSelectedRechargeGoodsId(sortedRechargeGoods[0].spu_id);
    }
  }, [selectedRechargeGoodsId, sortedRechargeGoods]);

  const goodsCategories = useMemo(() => {
    const map = new Map<string, { key: string; label: string; sort: number; count: number }>();
    sortedGoods.forEach((item) => {
      const key = goodsCategoryKey(item);
      const current = map.get(key);
      if (current) {
        current.count += 1;
        return;
      }
      map.set(key, {
        key,
        label: goodsCategoryLabel(item),
        sort: goodsRawNumber(item, 'category_sort'),
        count: 1,
      });
    });
    return Array.from(map.values()).sort((a, b) => a.sort - b.sort || a.label.localeCompare(b.label));
  }, [sortedGoods]);

  useEffect(() => {
    if (goodsCategories.length === 0) {
      if (selectedGoodsCategory) setSelectedGoodsCategory('');
      return;
    }
    if (!goodsCategories.some((item) => item.key === selectedGoodsCategory)) {
      setSelectedGoodsCategory(goodsCategories[0].key);
    }
  }, [goodsCategories, selectedGoodsCategory]);

  const filteredGoods = useMemo(() => {
    if (!selectedGoodsCategory) return sortedGoods;
    return sortedGoods.filter((item) => goodsCategoryKey(item) === selectedGoodsCategory);
  }, [sortedGoods, selectedGoodsCategory]);

  const goodsOptions = useMemo(() => {
    return sortedGoods.map((item) => ({
      value: item.product_id,
      label: `${item.name || item.product_id}${item.price ? ` / ${item.price}积分` : ''}`,
    }));
  }, [sortedGoods]);

  const selectedExchangeGoods = useMemo(() => {
    return sortedGoods.find((item) => item.product_id === selectedExchangeGoodsId) || null;
  }, [selectedExchangeGoodsId, sortedGoods]);

  useEffect(() => {
    if (sortedGoods.length === 0) {
      if (selectedExchangeGoodsId) setSelectedExchangeGoodsId('');
      return;
    }
    if (!sortedGoods.some((item) => item.product_id === selectedExchangeGoodsId)) {
      setSelectedExchangeGoodsId(sortedGoods[0].product_id);
    }
  }, [selectedExchangeGoodsId, sortedGoods]);

  const createPayload = (taskType: string) => {
    if (taskType === 'exchange_goods') {
      return {
        product_id: selectedExchangeGoods?.product_id || selectedExchangeGoodsId,
        product_name: selectedExchangeGoods?.name || '',
        scheduled_at: exchangeAt ? exchangeAt.toISOString() : '',
      };
    }
    if (taskType !== 'create_recharge_order') return {};
    return {
      spu_id: selectedRechargeGoods?.spu_id || selectedRechargeGoodsId,
      sku_id: selectedRechargeGoods?.sku_id || '',
      product_name: selectedRechargeGoods?.name || '',
      count: rechargeCount,
      pay_channel: rechargePayChannel,
    };
  };

  const startTask = async (taskType = selectedTaskType, accountIds = selectedIds) => {
    if (accountIds.length === 0) {
      message.warning('请先选择虎牙 CK');
      return;
    }
    if (taskType === 'create_recharge_order' && !selectedRechargeGoodsId) {
      message.warning('请先选择充值商品');
      return;
    }
    if (taskType === 'exchange_goods' && !selectedExchangeGoodsId) {
      message.warning('请先选择兑换商品');
      return;
    }

    setStarting(true);
    try {
      const result = await huyaApi.createTasks({
        account_ids: accountIds,
        task_type: taskType,
        concurrency,
        payload: createPayload(taskType),
      });
      const finishTask = () => {
        setBatchId(null);
        setStarting(false);
        void loadAll();
      };
      setBatchId(result.batch_id);
      message.success(`已创建 ${taskTypes[taskType] || taskType}，共 ${result.count} 个账号`);
      await loadTasks();
      connectLogs(`/api/huya/ws/${result.batch_id}`, {
        onClose: finishTask,
        onResult: finishTask,
        onError: finishTask,
      });
    } catch (e: unknown) {
      message.error(getErrorMessage(e));
      setStarting(false);
    }
  };

  const accountActionItems: MenuProps['items'] = QUICK_ACTIONS.map((item) => ({
    key: item.key,
    icon: item.icon,
    label: taskTypes[item.key] || item.key,
  }));

  const runSingleAccountAction = (taskType: string, account: HuyaAccountItem) => {
    setSelectedIds([account.id]);
    setAccountContextMenu(null);
    void startTask(taskType, [account.id]);
  };

  const runContextAccountAction = (taskType: string) => {
    if (!accountContextMenu) return;
    setSelectedIds(accountContextMenu.accountIds);
    setAccountContextMenu(null);
    void startTask(taskType, accountContextMenu.accountIds);
  };

  const saveConfig = async () => {
    setSavingConfig(true);
    try {
      const values = await form.validateFields();
      const result = await huyaApi.updateConfig(values);
      form.setFieldsValue(result);
      message.success('虎牙配置已保存');
    } catch (e: unknown) {
      message.error(getErrorMessage(e));
    } finally {
      setSavingConfig(false);
    }
  };

  const successCount = tasks.filter((task) => task.status === 'success').length;
  const plannedCount = tasks.filter((task) => task.status === 'planned').length;
  const failedCount = tasks.filter((task) => ['failed', 'error'].includes(task.status)).length;
  const qrResult = qrTask?.result || null;
  const qrImage = resultText(qrResult, 'mini_qrcode_image');
  const qrAccountName = qrTask
    ? resultProfileNick(qrResult) || qrTask.account_nickname || qrTask.account_uid || `#${qrTask.account_id}`
    : '';
  const payResult = payTask?.result || null;
  const payUrl = resultText(payResult, 'pay_url');
  const payProductName = resultText(payResult, 'product_name');
  const payAmountText = resultText(payResult, 'amount_text');
  const payChannelLabel = resultText(payResult, 'pay_channel_label');
  const payStatus = paymentStatus(payTask);
  const payStatusText = paymentStatusLabel(payTask);
  const payOrderId = payResult?.order_id;
  const payAccountName = payTask
    ? payTask.account_nickname || payTask.account_uid || `#${payTask.account_id}`
    : '';
  const exchangeRecordsResult = exchangeRecordsTask?.result || null;
  const exchangeRecordsRaw = exchangeRecordsResult?.records;
  const exchangeRecords = Array.isArray(exchangeRecordsRaw)
    ? exchangeRecordsRaw.filter((item) => item && typeof item === 'object') as Record<string, unknown>[]
    : [];
  const exchangeRecordsAccountName = exchangeRecordsTask
    ? exchangeRecordsTask.account_nickname || exchangeRecordsTask.account_uid || `#${exchangeRecordsTask.account_id}`
    : '';

  const renderTaskResult = (value: Record<string, unknown> | null, record: HuyaTaskItem) => {
    const bindQrImage = resultText(value, 'mini_qrcode_image');
    if (bindQrImage) {
      return (
        <Button size="small" icon={<QrcodeOutlined />} onClick={() => openQrTask(record)}>
          查看二维码
        </Button>
      );
    }
    if (record.task_type === 'get_bind_qr') {
      if (value?.can_change_bind === false) {
        const changeAt = resultText(value, 'change_available_at');
        return (
          <Space size={6}>
            <Tag color="orange">不可更换</Tag>
            {changeAt ? <Text type="secondary">{changeAt}</Text> : null}
          </Space>
        );
      }
      return <Text type="secondary">-</Text>;
    }
    if (record.task_type === 'confirm_bind') {
      if (value?.bind_confirmed === true) {
        const gameRole = resultObject(value, 'game_role');
        const roleName = typeof gameRole?.role_name === 'string' ? gameRole.role_name : '';
        return (
          <Space size={6}>
            <Tag color="green">已绑定</Tag>
            {roleName ? <Text>{roleName}</Text> : null}
          </Space>
        );
      }
      return <Text type="secondary">-</Text>;
    }
    if (record.task_type === 'query_game_name') {
      const roleName = resultText(value, 'role_name');
      if (roleName) return <Tag color="blue">{roleName}</Tag>;
      if (value?.is_bound === false) return <Tag>未绑定</Tag>;
      return <Text type="secondary">-</Text>;
    }
    if (record.task_type === 'query_exchange_records') {
      const recordCount = value?.record_count;
      if (typeof recordCount === 'number' && recordCount > 0) {
        return (
          <Button size="small" icon={<FieldTimeOutlined />} onClick={() => setExchangeRecordsTask(record)}>
            查看记录
          </Button>
        );
      }
      if (typeof recordCount === 'number') return <Tag>记录 {recordCount} 条</Tag>;
      return <Text type="secondary">-</Text>;
    }
    if (record.task_type === 'refresh_goods') {
      const goodsCount = value?.goods_count;
      if (typeof goodsCount === 'number') return <Tag color="green">商品 {goodsCount} 个</Tag>;
      return <Text type="secondary">-</Text>;
    }
    if (record.task_type === 'exchange_goods') {
      const productName = resultText(value, 'product_name');
      const orderId = resultText(value, 'order_id');
      if (record.status !== 'success') {
        return productName ? <Text>{productName}</Text> : <Text type="secondary">-</Text>;
      }
      return (
        <Space size={6}>
          <Tag color="green">已兑换</Tag>
          {productName ? <Text>{productName}</Text> : null}
          {orderId ? <Text type="secondary">{orderId}</Text> : null}
        </Space>
      );
    }
    if (record.task_type === 'refresh_recharge_goods') {
      const goodsCount = value?.goods_count;
      const failedCount = value?.failed_count;
      if (typeof goodsCount === 'number') {
        return (
          <Space size={6}>
            <Tag color="green">充值商品 {goodsCount} 个</Tag>
            {typeof failedCount === 'number' && failedCount > 0 ? <Tag color="orange">失败 {failedCount}</Tag> : null}
          </Space>
        );
      }
      return <Text type="secondary">-</Text>;
    }
    if (record.task_type === 'create_recharge_order') {
      if (resultText(value, 'pay_url')) {
        if (isPaymentFinished(record)) return <Tag color="green">已支付</Tag>;
        const status = paymentStatus(record);
        return (
          <Space size={6}>
            {status === 'timeout' ? <Tag color="orange">待支付</Tag> : <Tag color="processing">等待支付</Tag>}
            <Button size="small" icon={<QrcodeOutlined />} onClick={() => openPayTask(record)}>
              支付码
            </Button>
          </Space>
        );
      }
      return <Text type="secondary">-</Text>;
    }

    const availableScore = value?.available_score;
    if (typeof availableScore === 'number') {
      return <Tag color="blue">可用积分 {availableScore}</Tag>;
    }

    return value ? <Text code style={{ fontSize: 12 }}>{JSON.stringify(value)}</Text> : <Text type="secondary">-</Text>;
  };

  const handleAccountRowClick = (record: HuyaAccountItem, event: ReactMouseEvent) => {
    if (isInteractiveTarget(event.target)) return;
    setAccountContextMenu(null);
    toggleAccountSelection(record.id);
  };

  const handleAccountRowContextMenu = (record: HuyaAccountItem, event: ReactMouseEvent) => {
    if (isInteractiveTarget(event.target)) return;
    event.preventDefault();
    event.stopPropagation();
    const accountIds = selectedIds.includes(record.id) && selectedIds.length > 0 ? selectedIds : [record.id];
    setSelectedIds(accountIds);
    setAccountContextMenu({
      account: record,
      accountIds,
      x: event.clientX,
      y: event.clientY,
    });
  };

  const accountColumns: TableProps<HuyaAccountItem>['columns'] = [
    {
      title: '#',
      width: 56,
      align: 'center',
      render: (_: unknown, record) => record.id,
    },
    {
      title: '虎牙 CK',
      width: 250,
      render: (_: unknown, record) => (
        <Space direction="vertical" size={1} style={{ width: '100%' }}>
          <Space size={6} wrap>
            <Text strong>{accountDisplayName(record)}</Text>
            {record.tag ? <Tag color={tagColorMap[record.tag]}>{record.tag}</Tag> : null}
          </Space>
          <Text type="secondary" style={{ fontSize: 12 }}>
            UID {record.uid || record.yyuid || '-'}
          </Text>
          <Text code ellipsis style={{ maxWidth: 220, fontSize: 12 }}>
            {record.cookie_preview || '-'}
          </Text>
        </Space>
      ),
    },
    {
      title: '手机',
      dataIndex: 'game_phone',
      width: 128,
      ellipsis: true,
      render: (value: string) => value || <Text type="secondary">-</Text>,
    },
    {
      title: '游戏名',
      dataIndex: 'game_name',
      width: 170,
      ellipsis: true,
      render: (value: string, record) => value ? (
        <Space direction="vertical" size={0}>
          <Text>{value}</Text>
          {record.game_channel ? <Text type="secondary" style={{ fontSize: 12 }}>{record.game_channel}</Text> : null}
        </Space>
      ) : <Text type="secondary">未查</Text>,
    },
    {
      title: '积分',
      dataIndex: 'points',
      width: 92,
      align: 'center',
      render: (points: number | null) => points ?? <Text type="secondary">未查</Text>,
      sorter: (a, b) => (a.points ?? -1) - (b.points ?? -1),
    },
    {
      title: '兑换商品',
      width: 190,
      ellipsis: true,
      render: (_: unknown, record) => {
        const task = latestGoodsTaskByAccount.get(record.id);
        const product = taskProductText(task);
        if (!task || !product) return <Text type="secondary">-</Text>;
        return (
          <Space direction="vertical" size={1}>
            <Text ellipsis style={{ maxWidth: 170 }}>{product}</Text>
            <Tag color={STATUS_COLORS[task.status] || 'default'} style={{ width: 'fit-content' }}>
              {taskTypes[task.task_type] || task.task_type}
            </Tag>
          </Space>
        );
      },
    },
    {
      title: '数据状态',
      width: 150,
      render: (_: unknown, record) => {
        const latest = latestTaskByAccount.get(record.id);
        return (
          <Space size={4} wrap>
            <Tag color={ACCOUNT_STATUS_COLORS[record.status] || 'default'}>
              {ACCOUNT_STATUS_LABELS[record.status] || record.status || '-'}
            </Tag>
            {latest ? (
              <Tag color={STATUS_COLORS[latest.status] || 'default'}>
                {STATUS_LABELS[latest.status] || latest.status}
              </Tag>
            ) : null}
          </Space>
        );
      },
    },
    {
      title: '最近结果',
      width: 260,
      ellipsis: true,
      render: (_: unknown, record) => {
        const latest = latestTaskByAccount.get(record.id);
        if (!latest) return <Text type="secondary">暂无任务</Text>;
        return (
          <Space direction="vertical" size={2} style={{ width: '100%' }}>
            <Text ellipsis style={{ maxWidth: 238 }}>
              {taskTypes[latest.task_type] || latest.task_type}：{latest.message || '-'}
            </Text>
            {renderTaskResult(latest.result, latest)}
          </Space>
        );
      },
    },
    {
      title: '更新时间',
      dataIndex: 'updated_at',
      width: 154,
      render: (value: string | null) => value ? formatTime(value) : <Text type="secondary">-</Text>,
    },
    {
      title: '操作',
      width: 76,
      fixed: 'right',
      align: 'center',
      render: (_: unknown, record) => {
        return (
          <Dropdown
            menu={{
              items: accountActionItems,
              onClick: ({ key }) => {
                runSingleAccountAction(String(key), record);
              },
            }}
            trigger={['click']}
          >
            <Button size="small" icon={<MoreOutlined />} disabled={!canTask || wsConnected} />
          </Dropdown>
        );
      },
    },
  ];

  const taskColumns: TableProps<HuyaTaskItem>['columns'] = [
    { title: 'ID', dataIndex: 'id', width: 70, align: 'center' },
    {
      title: '任务',
      dataIndex: 'task_type',
      width: 150,
      render: (value: string) => taskTypes[value] || value,
    },
    {
      title: '账号',
      width: 160,
      render: (_: unknown, record) => record.account_nickname || record.account_uid || record.account_id,
    },
    {
      title: '状态',
      dataIndex: 'status',
      width: 100,
      align: 'center',
      render: (status: string) => (
        <Tag color={STATUS_COLORS[status] || 'default'}>{STATUS_LABELS[status] || status}</Tag>
      ),
    },
    { title: '消息', dataIndex: 'message', ellipsis: true },
    {
      title: '结果',
      dataIndex: 'result',
      width: 210,
      ellipsis: true,
      render: renderTaskResult,
    },
    {
      title: '时间',
      dataIndex: 'created_at',
      width: 170,
      render: (value: string | null) => value ? formatTime(value) : <Text type="secondary">-</Text>,
    },
  ];

  const goodsColumns: TableProps<HuyaGoodsItem>['columns'] = [
    { title: '商品ID', dataIndex: 'product_id', width: 88, ellipsis: true },
    {
      title: '名称',
      dataIndex: 'name',
      width: 230,
      render: (value: string) => (
        <Text
          title={value}
          style={{
            display: 'block',
            lineHeight: '20px',
            whiteSpace: 'normal',
            wordBreak: 'break-all',
          }}
        >
          {value || '-'}
        </Text>
      ),
    },
    {
      title: '分类',
      width: 96,
      render: (_: unknown, record) => {
        const label = goodsCategoryLabel(record);
        return label ? <Tag>{label}</Tag> : <Text type="secondary">-</Text>;
      },
    },
    {
      title: '价格',
      dataIndex: 'price',
      width: 78,
      align: 'center',
      render: (value: number | null) => value ?? <Text type="secondary">-</Text>,
    },
    {
      title: '库存',
      dataIndex: 'remain_text',
      width: 72,
      render: (value: string) => formatRemainText(value) || <Text type="secondary">-</Text>,
    },
    {
      title: '更新时间',
      dataIndex: 'updated_at',
      width: 154,
      render: (value: string | null) => value ? formatTime(value) : <Text type="secondary">-</Text>,
    },
  ];

  const rechargeGoodsColumns: TableProps<HuyaRechargeGoodsItem>['columns'] = [
    { title: 'SPU', dataIndex: 'spu_id', width: 120, ellipsis: true },
    { title: 'SKU', dataIndex: 'sku_id', width: 100, ellipsis: true },
    { title: '名称', dataIndex: 'name', ellipsis: true },
    {
      title: '单价',
      dataIndex: 'price',
      width: 90,
      align: 'center',
      render: (value: number | null) => formatPriceText(value) || <Text type="secondary">-</Text>,
    },
    {
      title: '库存',
      dataIndex: 'stock',
      width: 90,
      align: 'center',
      render: (value: number | null) => value ?? <Text type="secondary">-</Text>,
    },
    {
      title: '来源',
      dataIndex: 'task_name',
      width: 140,
      ellipsis: true,
      render: (value: string) => value || <Text type="secondary">商品详情</Text>,
    },
    {
      title: '更新时间',
      dataIndex: 'updated_at',
      width: 160,
      render: (value: string | null) => value ? formatTime(value) : <Text type="secondary">-</Text>,
    },
  ];

  const exchangeRecordColumns: TableProps<Record<string, unknown>>['columns'] = [
    {
      title: '序号',
      width: 70,
      align: 'center',
      render: (_: unknown, record, index) => String(record.index || index + 1),
    },
    {
      title: '消耗',
      width: 110,
      render: (_: unknown, record) => {
        const scoreText = record.score_text;
        if (typeof scoreText === 'string' && scoreText) return scoreText;
        const score = record.score;
        return typeof score === 'number' ? `${score}积分` : <Text type="secondary">-</Text>;
      },
    },
    {
      title: '奖品名称',
      dataIndex: 'prize_name',
      ellipsis: true,
      render: (value: unknown) => typeof value === 'string' && value ? value : <Text type="secondary">-</Text>,
    },
    {
      title: '兑换状态',
      width: 110,
      align: 'center',
      render: (_: unknown, record) => {
        const label = typeof record.status_label === 'string' ? record.status_label : '';
        const color = label === '已发放' ? 'green' : 'orange';
        return label ? <Tag color={color}>{label}</Tag> : <Text type="secondary">-</Text>;
      },
    },
    {
      title: '兑换时间',
      width: 170,
      render: (_: unknown, record) => {
        const text = record.exchange_time_text;
        return typeof text === 'string' && text ? text : <Text type="secondary">-</Text>;
      },
    },
  ];

  const taskSummary = (
    <div style={{ display: 'flex', alignItems: 'center', gap: 16, marginBottom: 8, color: token.colorTextSecondary, flexWrap: 'wrap' }}>
      <span>共 <b>{tasks.length}</b> 个任务</span>
      <span>已计划 <b>{plannedCount}</b></span>
      <span>成功 <b style={{ color: token.colorSuccess }}>{successCount}</b></span>
      <span>失败 <b style={{ color: token.colorError }}>{failedCount}</b></span>
    </div>
  );

  const renderTaskTable = (pageSize = 12) => (
    <Table
      columns={taskColumns}
      dataSource={tasks}
      rowKey="id"
      loading={loading}
      size="small"
      pagination={{ pageSize, showTotal: (total) => `共 ${total} 条` }}
      scroll={{ x: 920 }}
    />
  );

  const accountContextMenuPosition = accountContextMenu
    ? contextMenuPosition(accountContextMenu.x, accountContextMenu.y)
    : null;

  return (
    <div style={{ height: '100%', display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
      <div style={{ flexShrink: 0, marginBottom: 12, display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 12 }}>
        <h2 style={{ margin: 0 }}>虎牙任务操作台</h2>
        <Space wrap>
          <Tag>账号 {accounts.length}</Tag>
          <Tag color="blue">已选 {selectedIds.length}</Tag>
          <Tag color="green">已查积分 {accounts.filter((item) => item.points !== null && item.points !== undefined).length}</Tag>
          <Tag color="cyan">已绑定 {accounts.filter((item) => item.game_name || item.game_channel || item.game_phone).length}</Tag>
          <Button icon={<ReloadOutlined />} onClick={loadAll} loading={loading}>
            刷新
          </Button>
          {batchId && <Tag color="processing">批次 {batchId}</Tag>}
        </Space>
      </div>

      {accountContextMenu && accountContextMenuPosition ? (
        <div
          style={{
            position: 'fixed',
            left: accountContextMenuPosition.left,
            top: accountContextMenuPosition.top,
            zIndex: 2000,
            width: 210,
            padding: 6,
            borderRadius: 8,
            border: `1px solid ${token.colorBorderSecondary}`,
            background: token.colorBgElevated,
            boxShadow: token.boxShadowSecondary,
          }}
          onMouseDown={(event) => event.stopPropagation()}
          onClick={(event) => event.stopPropagation()}
          onContextMenu={(event) => event.preventDefault()}
        >
          <div style={{ padding: '4px 8px 8px', borderBottom: `1px solid ${token.colorBorderSecondary}`, marginBottom: 4 }}>
            <Text strong ellipsis style={{ display: 'block' }}>
              {accountContextMenu.accountIds.length > 1
                ? `已选 ${accountContextMenu.accountIds.length} 个账号`
                : accountDisplayName(accountContextMenu.account)}
            </Text>
            <Text type="secondary" style={{ fontSize: 12 }}>
              {accountContextMenu.accountIds.length > 1 ? '右键批量动作' : '右键账号动作'}
            </Text>
          </div>
          <Space direction="vertical" size={2} style={{ width: '100%' }}>
            {QUICK_ACTIONS.map((item) => (
              <Button
                key={item.key}
                type="text"
                size="small"
                block
                icon={item.icon}
                disabled={!canTask || wsConnected}
                onClick={() => runContextAccountAction(item.key)}
                style={{ justifyContent: 'flex-start' }}
              >
                {taskTypes[item.key] || item.key}
              </Button>
            ))}
          </Space>
        </div>
      ) : null}

      <div
        style={{
          flex: 1,
          minHeight: 0,
          display: 'grid',
          gridTemplateColumns: 'minmax(0, 1fr) minmax(330px, 380px)',
          gap: 12,
          overflow: 'hidden',
        }}
      >
        <div style={{ minWidth: 0, minHeight: 0, overflowY: 'auto', overflowX: 'hidden', paddingRight: 2 }}>
          <Card
            size="small"
            title="账号表格"
            extra={(
              <Space size={6} wrap>
                <Button size="small" onClick={() => setSelectedIds(filteredAccounts.map((item) => item.id))}>
                  全选结果
                </Button>
                <Button size="small" onClick={() => setSelectedIds([])}>
                  清空
                </Button>
              </Space>
            )}
          >
            <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginBottom: 10, alignItems: 'center' }}>
              <Input.Search
                allowClear
                placeholder="搜索 UID、昵称、标签、游戏名、手机号、CK"
                value={searchText}
                onChange={(event) => setSearchText(event.target.value)}
                style={{ width: 320 }}
                prefix={<SearchOutlined />}
              />
              <Select
                allowClear
                placeholder="按标签筛选"
                value={tagFilter || undefined}
                onChange={(value) => setTagFilter(value || '')}
                options={tags.map((tag) => ({ value: tag, label: tag }))}
                style={{ width: 150 }}
              />
              <Text type="secondary">{filteredAccounts.length} / {accounts.length}</Text>
            </div>
            <Table
              rowSelection={{
                selectedRowKeys: selectedIds,
                onChange: (keys) => setSelectedIds(keys.map((key) => Number(key))),
              }}
              columns={accountColumns}
              dataSource={filteredAccounts}
              rowKey="id"
              loading={loading}
              size="small"
              pagination={{ pageSize: 12, showSizeChanger: true, showTotal: (total) => `共 ${total} 条` }}
              scroll={{ x: 1520 }}
              rowClassName={(record) => selectedIds.includes(record.id) ? 'ant-table-row-selected' : ''}
              onRow={(record) => ({
                onClick: (event) => handleAccountRowClick(record, event),
                onContextMenu: (event) => handleAccountRowContextMenu(record, event),
                style: { cursor: 'pointer' },
                title: '点击选中，右键打开账号动作',
              })}
            />
          </Card>

          <Row gutter={12} style={{ marginTop: 12 }}>
            <Col xs={24} xl={12}>
              <Card size="small" title={<Space><ShoppingOutlined />兑换商品</Space>}>
                <Table
                  columns={goodsColumns}
                  dataSource={filteredGoods}
                  rowKey="id"
                  size="small"
                  pagination={false}
                  scroll={{ y: 260 }}
                  locale={{ emptyText: '暂无兑换商品，请先刷新商品列表' }}
                  onRow={(record) => ({
                    onClick: () => setSelectedExchangeGoodsId(record.product_id),
                  })}
                  rowClassName={(record) => record.product_id === selectedExchangeGoodsId ? 'ant-table-row-selected' : ''}
                />
              </Card>
            </Col>
            <Col xs={24} xl={12}>
              <Card size="small" title={<Space><CreditCardOutlined />充值商品</Space>}>
                <Table
                  columns={rechargeGoodsColumns}
                  dataSource={sortedRechargeGoods}
                  rowKey="id"
                  size="small"
                  pagination={false}
                  scroll={{ y: 260 }}
                  locale={{ emptyText: '暂无充值商品，请先刷新充值商品列表' }}
                  onRow={(record) => ({
                    onClick: () => setSelectedRechargeGoodsId(record.spu_id),
                  })}
                  rowClassName={(record) => record.spu_id === selectedRechargeGoodsId ? 'ant-table-row-selected' : ''}
                />
              </Card>
            </Col>
          </Row>

          <Card size="small" title={<Space><FieldTimeOutlined />任务记录</Space>} style={{ marginTop: 12 }}>
            {taskSummary}
            {renderTaskTable(8)}
            <RealtimeLogPanel
              logs={logs}
              connected={wsConnected}
              title="虎牙实时日志"
              emptyText="暂无虎牙任务日志"
              collapsible
              spinWhenEmpty
              style={{ marginTop: 8 }}
            />
          </Card>
        </div>

        <div style={{ minHeight: 0, overflowY: 'auto', overflowX: 'hidden' }}>
          <Space direction="vertical" size={12} style={{ width: '100%' }}>
            <Card size="small" title="批量动作" extra={<Tag color={selectedIds.length ? 'blue' : 'default'}>{selectedAccountText}</Tag>}>
              <Space direction="vertical" size={10} style={{ width: '100%' }}>
                <InputNumber
                  min={1}
                  max={10}
                  value={concurrency}
                  onChange={(value) => setConcurrency(value || 1)}
                  addonBefore="并发"
                  style={{ width: '100%' }}
                />
                <Select
                  value={selectedTaskType}
                  onChange={setSelectedTaskType}
                  options={Object.entries(taskTypes).map(([value, label]) => ({ value, label }))}
                  style={{ width: '100%' }}
                />
                <Button
                  type="primary"
                  block
                  icon={<PlayCircleOutlined />}
                  loading={starting}
                  disabled={!canTask || selectedIds.length === 0 || wsConnected}
                  onClick={() => startTask()}
                >
                  创建任务
                </Button>
                <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
                  {QUICK_ACTIONS
                    .filter((item) => !['refresh_goods', 'refresh_recharge_goods', 'exchange_goods', 'create_recharge_order'].includes(item.key))
                    .map((item) => (
                      <Tooltip key={item.key} title={taskTypes[item.key] || item.key}>
                        <Button
                          icon={item.icon}
                          onClick={() => startTask(item.key)}
                          disabled={!canTask || selectedIds.length === 0 || wsConnected}
                        >
                          {taskTypes[item.key] || item.key}
                        </Button>
                      </Tooltip>
                    ))}
                </div>
              </Space>
            </Card>

            <Card
              size="small"
              title={<Space><ShoppingOutlined />兑换</Space>}
              extra={(
                <Button
                  size="small"
                  icon={<ReloadOutlined />}
                  onClick={() => startTask('refresh_goods')}
                  disabled={!canTask || selectedIds.length === 0 || wsConnected}
                >
                  刷新
                </Button>
              )}
            >
              <Space direction="vertical" size={10} style={{ width: '100%' }}>
                {goodsCategories.length > 0 ? (
                  <Select
                    value={selectedGoodsCategory}
                    onChange={setSelectedGoodsCategory}
                    options={goodsCategories.map((item) => ({
                      value: item.key,
                      label: `${item.label} (${item.count})`,
                    }))}
                    style={{ width: '100%' }}
                  />
                ) : null}
                <Select
                  showSearch
                  allowClear
                  placeholder="选择兑换商品"
                  value={selectedExchangeGoodsId || undefined}
                  onChange={(value) => setSelectedExchangeGoodsId(value || '')}
                  options={goodsOptions}
                  style={{ width: '100%' }}
                  filterOption={(input, option) => String(option?.label || '').toLowerCase().includes(input.toLowerCase())}
                />
                <Input
                  addonBefore="PID"
                  value={selectedExchangeGoodsId}
                  onChange={(event) => setSelectedExchangeGoodsId(event.target.value.trim())}
                  placeholder="商品 PID"
                />
                <DatePicker
                  showTime
                  allowClear
                  value={exchangeAt}
                  onChange={setExchangeAt}
                  placeholder="立即兑换"
                  style={{ width: '100%' }}
                />
                <Button
                  block
                  type="primary"
                  icon={<CheckCircleOutlined />}
                  onClick={() => startTask('exchange_goods')}
                  disabled={!canTask || selectedIds.length === 0 || !selectedExchangeGoodsId || wsConnected}
                >
                  批量兑换商品
                </Button>
              </Space>
            </Card>

            <Card
              size="small"
              title={<Space><CreditCardOutlined />充值支付</Space>}
              extra={(
                <Button
                  size="small"
                  icon={<ReloadOutlined />}
                  onClick={() => startTask('refresh_recharge_goods')}
                  disabled={!canTask || selectedIds.length === 0 || wsConnected}
                >
                  刷新
                </Button>
              )}
            >
              <Space direction="vertical" size={10} style={{ width: '100%' }}>
                <Select
                  showSearch
                  allowClear
                  placeholder="选择充值商品"
                  value={selectedRechargeGoodsId || undefined}
                  onChange={(value) => setSelectedRechargeGoodsId(value || '')}
                  options={rechargeGoodsOptions}
                  style={{ width: '100%' }}
                  filterOption={(input, option) => String(option?.label || '').toLowerCase().includes(input.toLowerCase())}
                />
                <InputNumber
                  min={1}
                  max={999}
                  value={rechargeCount}
                  onChange={(value) => setRechargeCount(value || 1)}
                  addonAfter="份"
                  style={{ width: '100%' }}
                />
                <Radio.Group value={rechargePayChannel} onChange={(event) => setRechargePayChannel(event.target.value)}>
                  <Radio value="Weixin">微信</Radio>
                  <Radio value="Zfb">支付宝</Radio>
                </Radio.Group>
                <Button
                  block
                  type="primary"
                  icon={<QrcodeOutlined />}
                  onClick={() => startTask('create_recharge_order')}
                  disabled={!canTask || selectedIds.length === 0 || !selectedRechargeGoodsId || wsConnected}
                >
                  生成支付二维码
                </Button>
              </Space>
            </Card>

            <Card
              size="small"
              title={<Space><SettingOutlined />配置</Space>}
              extra={canConfig && (
                <Button size="small" type="primary" onClick={saveConfig} loading={savingConfig}>
                  保存
                </Button>
              )}
            >
              <Form form={form} layout="vertical" disabled={!canConfig}>
                <Row gutter={8}>
                  <Col span={12}>
                    <Form.Item label="直播间 ID" name="room_pid">
                      <Input placeholder="1199650619883" />
                    </Form.Item>
                  </Col>
                  <Col span={12}>
                    <Form.Item label="SID" name="sid">
                      <Input placeholder="2203" />
                    </Form.Item>
                  </Col>
                  <Col span={12}>
                    <Form.Item label="兑换活动 ID" name="outer_act_id">
                      <Input placeholder="9504" />
                    </Form.Item>
                  </Col>
                  <Col span={12}>
                    <Form.Item label="绑定 bActId" name="bind_act_id">
                      <Input placeholder="9271" />
                    </Form.Item>
                  </Col>
                  <Col span={24}>
                    <Form.Item label="支付渠道" name="pay_channel">
                      <Select
                        options={[
                          { value: 'Weixin', label: '微信' },
                          { value: 'Zfb', label: '支付宝' },
                        ]}
                      />
                    </Form.Item>
                  </Col>
                </Row>
              </Form>
            </Card>
          </Space>
        </div>
      </div>

      <Modal
        title="兑换记录"
        open={!!exchangeRecordsTask}
        onCancel={() => setExchangeRecordsTask(null)}
        footer={null}
        width={760}
      >
        <Space direction="vertical" size={10} style={{ width: '100%' }}>
          <Text type="secondary">
            {exchangeRecordsAccountName}{exchangeRecordsResult?.sid ? ` / SID ${String(exchangeRecordsResult.sid)}` : ''}
          </Text>
          <Table
            columns={exchangeRecordColumns}
            dataSource={exchangeRecords}
            rowKey={(record) => String(record.record_id || record.order_id || record.index)}
            size="small"
            pagination={false}
            scroll={{ y: 360 }}
            locale={{ emptyText: '暂无兑换记录' }}
          />
        </Space>
      </Modal>

      <Modal
        title="绑定小程序码"
        open={!!qrTask}
        onCancel={() => setQrTask(null)}
        footer={null}
        width={360}
      >
        {qrTask && (
          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12, padding: '8px 0 12px' }}>
            <Text strong style={{ maxWidth: '100%', textAlign: 'center' }}>{qrAccountName}</Text>
            {qrImage ? (
              <div
                style={{
                  padding: 14,
                  borderRadius: 10,
                  background: '#fff',
                  lineHeight: 0,
                }}
              >
                <img
                  src={qrImage}
                  alt="绑定小程序码"
                  style={{
                    width: 260,
                    height: 260,
                    objectFit: 'contain',
                    display: 'block',
                    borderRadius: 4,
                    background: '#fff',
                  }}
                />
              </div>
            ) : (
              <Text type="secondary">暂无二维码</Text>
            )}
          </div>
        )}
      </Modal>

      <Modal
        title="支付二维码"
        open={!!payTask}
        onCancel={() => setPayTask(null)}
        footer={null}
        width={380}
      >
        {payTask && (
          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12, padding: '8px 0 12px' }}>
            <Space direction="vertical" size={2} style={{ width: '100%', textAlign: 'center' }}>
              <Text strong>{payProductName || '充值商品'}</Text>
              <Text type="secondary">
                {payChannelLabel || '支付'}{payAmountText ? ` / ${payAmountText}` : ''}{payAccountName ? ` / ${payAccountName}` : ''}
              </Text>
              {payStatusText ? (
                <Tag color={payStatus === 'timeout' ? 'orange' : 'processing'} style={{ alignSelf: 'center', marginInlineEnd: 0 }}>
                  {payStatusText}
                </Tag>
              ) : null}
              {typeof payOrderId === 'number' || typeof payOrderId === 'string' ? (
                <Text type="secondary" style={{ fontSize: 12 }}>订单号 {String(payOrderId)}</Text>
              ) : null}
            </Space>
            {payUrl ? (
              <div style={{ padding: 14, borderRadius: 10, background: '#fff', lineHeight: 0 }}>
                <QRCode value={payUrl} size={260} bordered={false} color="#000" bgColor="#fff" />
              </div>
            ) : (
              <Text type="secondary">暂无支付二维码</Text>
            )}
          </div>
        )}
      </Modal>
    </div>
  );
}
