/**
 * 开发调试全链路日志页面。
 *
 * - 顶部：覆盖率状态栏 + 筛选栏
 * - 左侧：请求列表（Table，分页）
 * - 右侧：选中请求的 span 链路树
 * - 设置面板：Drawer（日志开关、保留天数、清理等）
 */

import React, { useEffect, useState, useCallback } from "react";
import {
  Table, Tag, Alert, Button, Select, Input, InputNumber,
  Checkbox, DatePicker, TimePicker, Space, Typography, Row, Col,
  Drawer, Switch, message, Spin, Tooltip, Divider, Progress,
} from "antd";
import {
  SettingOutlined, ReloadOutlined, SearchOutlined,
  DeleteOutlined, ScanOutlined,
} from "@ant-design/icons";
import type { ColumnsType } from "antd/es/table";
import dayjs from "dayjs";
import {
  fetchDates, fetchRequests, fetchRequestDetail,
  fetchCoverage, triggerCoverageScan,
  fetchSettings, updateSettings, cleanupLogs,
} from "../api/devTrace";
import type {
  TraceRequest, TraceDetail, TraceSpan, TraceSettings,
  TraceFilter, TraceCoverage, SpanType, TraceType, CoverageCategory,
} from "../types/devTrace";

const { Title, Text } = Typography;

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

const SPAN_TYPE_OPTIONS: SpanType[] = [
  "HTTP_IN", "AUTH", "ROUTE", "SERVICE",
  "DB_QUERY", "DB_CONN", "DB_CONN_RELEASE",
  "HTTP_OUT", "ERROR", "DB_ERROR",
  "MIDDLEWARE", "MIDDLEWARE_ERROR",
  "SSE_START", "SSE_EVENT", "SSE_END",
  "AI_CALL", "AI_STREAM", "AI_ERROR",
  "WS_CONNECT", "WS_MESSAGE", "WS_DISCONNECT",
  "JOB_START", "JOB_END", "JOB_ERROR",
];

const TRACE_TYPE_OPTIONS: { label: string; value: TraceType }[] = [
  { label: "HTTP", value: "http" },
  { label: "SSE", value: "sse" },
  { label: "WebSocket", value: "ws" },
  { label: "Job", value: "job" },
];

const METHOD_OPTIONS = ["GET", "POST", "PUT", "DELETE", "PATCH"];

/** span_type → 颜色映射 */
const SPAN_COLOR: Record<string, string> = {
  HTTP_IN: "#1890ff", HTTP_OUT: "#1890ff",
  AUTH: "#fa8c16",
  ROUTE: "#1890ff",
  SERVICE: "#52c41a",
  DB_QUERY: "#722ed1", DB_CONN: "#722ed1", DB_CONN_RELEASE: "#722ed1",
  DB_ERROR: "#f5222d",
  ERROR: "#f5222d",
  MIDDLEWARE: "#8c8c8c", MIDDLEWARE_ERROR: "#f5222d",
  SSE_START: "#13c2c2", SSE_EVENT: "#13c2c2", SSE_END: "#13c2c2",
  AI_CALL: "#2f54eb", AI_STREAM: "#2f54eb", AI_ERROR: "#f5222d",
  WS_CONNECT: "#faad14", WS_MESSAGE: "#faad14", WS_DISCONNECT: "#faad14",
  JOB_START: "#8c8c8c", JOB_END: "#8c8c8c", JOB_ERROR: "#f5222d",
};

const ERROR_SPAN_TYPES = new Set(["ERROR", "DB_ERROR", "MIDDLEWARE_ERROR", "AI_ERROR", "JOB_ERROR"]);

// ---- 辅助函数 ----

function fmtTime(raw: string | null | undefined): string {
  if (!raw) return "—";
  const d = new Date(raw);
  return Number.isNaN(d.getTime()) ? raw : d.toLocaleString("zh-CN");
}

function fmtDuration(ms: number): string {
  if (ms < 1000) return `${ms.toFixed(0)}ms`;
  return `${(ms / 1000).toFixed(2)}s`;
}

function coveragePct(cat: CoverageCategory): number {
  return cat.total === 0 ? 100 : Math.round((cat.covered / cat.total) * 100);
}

// ---- 覆盖率状态栏 ----

