// ============================================================
// TaskHistory — 任务记录列表（分页 + 列头筛选 + 排序 + 滚动）
//
// 筛选策略（混合模式）：
//   - 服务端筛选（传 API）：status、model_id、user_id（企业版）
//   - 客户端筛选（antd onFilter）：output_type、provider_name
//   - 服务端不支持日期查询，时间范围筛选暂用 sorter 排序替代
//   - 企业版才会在"用户"列显示筛选菜单
//
// 使用 antd Table onChange 统一管理 pagination + filters + sorter，
// 参考 table-test.tsx 示例 4 的模式。
// ============================================================

import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
import { Table, Tag, Typography, theme as antTheme, Popover, Checkbox, Button, Space, Pagination, Dropdown, message } from 'antd';
import type { ColumnsType, TableProps } from 'antd/es/table';
import type { FilterValue, SorterResult } from 'antd/es/table/interface';
import { HistoryOutlined, SearchOutlined, SettingOutlined } from '@ant-design/icons';

import { useTaskList, useTaskPolling } from '@/shared/hooks/use-api';
import type { ModelsListResponseBody } from '@/modules/home/controllers/model-api';
import { useAppContext } from '@/app/providers/AppProvider';
import { useHomeContext } from '@/modules/home/stores/HomeContext';
import { useTheme } from '@/app/providers/ThemeProvider';
import { on, off, EVENTS } from '@/shared/utils/bus';
import type {
   TaskInfoItemResponseBody,
   TaskListRequestParams,
   TaskStatusString,
} from '@/modules/home/controllers/task-api';
import { TaskStatusMap, OutputTypeMap } from '@/modules/home/controllers/task-api';
import { getTaskListMenuItems } from '../../controllers/menus';
import type { ContextMenuItems } from '@/shared/components/ContextMenu';

const { Text } = Typography;

// ---------- 辅助类型 ----------

/** antd Table onChange 中 filters 的类型 */
type TableFilters = Record<string, FilterValue | null>;

/** antd Table onChange 中 sorter 的类型（单列排序） */
interface TableSorter {
   field?: React.Key | readonly React.Key[];
   order?: 'ascend' | 'descend';
}

/** 表格参数（缓存 antd onChange 的结果，用于构建 API 请求） */
interface TableParams {
   pagination: { current: number; pageSize: number };
   filters: TableFilters;
   sorter: TableSorter;
}

// ---------- 常量 ----------

/** 状态筛选选项（来自 API 枚举） */
const STATUS_FILTERS = Object.entries(TaskStatusMap).map(([value, label]) => ({
   text: label,
   value,
}));

/** 输出类型筛选选项（从 OutputTypeMap 派生） */
const OUTPUT_TYPE_FILTERS = Object.entries(OutputTypeMap).map(([value, text]) => ({
   text,
   value,
}));

/** 状态标签配置（渲染用，含未来未知状态的降级处理） */
const STATUS_CONFIG: Record<string, { color: string; label: string }> = {
   pending: { color: 'blue', label: '排队中' },
   submitted: { color: 'cyan', label: '已提交' },
   processing: { color: 'orange', label: '处理中' },
   success: { color: 'green', label: '已完成' },
   failed: { color: 'red', label: '失败' },
};

/** 未知状态降级：后端新增状态时自动回退显示 key 原文 */
function resolveStatus(s: string): { color: string; label: string } {
   return STATUS_CONFIG[s] || { color: 'default', label: s };
}

// ---------- 列可见性持久化 ----------

/** localStorage 键名（遵循 ele-heixiu-* 规范） */
const COLUMN_VISIBILITY_KEY = 'ele-heixiu-table-columns';

/** 列元数据（定义所有可用列 + 默认可见性） */
interface ColumnMeta { key: string; title: string; defaultVisible: boolean }

const ALL_COLUMNS: ColumnMeta[] = [
   { key: 'id', title: '任务 ID', defaultVisible: true },
   { key: 'created_at', title: '创建时间', defaultVisible: true },
   { key: 'model_name', title: '模型', defaultVisible: true },
   { key: 'output_type', title: '输出类型', defaultVisible: true  },
   { key: 'status', title: '状态', defaultVisible: true },
   { key: 'prompt', title: '提示词', defaultVisible: true },
   { key: 'duration', title: '时长', defaultVisible: true },
   { key: 'cost_cent', title: '费用', defaultVisible: true },
   { key: 'user_display_name', title: '用户', defaultVisible: true },
   { key: 'provider_name', title: '供应商', defaultVisible: false },
   { key: 'fps', title: '帧率', defaultVisible: false },
   { key: 'file_size', title: '文件大小', defaultVisible: false },
   { key: 'tags', title: '标签', defaultVisible: false },
   { key: 'error_message', title: '错误信息', defaultVisible: false },
];