const CoverageBar: React.FC<{
  coverage: TraceCoverage | null;
  loading: boolean;
  onScan: () => void;
}> = ({ coverage, loading, onScan }) => {
  if (!coverage) {
    return (
      <Alert
        type="info"
        showIcon
        style={{ marginBottom: 12 }}
        message={
          <Row align="middle" gutter={16}>
            <Col>
              <Text strong>Trace 覆盖率</Text>
            </Col>
            <Col>
              <Text type="secondary">暂无扫描数据</Text>
            </Col>
            <Col>
              <Button
                size="small" icon={<ScanOutlined />}
                loading={loading} onClick={onScan}
              >
                扫描
              </Button>
            </Col>
          </Row>
        }
      />
    );
  }

  const dims: { label: string; cat: CoverageCategory }[] = [
    { label: "路由", cat: coverage.routes },
    { label: "Service", cat: coverage.services },
    { label: "Job", cat: coverage.jobs },
    { label: "SSE", cat: coverage.sse_endpoints },
    { label: "WS", cat: coverage.ws_endpoints },
  ];

  const allUncovered = dims.flatMap((d) =>
    d.cat.uncovered.map((name) => `${d.label}: ${name}`),
  );

  return (
    <Alert
      type={allUncovered.length === 0 ? "success" : "warning"}
      showIcon
      style={{ marginBottom: 12 }}
      message={
        <Row align="middle" gutter={16}>
          <Col>
            <Text strong>Trace 覆盖率</Text>
          </Col>
          {dims.map((d) => (
            <Col key={d.label}>
              <Space size={4}>
                <Text type="secondary">{d.label}</Text>
                <Progress
                  type="circle" size={28}
                  percent={coveragePct(d.cat)}
                  format={(p) => `${p}%`}
                />
              </Space>
            </Col>
          ))}
          <Col>
            <Button
              size="small" icon={<ScanOutlined />}
              loading={loading} onClick={onScan}
            >
              扫描
            </Button>
          </Col>
        </Row>
      }
      description={
        allUncovered.length > 0 ? (
          <div style={{ marginTop: 4 }}>
            <Text type="secondary" style={{ fontSize: 12 }}>
              未覆盖：{allUncovered.join("、")}
            </Text>
          </div>
        ) : null
      }
    />
  );
};

// ---- Span 链路树 ----

const SpanTree: React.FC<{ detail: TraceDetail | null; loading: boolean }> = ({ detail, loading }) => {
  if (loading) return <Spin style={{ display: "block", marginTop: 40, textAlign: "center" }} />;
  if (!detail) {
    return <Text type="secondary" style={{ display: "block", textAlign: "center", marginTop: 40 }}>点击左侧请求查看链路详情</Text>;
  }

  return (
    <div style={{ padding: "0 8px" }}>
      <div style={{ marginBottom: 8 }}>
        <Text strong>{detail.method} {detail.path}</Text>
        <Tag color={detail.error ? "red" : "green"} style={{ marginLeft: 8 }}>
          {detail.status_code ?? "—"}
        </Tag>
        <Text type="secondary" style={{ marginLeft: 8 }}>{fmtDuration(detail.total_duration_ms)}</Text>
      </div>
      <Divider style={{ margin: "8px 0" }} />
      {detail.spans.map((span, idx) => (
        <SpanRow key={idx} span={span} index={idx} />
      ))}
    </div>
  );
};

const SpanRow: React.FC<{ span: TraceSpan; index: number }> = ({ span, index }) => {
  const isError = ERROR_SPAN_TYPES.has(span.span_type);
  const color = SPAN_COLOR[span.span_type] ?? "#8c8c8c";
  const isDbQuery = span.span_type === "DB_QUERY";

  // 根据 span 类型决定缩进层级
  const indent = getSpanIndent(span.span_type);

  return (
    <div
      style={{
        marginLeft: indent * 16,
        padding: "4px 8px",
        marginBottom: 2,
        borderLeft: `3px solid ${color}`,
        background: isError ? "#fff2f0" : index % 2 === 0 ? "#fafafa" : "#fff",
        borderRadius: 2,
        fontSize: 13,
      }}
    >
      <Row justify="space-between" align="top">
        <Col flex="auto">
          <Tag color={color} style={{ fontSize: 11, lineHeight: "18px" }}>
            {span.span_type}
          </Tag>
          <Text style={{ color: isError ? "#f5222d" : undefined }}>
            {span.description_zh || `${span.module}.${span.function}`}
          </Text>
        </Col>
        <Col flex="none">
          <Text type="secondary" style={{ fontSize: 12, whiteSpace: "nowrap" }}>
            {fmtDuration(span.duration_ms)}
          </Text>
        </Col>
      </Row>
      {/* DB_QUERY / DB_ERROR：展示 SQL 详情 */}
      {(isDbQuery || span.span_type === "DB_ERROR") && !!span.extra?.sql && (
        <div style={{ margin: "4px 0 0", fontSize: 11 }}>
          <pre style={{
            margin: 0, padding: "4px 8px",
            background: span.span_type === "DB_ERROR" ? "#fff1f0" : "#f0f5ff",
            border: `1px solid ${span.span_type === "DB_ERROR" ? "#ffa39e" : "#adc6ff"}`,
            borderRadius: 2,
            whiteSpace: "pre-wrap", wordBreak: "break-all",
            maxHeight: 200, overflow: "auto",
            fontFamily: "'Cascadia Code', 'Fira Code', Consolas, monospace",
          }}>
            {String(span.extra.sql)}
          </pre>
          {/* 绑定参数 */}
          {span.extra.params != null && (
            <div style={{ marginTop: 2, color: "#8c8c8c" }}>
              <span style={{ color: "#722ed1" }}>参数：</span>
              {JSON.stringify(span.extra.params)}
            </div>
          )}
          {/* 行数 + 调用来源 */}
          <div style={{ marginTop: 2, color: "#8c8c8c", display: "flex", gap: 12 }}>
            {span.extra.row_count != null && (
              <span><span style={{ color: "#1890ff" }}>行数：</span>{String(span.extra.row_count)}</span>
            )}
            {span.extra.caller != null && (
              <span><span style={{ color: "#52c41a" }}>来源：</span>{String(span.extra.caller)}</span>
            )}
          </div>
        </div>
      )}
      {/* 通用 params 展示（非 DB_QUERY/DB_ERROR，且 params 非空） */}
      {!isDbQuery && span.span_type !== "DB_ERROR" && span.params && Object.keys(span.params).length > 0 && (
        <div style={{ marginTop: 4, fontSize: 11, color: "#8c8c8c" }}>
          <span style={{ color: "#722ed1" }}>参数：</span>
          <span style={{ fontFamily: "'Cascadia Code', 'Fira Code', Consolas, monospace" }}>
            {JSON.stringify(span.params, null, 0)}
          </span>
        </div>
      )}
      {/* ERROR：展示错误信息 */}
      {isError && span.result_summary && (
        <div style={{ marginTop: 4, color: "#f5222d", fontSize: 12 }}>
          {span.result_summary}
        </div>
      )}
    </div>
  );
};

/** 根据 span_type 返回缩进层级（模拟层级关系） */
function getSpanIndent(spanType: SpanType): number {
  switch (spanType) {
    case "HTTP_IN": case "HTTP_OUT": return 0;
    case "MIDDLEWARE": case "MIDDLEWARE_ERROR": return 1;
    case "AUTH": return 1;
    case "ROUTE": return 1;
    case "SERVICE": return 2;
    case "DB_QUERY": case "DB_CONN": case "DB_CONN_RELEASE": case "DB_ERROR": return 3;
    case "SSE_START": case "SSE_END": return 1;
    case "SSE_EVENT": case "AI_CALL": case "AI_STREAM": case "AI_ERROR": return 2;
    case "WS_CONNECT": case "WS_DISCONNECT": return 1;
    case "WS_MESSAGE": return 2;
    case "JOB_START": case "JOB_END": case "JOB_ERROR": return 0;
    case "ERROR": return 1;
    default: return 1;
  }
}

// ---- 设置面板（Task 20） ----