function readStoredColumns(): Set<string> | null {
   try {
      const raw = localStorage.getItem(COLUMN_VISIBILITY_KEY);
      if (raw) {
         const arr: unknown = JSON.parse(raw);
         if (Array.isArray(arr) && arr.every((v) => typeof v === 'string')) return new Set(arr);
      }
   } catch { /* ignore */ }
   return null;
}

function writeStoredColumns(visible: Set<string>): void {
   try { localStorage.setItem(COLUMN_VISIBILITY_KEY, JSON.stringify([...visible])); } catch { /* ignore */ }
}

// ---------- 工具函数 ----------

/** 从 filters 中提取单值筛选（如 status 单选） */
function pickOne(filters: TableFilters, key: string): string | undefined {
   const v = filters[key];
   if (Array.isArray(v) && v.length > 0) return v[0] as string;
   return undefined;
}

/** 从 filters 中提取多值筛选（如 model_ids、user_ids） */
function pickMany(filters: TableFilters, key: string): string[] | undefined {
   const v = filters[key];
   if (Array.isArray(v) && v.length > 0) return v as string[];
   return undefined;
}

// ---------- 组件 ----------

interface TaskHistoryProps {
   /** 模型全量数据，由父组件 LeftPanel 传入（避免兄弟组件各自请求） */
   allModels: ModelsListResponseBody[] | null;
}