const SettingsDrawer: React.FC<{
  open: boolean;
  onClose: () => void;
}> = ({ open, onClose }) => {
  const [settings, setSettings] = useState<TraceSettings | null>(null);
  const [loading, setLoading] = useState(false);
  const [saving, setSaving] = useState(false);
  const [cleanRange, setCleanRange] = useState<[dayjs.Dayjs, dayjs.Dayjs] | null>(null);
  const [cleaning, setCleaning] = useState(false);

  const loadSettings = useCallback(async () => {
    setLoading(true);
    try {
      const res = await fetchSettings();
      setSettings(res);
    } catch {
      message.error("加载设置失败");
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => {
    if (open) loadSettings();
  }, [open, loadSettings]);

  const handleToggle = async (field: keyof TraceSettings, value: boolean) => {
    try {
      const res = await updateSettings({ [field]: value });
      setSettings(res);
      message.success("设置已更新");
    } catch {
      message.error("更新失败");
    }
  };

  const handleSaveRetention = async () => {
    if (!settings) return;
    setSaving(true);
    try {
      const res = await updateSettings({ retention_days: settings.retention_days });
      setSettings(res);
      message.success("保留天数已更新");
    } catch {
      message.error("更新失败");
    } finally {
      setSaving(false);
    }
  };

  const handleCleanup = async () => {
    if (!cleanRange) { message.warning("请选择日期范围"); return; }
    setCleaning(true);
    try {
      const res = await cleanupLogs(
        cleanRange[0].format("YYYY-MM-DD"),
        cleanRange[1].format("YYYY-MM-DD"),
      );
      message.success(`已清理 ${res.deleted_files} 个文件（${res.deleted_dates.length} 天）`);
    } catch {
      message.error("清理失败");
    } finally {
      setCleaning(false);
    }
  };

  return (
    <Drawer
      title="Trace 设置" open={open} onClose={onClose}
      width={400} destroyOnClose
    >
      {loading ? <Spin /> : settings && (
        <div>
          <div style={{ marginBottom: 20 }}>
            <Text strong>日志开关</Text>
            <Switch
              checked={settings.enabled}
              onChange={(v) => handleToggle("enabled", v)}
              style={{ marginLeft: 12 }}
            />
          </div>

          <div style={{ marginBottom: 20 }}>
            <Text strong>记录 SQL</Text>
            <Switch
              checked={settings.log_sql}
              onChange={(v) => handleToggle("log_sql", v)}
              style={{ marginLeft: 12 }}
            />
          </div>

          <div style={{ marginBottom: 20 }}>
            <Text strong>记录参数</Text>
            <Switch
              checked={settings.log_params}
              onChange={(v) => handleToggle("log_params", v)}
              style={{ marginLeft: 12 }}
            />
          </div>

          <Divider />

          <div style={{ marginBottom: 20 }}>
            <Text strong>保留天数</Text>
            <div style={{ marginTop: 8 }}>
              <Space>
                <InputNumber
                  min={1} max={365}
                  value={settings.retention_days}
                  onChange={(v) => v && setSettings({ ...settings, retention_days: v })}
                />
                <Button type="primary" size="small" loading={saving} onClick={handleSaveRetention}>
                  保存
                </Button>
              </Space>
            </div>
          </div>

          <div style={{ marginBottom: 20 }}>
            <Text strong>日志目录</Text>
            <div style={{ marginTop: 4 }}>
              <Text type="secondary" copyable style={{ fontSize: 12 }}>{settings.log_dir}</Text>
            </div>
          </div>

          <Divider />

          <div>
            <Text strong>手动清理</Text>
            <div style={{ marginTop: 8 }}>
              <Space direction="vertical" style={{ width: "100%" }}>
                <DatePicker.RangePicker
                  style={{ width: "100%" }}
                  value={cleanRange}
                  onChange={(v) => setCleanRange(v as [dayjs.Dayjs, dayjs.Dayjs] | null)}
                />
                <Button
                  danger icon={<DeleteOutlined />}
                  loading={cleaning} onClick={handleCleanup}
                  block
                >
                  清理选定范围
                </Button>
              </Space>
            </div>
          </div>
        </div>
      )}
    </Drawer>
  );
};

// ---- 请求列表列定义 ----

const requestColumns: ColumnsType<TraceRequest> = [
  {
    title: "时间", dataIndex: "timestamp", key: "timestamp", width: 170,
    render: fmtTime,
  },
  {
    title: "类型", dataIndex: "trace_type", key: "trace_type", width: 70,
    render: (v: TraceType) => {
      const colors: Record<TraceType, string> = { http: "blue", sse: "cyan", ws: "gold", job: "default" };
      return <Tag color={colors[v]}>{v.toUpperCase()}</Tag>;
    },
  },
  { title: "方法", dataIndex: "method", key: "method", width: 70 },
  {
    title: "路径", dataIndex: "path", key: "path", ellipsis: true,
    render: (v: string) => <Tooltip title={v}>{v}</Tooltip>,
  },
  {
    title: "状态", dataIndex: "status_code", key: "status_code", width: 60,
    render: (v: number | null) => {
      if (v == null) return "—";
      const color = v >= 400 ? "red" : v >= 300 ? "orange" : "green";
      return <Tag color={color}>{v}</Tag>;
    },
  },
  {
    title: "耗时", dataIndex: "total_duration_ms", key: "duration", width: 90,
    render: fmtDuration,
    sorter: (a, b) => a.total_duration_ms - b.total_duration_ms,
  },
  {
    title: "DB", dataIndex: "db_query_count", key: "db", width: 50,
    render: (v: number) => v > 0 ? <Text type="secondary">{v}</Text> : "—",
  },
  {
    title: "错误", dataIndex: "error", key: "error", width: 60,
    render: (v: string | null) => v ? <Tag color="red">有</Tag> : null,
  },
];

// ---- 主页面组件 ----

const DevTrace: React.FC = () => {
  // 数据状态
  const [dates, setDates] = useState<string[]>([]);
  const [requests, setRequests] = useState<TraceRequest[]>([]);
  const [total, setTotal] = useState(0);
  const [detail, setDetail] = useState<TraceDetail | null>(null);
  const [coverage, setCoverage] = useState<TraceCoverage | null>(null);

  // 加载状态
  const [listLoading, setListLoading] = useState(false);
  const [detailLoading, setDetailLoading] = useState(false);
  const [coverageLoading, setCoverageLoading] = useState(false);

  // 筛选状态
  const [filter, setFilter] = useState<TraceFilter>({
    date: dayjs().format("YYYY-MM-DD"),
    page: 1,
    page_size: 30,
  });

  // UI 状态
  const [selectedRowKey, setSelectedRowKey] = useState<string | null>(null);
  const [settingsOpen, setSettingsOpen] = useState(false);
  const [hiddenIds, setHiddenIds] = useState<Set<string>>(new Set());

  // 加载日期列表
  const loadDates = useCallback(async () => {
    try {
      const res = await fetchDates();
      setDates(res.dates);
    } catch {
      // 静默降级
    }
  }, []);

  // 加载请求列表
  const loadRequests = useCallback(async () => {
    setListLoading(true);
    try {
      const res = await fetchRequests(filter);
      setRequests(res.items);
      setTotal(res.total);
    } catch {
      message.error("加载请求列表失败");
    } finally {
      setListLoading(false);
    }
  }, [filter]);

  // 加载覆盖率
  const loadCoverage = useCallback(async () => {
    setCoverageLoading(true);
    try {
      const res = await fetchCoverage();
      setCoverage(res);
    } catch {
      message.warning("覆盖率数据加载失败，可点击扫描按钮重试");
    } finally {
      setCoverageLoading(false);
    }
  }, []);

  // 手动扫描覆盖率
  const handleScan = async () => {
    setCoverageLoading(true);
    try {
      const res = await triggerCoverageScan();
      setCoverage(res);
      message.success("覆盖率扫描完成");
    } catch {
      message.error("扫描失败");
    } finally {
      setCoverageLoading(false);
    }
  };

  // 点击行查看详情
  const handleRowClick = async (record: TraceRequest) => {
    setSelectedRowKey(record.request_id);
    setDetailLoading(true);
    try {
      const res = await fetchRequestDetail(record.request_id);
      setDetail(res);
    } catch {
      message.error("加载详情失败");
    } finally {
      setDetailLoading(false);
    }
  };

  // 初始化
  useEffect(() => { loadDates(); loadCoverage(); }, [loadDates, loadCoverage]);
  useEffect(() => { loadRequests(); }, [loadRequests]);

  // 筛选变更辅助
  const updateFilter = (patch: Partial<TraceFilter>) => {
    setFilter((prev) => ({ ...prev, ...patch, page: 1 }));
  };

  // 过滤掉被屏蔽的记录
  const visibleRequests = requests.filter((r) => !hiddenIds.has(r.request_id));

  // 清空：把当前可见记录全部加入屏蔽集合（累积）
  const handleClearList = () => {
    setHiddenIds((prev) => {
      const next = new Set(prev);
      for (const r of requests) next.add(r.request_id);
      return next;
    });
    setDetail(null);
    setSelectedRowKey(null);
  };

  // 取消清空：清空屏蔽集合
  const handleUnclearList = () => {
    setHiddenIds(new Set());
  };

  return (
    <div>
      {/* 标题栏 */}
      <Row justify="space-between" align="middle" style={{ marginBottom: 12 }}>
        <Title level={4} style={{ margin: 0 }}>开发调试日志</Title>
        <Space>
          <Button icon={<DeleteOutlined />} onClick={handleClearList} disabled={visibleRequests.length === 0}>
            清空
          </Button>
          {hiddenIds.size > 0 && (
            <Button onClick={handleUnclearList}>
              取消清空({hiddenIds.size})
            </Button>
          )}
          <Button icon={<ReloadOutlined />} onClick={() => { loadRequests(); loadCoverage(); }} loading={listLoading}>
            刷新
          </Button>
          <Button icon={<SettingOutlined />} onClick={() => setSettingsOpen(true)}>
            设置
          </Button>
        </Space>
      </Row>

      {/* 覆盖率状态栏 */}
      <CoverageBar coverage={coverage} loading={coverageLoading} onScan={handleScan} />

      {/* 筛选栏 */}
      <div style={{ marginBottom: 12, background: "#fafafa", padding: "10px 12px", borderRadius: 4 }}>
        <Space wrap size={[8, 8]}>
          <Select
            placeholder="日期" style={{ width: 130 }}
            value={filter.date}
            onChange={(v) => updateFilter({ date: v })}
            options={dates.map((d) => ({ label: d, value: d }))}
            showSearch
          />
          <TimePicker.RangePicker
            format="HH:mm"
            placeholder={["开始时间", "结束时间"]}
            onChange={(_, strs) => updateFilter({
              start_time: strs[0] || undefined,
              end_time: strs[1] || undefined,
            })}
          />
          <Select
            placeholder="类型" style={{ width: 100 }} allowClear
            options={TRACE_TYPE_OPTIONS}
            onChange={(v) => updateFilter({ trace_type: v })}
          />
          <Select
            placeholder="方法" style={{ width: 100 }} allowClear
            options={METHOD_OPTIONS.map((m) => ({ label: m, value: m }))}
            onChange={(v) => updateFilter({ method: v })}
          />
          <Input
            placeholder="路径关键词" style={{ width: 160 }}
            prefix={<SearchOutlined />} allowClear
            onPressEnter={(e) => updateFilter({ path_contains: (e.target as HTMLInputElement).value || undefined })}
            onBlur={(e) => updateFilter({ path_contains: e.target.value || undefined })}
          />
          <InputNumber
            placeholder="状态码" style={{ width: 90 }}
            min={100} max={599}
            onChange={(v) => updateFilter({ status_code: v ?? undefined })}
          />
          <InputNumber
            placeholder="最小耗时(ms)" style={{ width: 130 }}
            min={0}
            onChange={(v) => updateFilter({ min_duration: v ?? undefined })}
          />
          <Checkbox
            onChange={(e) => updateFilter({ has_error: e.target.checked || undefined })}
          >
            仅错误
          </Checkbox>
          <Select
            placeholder="Span 类型" style={{ width: 150 }} allowClear
            options={SPAN_TYPE_OPTIONS.map((s) => ({ label: s, value: s }))}
            onChange={(v) => updateFilter({ span_type: v })}
          />
        </Space>
      </div>

      {/* 左右分栏 */}
      <Row gutter={12} style={{ minHeight: 500 }}>
        {/* 左侧：请求列表 */}
        <Col span={14}>
          <Table<TraceRequest>
            columns={requestColumns}
            dataSource={visibleRequests}
            rowKey="request_id"
            size="small"
            loading={listLoading}
            pagination={{
              current: filter.page,
              pageSize: filter.page_size,
              total,
              showSizeChanger: true,
              pageSizeOptions: ["20", "30", "50", "100"],
              showTotal: (t) => `共 ${t} 条`,
              onChange: (page, pageSize) => setFilter((prev) => ({ ...prev, page, page_size: pageSize })),
            }}
            onRow={(record) => ({
              onClick: () => handleRowClick(record),
              style: {
                cursor: "pointer",
                background: record.request_id === selectedRowKey ? "#e6f7ff" : undefined,
              },
            })}
            scroll={{ y: 520 }}
          />
        </Col>

        {/* 右侧：Span 链路树 */}
        <Col span={10}>
          <div style={{
            border: "1px solid #f0f0f0", borderRadius: 4,
            padding: 12, minHeight: 520, maxHeight: 600,
            overflow: "auto", background: "#fff",
          }}>
            <SpanTree detail={detail} loading={detailLoading} />
          </div>
        </Col>
      </Row>

      {/* 设置面板 */}
      <SettingsDrawer open={settingsOpen} onClose={() => setSettingsOpen(false)} />
    </div>
  );
};

export default DevTrace;