export function TaskHistory({ allModels }: TaskHistoryProps) {
   const { token } = antTheme.useToken();
   const { edition } = useAppContext();
   const {
      selectedTask,
      setSelectedTask,
      taskPage,
      taskPageSize,
      setTaskPage,
      setTaskPageSize,
   } = useHomeContext();

   const isEnterprise = edition === 'enterprise';

   // -------- 右键菜单 --------

   const [contextMenu, setContextMenu] = useState<{
       record: TaskInfoItemResponseBody;
       x: number;
       y: number;
   } | null>(null);

   /** ContextMenuItems → AntD MenuProps['items'] 转换 */
   const toAntdMenu = useCallback((items: ContextMenuItems) =>
       items.map((item) => {
           if (item === 'divider') return { type: 'divider' as const };
           return {
               key: item.key,
               label: item.label,
               onClick: item.onClick,
               disabled: item.disabled,
               danger: item.danger,
           };
       }), []);

   /** 根据任务记录生成右键菜单项 */
   const buildTaskMenuItems = useCallback((record: TaskInfoItemResponseBody) => {
       return toAntdMenu(getTaskListMenuItems({
           canOpenOutput: record.status === 'success',
           canReedit: true,
           canRegen: true,
           canPullResult: record.status === 'success',
           canRedownload: record.status === 'success',
           isFavorited: false,
           onOpenOutputDir: () => { message.info('打开输出目录功能待实现'); },
           onCopyResourceLink: () => {
               const assets = record.output_assets;
               if (assets && assets.length > 0) {
                   navigator.clipboard.writeText(assets[0].url).then(
                       () => message.success('已复制资源链接'),
                       () => message.error('复制失败'),
                   );
               } else {
                   message.warning('该任务无输出资源');
               }
           },
           onReedit: () => { message.info('重新编辑功能待实现'); },
           onRegen: () => { message.info('再次生成功能待实现'); },
           onPullResult: () => { message.info('拉取结果功能待实现'); },
           onRedownload: () => { message.info('重新下载功能待实现'); },
           onToggleFavorite: () => { message.info('收藏功能待实现'); },
           onDelete: () => { message.info('删除记录功能待实现'); },
       }));
   }, [toAntdMenu, message]);

   // 菜单打开时监听全局 mousedown → 点击菜单外区域自动关闭
   useEffect(() => {
       if (!contextMenu) return;
       const handler = (e: MouseEvent) => {
           const menuEl = document.querySelector('.ant-dropdown');
           if (menuEl && !menuEl.contains(e.target as Node)) {
               setContextMenu(null);
           }
       };
       // setTimeout 0 避免捕获打开菜单的同一次右击 mousedown
       const timer = setTimeout(() => {
           document.addEventListener('mousedown', handler, true);
       }, 0);
       return () => {
           clearTimeout(timer);
           document.removeEventListener('mousedown', handler, true);
       };
   }, [contextMenu]);

   // -------- 列可见性 --------

   const [visibleColumns, setVisibleColumns] = useState<Set<string>>(() => {
      const stored = readStoredColumns();
      if (stored) return stored;
      return new Set(ALL_COLUMNS.filter((c) => c.defaultVisible).map((c) => c.key));
   });

   const handleColumnToggle = useCallback((key: string, checked: boolean) => {
      setVisibleColumns((prev) => {
         const next = new Set(prev);
         if (checked) next.add(key); else next.delete(key);
         writeStoredColumns(next);
         return next;
      });
   }, []);

   // -------- 主题感知斑马纹 / hover / 选中行 CSS --------

   const { isDark } = useTheme();

   const tableCss = useMemo(() => {
      const searchIconColor = token.colorPrimary;
      if (isDark) {
         return `
            .task-table .task-row-odd > td { background: rgba(255,255,255,0.02) !important; }
            .task-table .task-row-even > td { background: transparent !important; }
            .task-table .task-row-selected > td { background: ${token.blue}1A !important; }
            .task-table .task-row-selected.task-row-odd > td { background: ${token.blue}22 !important; }
            .task-table .ant-table-row:hover > td { background: rgba(255,255,255,0.05) !important; }
            .task-table .task-row-selected:hover > td { background: ${token.blue}28 !important; }
            .task-table .prompt-search-icon.filtered { color: ${searchIconColor} !important; }
            .task-table .ant-table-body { overflow-x: hidden !important; }
            .task-table .ant-table-thead > tr > th { font-weight: normal !important; }
            /* 纵向滚动条（表格 body） */
            .task-table .ant-table-body::-webkit-scrollbar { width: 6px; }
            .task-table .ant-table-body::-webkit-scrollbar-track { background: transparent; }
            .task-table .ant-table-body::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); border-radius: 3px; }
            .task-table .ant-table-body::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.35); }
            /* 横向滚动条（固定面板） */
            .task-hscroll::-webkit-scrollbar { height: 6px; }
            .task-hscroll::-webkit-scrollbar-track { background: transparent; }
            .task-hscroll::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); border-radius: 3px; }
            .task-hscroll::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.35); }
         `;
      }
      return `
         .task-table .task-row-odd > td { background: #fafafa !important; }
         .task-table .task-row-even > td { background: #ffffff !important; }
         .task-table .task-row-selected > td { background: #e6f4ff !important; }
         .task-table .task-row-selected.task-row-odd > td { background: #dceeff !important; }
         .task-table .ant-table-row:hover > td { background: #f0f5ff !important; }
         .task-table .task-row-selected:hover > td { background: #d6ebff !important; }
         .task-table .prompt-search-icon.filtered { color: ${searchIconColor} !important; }
         .task-table .ant-table-body { overflow-x: hidden !important; }
         .task-table .ant-table-thead > tr > th { font-weight: normal !important; }
         /* 纵向滚动条（表格 body） */
         .task-table .ant-table-body::-webkit-scrollbar { width: 6px; }
         .task-table .ant-table-body::-webkit-scrollbar-track { background: transparent; }
         .task-table .ant-table-body::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.15); border-radius: 3px; }
         .task-table .ant-table-body::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,0.25); }
         /* 横向滚动条（固定面板） */
         .task-hscroll::-webkit-scrollbar { height: 6px; }
         .task-hscroll::-webkit-scrollbar-track { background: transparent; }
         .task-hscroll::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.15); border-radius: 3px; }
         .task-hscroll::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,0.25); }
      `;
   }, [isDark, token.blue, token.colorPrimary]);

   // -------- antd Table onChange 参数缓存 --------

   const [tableParams, setTableParams] = useState<TableParams>({
      pagination: { current: taskPage, pageSize: taskPageSize },
      filters: {},
      sorter: {},
   });

   // 同步 context 分页 ↔ tableParams
   useEffect(() => {
      setTableParams((prev:TableParams) => ({
         ...prev,
         pagination: { current: taskPage, pageSize: taskPageSize },
      }));
   }, [taskPage, taskPageSize]);

   // -------- 模型列表（用于模型列筛选选项 + 模型名→ID 映射，由父组件传入）--------

   const modelFilters:{text:string, value:string}[] = useMemo(() => {
      if (!allModels) return [];
      return allModels.map((m) => ({ text: m.name, value: m.id }));
   }, [allModels]);

   // -------- 供应商列表（从模型数据中提取）--------

   const providerFilters = useMemo(() => {
      if (!allModels) return [];
      const seen = new Set<string>();
      allModels.forEach((m) => {
         if (m.provider_name) seen.add(m.provider_name);
      });
      return Array.from(seen).sort().map((name) => ({ text: name, value: name }));
   }, [allModels]);

   // -------- 用户列表（从当前任务数据中提取，仅企业版使用）--------
   // TODO: 替换为独立的用户列表 API

   const [userFilters, setUserFilters] = useState<Array<{ text: string; value: string }>>([]);

   // -------- 构建 API 请求参数 --------

   const requestParams = useMemo<TaskListRequestParams>(() => {
      const req: TaskListRequestParams = {
         page: tableParams.pagination.current,
         page_size: tableParams.pagination.pageSize,
      };
      const { filters } = tableParams;

      // 服务端筛选字段
      const status = pickOne(filters, 'status');
      if (status) req.status = status as TaskStatusString;

      const modelIds = pickMany(filters, 'model_name');
      if (modelIds) req.model_ids = modelIds;

      // user_ids：仅企业版传入
      if (isEnterprise) {
         const userIds = pickMany(filters, 'user_id');
         if (userIds) req.user_ids = userIds;
      }

      return req;
   }, [tableParams, isEnterprise]);

   // -------- 数据获取 --------

   const [refreshTick, setRefreshTick] = useState(0);
   const handleTaskSubmitted = useCallback(() => {
      setRefreshTick((t) => t + 1);
   }, []);

   useEffect(() => {
      on(EVENTS.TASK_SUBMITTED, handleTaskSubmitted);
      return () => { off(EVENTS.TASK_SUBMITTED, handleTaskSubmitted); };
   }, [handleTaskSubmitted]);

   const {
      data: taskData,
      loading,
      errorMessage,
   } = useTaskList({
      requestParams,
      refreshSignal: refreshTick,
   });

   const tasks = taskData?.items ?? [];

   // 批量轮询非终态任务状态
   const handleTaskTerminal = useCallback(() => {
      setRefreshTick((t) => t + 1);
   }, []);
   const polledTasks = useTaskPolling(tasks, handleTaskTerminal);

   // 轮询更新后同步选中的任务到 Context
   useEffect(() => {
      if (!selectedTask || polledTasks.length === 0) return;
      const updated = polledTasks.find(t => t.id === selectedTask.id);
      if (updated && updated.status !== selectedTask.status) {
         setSelectedTask(updated);
      }
   }, [polledTasks, selectedTask, setSelectedTask]);

   const total = taskData?.total ?? 0;

   // 从任务数据中提取用户列表（企业版筛选用）
   useEffect(() => {
      if (!isEnterprise || !taskData?.items) return;
      const seen = new Set<string>();
      taskData.items.forEach((t) => {
         if (t.user_id && t.user_display_name) {
            seen.add(JSON.stringify({ text: t.user_display_name, value: t.user_id }));
         }
      });
      const newList = Array.from(seen).map((s) => JSON.parse(s) as { text: string; value: string });
      setUserFilters((prev) => {
         if (prev.length === newList.length) return prev; // 避免不必要的重渲染
         return newList;
      });
   }, [isEnterprise, taskData]);

   // -------- antd Table onChange --------

   const handleTableChange: TableProps<TaskInfoItemResponseBody>['onChange'] = (
      _pagination,
      filters,
      sorter,
   ) => {
      // 缓存筛选和排序参数（分页由独立 Pagination 组件管理）
      const s = sorter as SorterResult<TaskInfoItemResponseBody>;
      setTableParams((prev) => ({
         ...prev,
         filters: filters as TableFilters,
         sorter: !Array.isArray(s) && s.field
            ? { field: s.field, order: s.order as 'ascend' | 'descend' | undefined }
            : {},
      }));
   };

   // -------- 表格列定义（含列头筛选/排序 + 可见性过滤）--------
   //
   // 列顺序：任务 ID(固定) → 创建时间 → 模型 → 输出类型 → 状态 → 提示词 →
   //         时长(视频) → 费用 → 供应商 → 帧率/文件大小/标签/错误信息 → 用户(企业版)
   //
   // 可见性：通过标题栏齿轮图标控制，持久化到 localStorage
   //
   // ⚠️ 重要：columns 依赖中不出 token.colorPrimary，否则主题切换时 columns 重新生成
   //   会导致 Table 内部状态重置（页码、筛选、排序等丢失）。
   //   主题色通过 searchIconColorRef 传递，不触发 columns 重建。

   /** 提示词搜索图标颜色（通过 CSS class 承载，避免 columns 依赖 token 导致主题切换重置状态） */

   /** 提取视频时长（秒），非视频输出类型返回 null → 渲染 "-" */
   function getVideoDuration(record: TaskInfoItemResponseBody): number | null {
      if (record.output_type === 'video') {
         return record.ui_params.duration ?? null;
      }
      return null;
   }

   /** 格式化时长：<60s 显示秒，≥60s 显示分秒 */
   function formatDuration(seconds: number): string {
      if (seconds < 60) return `${seconds} 秒`;
      const m = Math.floor(seconds / 60);
      const s = seconds % 60;
      return s > 0 ? `${m} 分 ${s} 秒` : `${m} 分钟`;
   }

   const allColumns = useMemo<ColumnsType<TaskInfoItemResponseBody>>(() => {
      const cols: ColumnsType<TaskInfoItemResponseBody> = [
         // ---- 任务 ID（固定列） ----
         {
            title: '任务 ID',
            dataIndex: 'id',
            key: 'id',
            width: 140,
            ellipsis: true,
            fixed: 'left',
            render: (id: string) => (
               <Text copyable={{ text: id }} style={{ fontSize: 12, fontFamily: 'monospace' }}>
                  {id.slice(0, 10)}...
               </Text>
            ),
         },
         // ---- 创建时间 ----
         {
            title: '创建时间',
            dataIndex: 'created_at',
            key: 'created_at',
            width: 155,
            sorter: (a, b) => {
               const da = a.created_at ? new Date(a.created_at).getTime() : 0;
               const db = b.created_at ? new Date(b.created_at).getTime() : 0;
               return da - db;
            },
            sortOrder: tableParams.sorter.field === 'created_at' ? tableParams.sorter.order : null,
            render: (v: Date) => (
               <Text style={{ fontSize: 11 }}>
                  {v ? new Date(v).toLocaleString('zh-CN', {
                     month: '2-digit', day: '2-digit',
                     hour: '2-digit', minute: '2-digit', second: '2-digit',
                  }) : '-'}
               </Text>
            ),
         },
         // ---- 模型 ----
         {
            title: '模型',
            dataIndex: 'model_name',
            key: 'model_name',
            width: 130,
            ellipsis: true,
            filters: modelFilters,
            filteredValue: (tableParams.filters.model_name as string[]) || null,
            onFilter: () => true, // 服务端筛选
            filterSearch: true,
            filterMode: 'menu' as const,
         },
         // ---- 输出类型 ----
         {
            title: '输出类型',
            dataIndex: 'output_type',
            key: 'output_type',
            width: 120,
            filters: OUTPUT_TYPE_FILTERS,
            filteredValue: (tableParams.filters.output_type as string[]) || null,
            onFilter: (value, record) => record.output_type === value, // 客户端筛选
            render: (t: string) => (
               <Tag style={{ fontSize: 10 }}>
                  {OutputTypeMap[t as keyof typeof OutputTypeMap] || t}
               </Tag>
            ),
         },
         // ---- 状态 ----
         {
            title: '状态',
            dataIndex: 'status',
            key: 'status',
            width: 80,
            filters: STATUS_FILTERS,
            filteredValue: (tableParams.filters.status as string[]) || null,
            onFilter: () => true, // 服务端筛选
            render: (s: string) => {
               const cfg = resolveStatus(s);
               return <Tag color={cfg.color} style={{ fontSize: 10 }}>{cfg.label}</Tag>;
            },
         },
         // ---- 提示词 ----
         {
            title: '提示词',
            dataIndex: 'prompt',
            key: 'prompt',
            width: 160,
            ellipsis: true,
            filterIcon: (filtered: boolean) => (
               <SearchOutlined className={`prompt-search-icon${filtered ? ' filtered' : ''}`} style={{ fontSize: 12 }} />
            ),
            filterDropdown: undefined, // TODO: 后续加自定义搜索框
            render: (p: string | undefined) => (
               <Text style={{ fontSize: 11 }} title={p}>{p || '-'}</Text>
            ),
         },
         // ---- 时长（仅视频有数据，否则 "-"） ----
         {
            title: '时长',
            key: 'duration',
            width: 75,
            sorter: (a, b) => {
               const da = getVideoDuration(a) ?? -1;
               const db = getVideoDuration(b) ?? -1;
               return da - db;
            },
            sortOrder: tableParams.sorter.field === 'duration' ? tableParams.sorter.order : null,
            render: (_: unknown, record: TaskInfoItemResponseBody) => {
               const sec = getVideoDuration(record);
               return <Text style={{ fontSize: 11 }}>{sec !== null ? formatDuration(sec) : '-'}</Text>;
            },
         },
         // ---- 费用 ----
         {
            title: '费用',
            dataIndex: 'cost_cent',
            key: 'cost_cent',
            width: 75,
            sorter: (a, b) => a.cost_cent - b.cost_cent,
            sortOrder: tableParams.sorter.field === 'cost_cent' ? tableParams.sorter.order : null,
            render: (c: number) => (
               <Text style={{ fontSize: 11 }}>¥{(c / 100).toFixed(2)}</Text>
            ),
         },
         // ---- 供应商（默认隐藏） ----
         {
            title: '供应商',
            dataIndex: 'provider_name',
            key: 'provider_name',
            width: 100,
            ellipsis: true,
            filters: providerFilters,
            filteredValue: (tableParams.filters.provider_name as string[]) || null,
            onFilter: (value, record) => record.provider_name === value, // 客户端筛选
            filterSearch: true,
         },
         // ---- 帧率（占位列，待后端支持） ----
         {
            title: '帧率',
            key: 'fps',
            width: 70,
            render: () => <Text type="secondary" style={{ fontSize: 11 }}>-</Text>,
         },
         // ---- 文件大小（占位列，待后端支持） ----
         {
            title: '文件大小',
            key: 'file_size',
            width: 85,
            render: () => <Text type="secondary" style={{ fontSize: 11 }}>-</Text>,
         },
         // ---- 标签 ----
         {
            title: '标签',
            dataIndex: 'tags',
            key: 'tags',
            width: 100,
            ellipsis: true,
            render: (tags: Array<string | null>) => {
               const filtered = tags?.filter((t): t is string => t !== null) ?? [];
               if (filtered.length === 0) return <Text type="secondary" style={{ fontSize: 11 }}>-</Text>;
               return (
                  <Space size={2} wrap>
                     {filtered.slice(0, 2).map((t) => (
                        <Tag key={t} style={{ fontSize: 9, lineHeight: '14px', margin: 0 }}>{t}</Tag>
                     ))}
                     {filtered.length > 2 && (
                        <Text type="secondary" style={{ fontSize: 9 }}>+{filtered.length - 2}</Text>
                     )}
                  </Space>
               );
            },
         },
         // ---- 错误信息（失败任务时展示） ----
         {
            title: '错误信息',
            dataIndex: 'error_message',
            key: 'error_message',
            width: 140,
            ellipsis: true,
            render: (msg: string) => msg
               ? <Text type="danger" style={{ fontSize: 11 }} title={msg}>{msg}</Text>
               : <Text type="secondary" style={{ fontSize: 11 }}>-</Text>,
         },
      ];

      // 用户列：仅企业版可筛选
      if (isEnterprise) {
         cols.push({
            title: '用户',
            dataIndex: 'user_display_name',
            key: 'user_id',
            width: 100,
            ellipsis: true,
            filters: userFilters,
            filteredValue: (tableParams.filters.user_id as string[]) || null,
            onFilter: () => true, // 服务端筛选（传 user_ids）
            render: (n: string | null) => (
               <Text style={{ fontSize: 11 }}>{n || '-'}</Text>
            ),
         });
      } else {
         cols.push({
            title: '用户',
            dataIndex: 'user_display_name',
            key: 'user_id',
            width: 100,
            ellipsis: true,
            render: (n: string | null) => (
               <Text style={{ fontSize: 11 }}>{n || '-'}</Text>
            ),
         });
      }

      return cols;
   }, [
      modelFilters,
      providerFilters,
      userFilters,
      tableParams.filters,
      tableParams.sorter,
      isEnterprise,
   ]);

   // 按 visibleColumns 过滤实际展示的列
   const columns = useMemo(
      () => allColumns.filter((c) => visibleColumns.has(c.key as string)),
      [allColumns, visibleColumns],
   );

   // -------- 表格高度 --------
   //
   // 核心策略：分页器 + 水平滚动条均已独立到表格 wrapper 之外，
   // 因此 scroll.y 使用 wrapper 完整高度（仅减去 thead）。

   const scrollX = isEnterprise ? 1350 : 1250;
   const tableWrapperRef = useRef<HTMLDivElement>(null);
   const hScrollRef = useRef<HTMLDivElement>(null);
   const [tableBodyHeight, setTableBodyHeight] = useState(400);

   useEffect(() => {
      const wrapper = tableWrapperRef.current;
      if (!wrapper) return;

      const calcHeight = () => {
         const thead = wrapper.querySelector('.ant-table-thead') as HTMLElement | null;
         const theadH = thead?.offsetHeight ?? 0;
         const available = wrapper.clientHeight - theadH;
         setTableBodyHeight(Math.max(120, available));
      };

      // ResizeObserver：容器尺寸变化（窗口拉伸、面板拖拽）
      const resizeObserver = new ResizeObserver(calcHeight);
      resizeObserver.observe(wrapper);

      // MutationObserver：内部 DOM 变化（thead 高度变化如筛选菜单弹出、
      // 数据加载后水平滚动条出现等）
      const mutationObserver = new MutationObserver(calcHeight);
      mutationObserver.observe(wrapper, { childList: true, subtree: true });

      calcHeight();
      return () => {
         resizeObserver.disconnect();
         mutationObserver.disconnect();
      };
   }, [taskPageSize]);

   // -------- 固定水平滚动条 ↔ 表格 body 双向同步 --------
   //
   // .ant-table-body 原生水平滚动条被 CSS 隐藏（overflow-x: hidden），
   // 改为由独立的固定水平滚动条控制；两者通过 scroll 事件双向同步。

   useEffect(() => {
      const wrapper = tableWrapperRef.current;
      const hScroll = hScrollRef.current;
      if (!wrapper || !hScroll) return;

      const body = wrapper.querySelector('.ant-table-body') as HTMLElement | null;
      if (!body) return;

      const syncBodyToHScroll = () => {
         hScroll.scrollLeft = body.scrollLeft;
      };
      const syncHScrollToBody = () => {
         body.scrollLeft = hScroll.scrollLeft;
      };

      body.addEventListener('scroll', syncBodyToHScroll);
      hScroll.addEventListener('scroll', syncHScrollToBody);
      syncBodyToHScroll();

      // 数据变化（加载/筛选/翻页）后重同步位置
      const observer = new MutationObserver(syncBodyToHScroll);
      observer.observe(body, { childList: true, subtree: true });

      return () => {
         body.removeEventListener('scroll', syncBodyToHScroll);
         hScroll.removeEventListener('scroll', syncHScrollToBody);
         observer.disconnect();
      };
   }, [polledTasks, tableBodyHeight]);

   // -------- 行样式：斑马纹 + 选中行 --------

   const rowClassName = (_record: TaskInfoItemResponseBody, index: number) => {
      const base = index % 2 === 1 ? 'task-row-odd' : 'task-row-even';
      return selectedTask?.id === _record.id ? `${base} task-row-selected` : base;
   };

   // -------- 是否有活跃筛选 --------

   const hasActiveFilters = Object.values(tableParams.filters).some(
      (v) => Array.isArray(v) && v.length > 0,
   );

   // ======== 渲染 ========

   return (
      <div style={{ flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden', minHeight: 0 }}>
         {/* 标题栏 — 左侧色条 + 背景，视觉权重高于表格列头 */}
         <div
            style={{
               padding: '8px 12px 8px 9px',
               borderLeft: `3px solid ${token.colorPrimary}`,
               background: token.colorFillSecondary,
               display: 'flex',
               alignItems: 'center',
               gap: 6,
               flexShrink: 0,
            }}
         >
            <HistoryOutlined style={{ fontSize: 14, color: token.colorPrimary }} />
            <Text strong style={{ fontSize: 13 }}>任务记录</Text>
            {total > 0 && (
               <Text type="secondary" style={{ fontSize: 11 }}>共 {total} 条</Text>
            )}
            {hasActiveFilters && (
               <Tag color="blue" style={{ fontSize: 10, lineHeight: '16px', padding: '0 4px' }}>
                  已筛选
               </Tag>
            )}
            {errorMessage && (
               <Text type="danger" style={{ fontSize: 11, marginLeft: 'auto' }}>{errorMessage}</Text>
            )}

            {/* 列可见性控制 — 齿轮图标 */}
            <Popover
               trigger="click"
               placement="bottomRight"
               title="显示/隐藏列"
               content={
                  <Checkbox.Group
                     value={[...visibleColumns]}
                     style={{ display: 'flex', flexDirection: 'column', gap: 2 }}
                  >
                     {ALL_COLUMNS.map((col) => (
                        <Checkbox
                           key={col.key}
                           value={col.key}
                           disabled={col.key === 'id'}
                           onChange={(e) => handleColumnToggle(col.key, e.target.checked)}
                        >
                           {col.title}
                        </Checkbox>
                     ))}
                  </Checkbox.Group>
               }
            >
               <Button
                  type="text"
                  size="small"
                  icon={<SettingOutlined style={{ fontSize: 13 }} />}
                  style={{ marginLeft: 'auto' }}
                  title="列设置"
               />
            </Popover>
         </div>

         {/* 固定水平滚动条 — 在表格上方，始终可见，与表格 body 双向同步 */}
         <div
            ref={hScrollRef}
            className="task-hscroll"
            style={{
               flexShrink: 0,
               overflowX: 'auto',
               overflowY: 'hidden',
               height: 8,
            }}
         >
            <div style={{ width: scrollX, height: 1 }} />
         </div>

         {/* 表格 — 纵向滚动由 scroll.y 控制，横向滚动由上方固定滚动条控制 */}
         <div ref={tableWrapperRef} style={{ flex: 1, overflow: 'hidden', minHeight: 0 }}>
            <style>{tableCss}</style>
            <Table<TaskInfoItemResponseBody>
               className="task-table"
               columns={columns}
               dataSource={polledTasks}
               rowKey="id"
               size="small"
               loading={loading}
               showHeader={true}
               scroll={{ x: scrollX, y: tableBodyHeight }}
               rowClassName={rowClassName}
               onRow={(record) => ({
                  onClick: () => setSelectedTask(record),
                  onContextMenu: (e) => {
                      e.preventDefault();
                      setSelectedTask(record);
                      setContextMenu({ record, x: e.clientX, y: e.clientY });
                  },
                  style: { cursor: 'pointer' },
               })}
               onChange={handleTableChange}
               pagination={false}
               locale={{ emptyText: '暂无任务记录', filterReset: '重置', filterConfirm: '确定' }}
            />
         </div>

         {/* 独立分页器 — 固定在底部，不随表格数据量移动 */}
         <div style={{ flexShrink: 0, display: 'flex', justifyContent: 'center', padding: '4px 0 0', borderTop: `1px solid ${token.colorBorderSecondary}` }}>
            <Pagination
               current={tableParams.pagination.current}
               pageSize={tableParams.pagination.pageSize}
               total={total}
               size="small"
               showSizeChanger
               pageSizeOptions={[10, 20, 50]}
               onChange={(page, pageSize) => {
                  if (page !== taskPage) setTaskPage(page);
                  if (pageSize !== taskPageSize) setTaskPageSize(pageSize);
                  setTableParams((prev) => ({
                     ...prev,
                     pagination: { current: page, pageSize },
                  }));
               }}
            />
         </div>

         {/* 悬浮右键菜单 — 手动控制 open，trigger=[] 避免 AntD 自动监听干扰 onRow.onContextMenu */}
         <Dropdown
            open={!!contextMenu}
            onOpenChange={(open) => { if (!open) setContextMenu(null); }}
            menu={{
                items: contextMenu ? buildTaskMenuItems(contextMenu.record) : [],
                onClick: () => setContextMenu(null),
            }}
            trigger={[]}
         >
            <div
                onContextMenu={(e) => { e.preventDefault(); setContextMenu(null); }}
                style={{
                    position: 'fixed',
                    left: contextMenu?.x ?? -9999,
                    top: contextMenu?.y ?? -9999,
                    width: 1,
                    height: 1,
                }}
            />
         </Dropdown>
      </div>
   );
}
