import { IconAlertTriangle, IconChevronDown, IconChevronRight, IconChevronUp, IconClose, IconRefresh, IconSearch } from '@douyinfe/semi-icons';
import { Button, Card, Empty, Input, RadioGroup, Select, Spin, Table, Tag, TextArea } from '@douyinfe/semi-ui';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useDeferredValue, useEffect, useMemo, useState } from 'react';
import { api } from '../../api/client';
import type { ReconciliationIssue, ReconciliationSummary } from '../../api/types';
import { InlineError } from '../shared/AsyncState';
import { MobileFilterToggle } from '../shared/MobileFilterToggle';
import { PlatformTime } from '../shared/PlatformTime';
import { TablePagination } from '../shared/TablePagination';
import { WorkspaceMetricRail } from '../shared/WorkspaceMetricRail';
import { WorkspacePanelHeader } from '../shared/WorkspacePanelHeader';
import { WorkspaceSideSheet } from '../shared/WorkspaceSideSheet';
import { detailTriggerRow } from '../shared/detailTriggerRow';
import { QUERY_MEMORY } from '../queryPolicy';
import { useMobileLayout } from '../hooks/useMobileLayout';

const DESKTOP_PAGE_SIZE = 50;
const MOBILE_PAGE_SIZE = 20;
const activeStatuses = new Set(['pending', 'confirmed_source_a', 'confirmed_source_b']);
function statusLabel(status: string) {
  return {
    pending: '待处理',
    confirmed_source_a: '确认来源 A',
    confirmed_source_b: '确认来源 B',
    no_action: '无需处理',
    fixed: '已修复',
    recovered: '已恢复'
  }[status] ?? status;
}

function severityLabel(severity: string) {
  return { critical: '严重', major: '重要', minor: '一般' }[severity] ?? severity;
}

function ReconciliationSeverityTag({ severity }: { severity: string }) {
  const color = severity === 'critical' ? 'red' : severity === 'major' ? 'orange' : 'grey';
  return <Tag className={`v2-reconcile-severity is-${severity}`} color={color} type="light" size="small">{severityLabel(severity)}</Tag>;
}

function ReconciliationStatusTag({ status }: { status: string }) {
  const color = status === 'pending' ? 'orange' : status === 'confirmed_source_a' || status === 'confirmed_source_b' ? 'blue' : status === 'fixed' || status === 'recovered' ? 'green' : 'grey';
  return <Tag className={`v2-reconcile-status is-${status}`} color={color} type="light" size="small">{statusLabel(status)}</Tag>;
}

function ruleLabel(rule: string) {
  return {
    DUPLICATE_PLATE: '重复车牌',
    DUPLICATE_PHONE: '重复终端',
    UNBOUND_SOURCE: '来源未绑定',
    SOURCE_MISSING: '主车无来源',
    POSITION_DRIFT: '位置漂移',
    MILEAGE_REVERSE: '里程倒退',
    MILEAGE_JUMP: '里程跳变',
    MILEAGE_SOURCE_DIVERGENCE: '里程来源差异',
    FLEET_COUNT_MISMATCH: '车辆总数不一致',
    BUSINESS_SCOPE_UNBOUND: '业务车辆未绑定',
    AUTH_SCOPE_BUSINESS_MISMATCH: '授权与业务范围不一致'
  }[rule] ?? rule;
}

function categoryLabel(category: string) {
  return {
    identity: '身份',
    source: '数据来源',
    location: '实时位置',
    mileage: '里程',
    fleet: '车队口径',
    business: '业务范围'
  }[category] ?? category;
}

const evidenceLabels: Record<string, string> = {
  date: '统计日期',
  protocol: '协议来源',
  protocols: '协议来源',
  dailyMileageKm: '当日里程',
  latestTotalMileageKm: '最新总里程',
  derivedStartMileageKm: '推算起始总里程',
  minimumKm: '来源最小里程',
  maximumKm: '来源最大里程',
  differenceKm: '来源里程差',
  distanceM: '坐标差距',
  sourceA: '来源 A 快照',
  sourceB: '来源 B 快照',
  longitude: '经度',
  latitude: '纬度',
  receivedAt: '平台接收时间',
  sourceHash: '来源指纹',
  vin: '车辆 VIN',
  vins: '关联 VIN',
  plate: '车牌',
  vinCount: '关联车辆数',
  identifierHash: '终端标识指纹',
  latestSeen: '最近上报',
  bindingSource: '绑定依据',
  boundVehicles: '主车辆数',
  sourceUnionVehicles: '来源车辆并集',
  unboundSourceVehicles: '未绑定来源数',
  scopeVersion: '业务范围版本',
  customerId: '客户 ID',
  customerName: '客户名称',
  contractCode: '合同编号',
  projectName: '项目名称',
  departmentName: '所属部门',
  responsibleUserName: '责任人',
  userId: '账号 ID',
  username: '账号',
  customerRef: '客户标识',
  grantValidFrom: '授权生效时间'
};

const mileageEvidenceKeys = new Set(['dailyMileageKm', 'latestTotalMileageKm', 'derivedStartMileageKm', 'minimumKm', 'maximumKm', 'differenceKm']);

function evidenceLabel(key: string) {
  return evidenceLabels[key] ?? key;
}

function evidenceScalar(key: string, value: unknown) {
  if (value == null || value === '') return '—';
  if (typeof value === 'number') {
    const formatted = value.toLocaleString('zh-CN', { maximumFractionDigits: 3 });
    if (mileageEvidenceKeys.has(key)) return `${formatted} km`;
    if (key === 'distanceM') return `${formatted} m`;
    return formatted;
  }
  if (Array.isArray(value)) return value.map(String).join('、') || '—';
  if (typeof value === 'boolean') return value ? '是' : '否';
  return String(value);
}

function EvidenceValue({ name, value }: { name: string; value: unknown }) {
  if (value && typeof value === 'object' && !Array.isArray(value)) {
    return <div className="v2-reconcile-evidence-object">
      {Object.entries(value as Record<string, unknown>).map(([key, nested]) => <span key={key}>
        <small>{evidenceLabel(key)}</small>
        <strong>{evidenceScalar(key, nested)}</strong>
      </span>)}
    </div>;
  }
  return <strong className="v2-reconcile-evidence-value">{evidenceScalar(name, value)}</strong>;
}

function reviewStatusOptions(issue: ReconciliationIssue) {
  const options = [{ value: 'pending', label: '待复核' }];
  if (issue.protocolA && issue.protocolB) {
    options.push(
      { value: 'confirmed_source_a', label: `采信 ${issue.protocolA}` },
      { value: 'confirmed_source_b', label: `采信 ${issue.protocolB}` }
    );
  }
  options.push(
    { value: 'no_action', label: '无需处理' },
    { value: 'fixed', label: '已修复' }
  );
  return options;
}

function reviewGuidance(issue: ReconciliationIssue) {
  return {
    POSITION_DRIFT: '先比对两个来源的接收时间、车辆工况与当前位置，再判断可信来源；系统不会仅凭坐标差自动覆盖原始数据。',
    MILEAGE_REVERSE: '先核对原始总里程、单位换算和统计起止值；确认计算链路恢复后，再将问题标记为已修复。',
    MILEAGE_JUMP: '先核对仪表总里程、GPS 里程及跨日边界，排除单位或设备重置后再形成结论。',
    MILEAGE_SOURCE_DIVERGENCE: '对照仪表里程与 GPS 里程的原始报文、上报周期和业务口径，再选择本次可信来源。',
    DUPLICATE_PLATE: '核对车辆档案、VIN 与业务归属，确认真实主车辆后再处理重复绑定。',
    DUPLICATE_PHONE: '核对终端换绑记录与设备清单，避免将正常换车误判为重复终端。',
    UNBOUND_SOURCE: '先确认来源 VIN 的车辆档案和业务归属，再完成主车辆绑定。',
    SOURCE_MISSING: '核对设备接入、绑定关系与最近上报，确认是离线、未接入还是身份缺失。',
    FLEET_COUNT_MISMATCH: '分别核对主车辆、实时来源并集和未绑定来源，避免直接修改统计总数。',
    BUSINESS_SCOPE_UNBOUND: '核对 OneOS 当前业务范围与中台主车辆档案，再补齐主车辆绑定。',
    AUTH_SCOPE_BUSINESS_MISMATCH: '核对客户账号授权区间与 OneOS 活跃业务范围，再调整车辆权限。'
  }[issue.ruleCode] ?? '先核对原始证据、影响对象和时间范围，再选择结论并留下可追溯说明。';
}

function ReconciliationTrend({ data, maxTrend }: {
  data?: ReconciliationSummary;
  maxTrend: number;
}) {
  return <Card className="v2-reconcile-trend" bodyStyle={{ padding: 0 }}>
    <WorkspacePanelHeader
      variant="compact"
      title="趋势明细"
      description="最近 14 次自动对账运行的存量、新增与恢复"
      meta={<span>最近运行 <PlatformTime className="v2-ops-time" value={data?.lastRunAt} sourceZone="utc" /></span>}
    />
    <div>{data?.trend.slice(-14).map((item) => <article key={item.date}><time>{item.date.slice(5)}</time><div title={`存量 ${item.active}，新增 ${item.new}，恢复 ${item.recovered}`}><i className="is-active" style={{ width: `${Math.max(2, item.active / maxTrend * 100)}%` }} /><i className="is-new" style={{ width: `${Math.max(0, item.new / maxTrend * 100)}%` }} /><i className="is-recovered" style={{ width: `${Math.max(0, item.recovered / maxTrend * 100)}%` }} /></div><b>{item.active}</b></article>)}</div>
    {!data?.trend.length ? <p>完成首次每日检测后显示趋势。</p> : null}
    <footer><span><i className="is-active" />存量</span><span><i className="is-new" />新增</span><span><i className="is-recovered" />恢复</span></footer>
  </Card>;
}

function ReconciliationDetail({ issue, onClose, sheet = false }: { issue: ReconciliationIssue; onClose: () => void; sheet?: boolean }) {
  const queryClient = useQueryClient();
  const [status, setStatus] = useState(issue.status === 'recovered' ? 'pending' : issue.status);
  const [note, setNote] = useState(issue.resolutionNote ?? '');
  useEffect(() => {
    setStatus(issue.status === 'recovered' ? 'pending' : issue.status);
    setNote(issue.resolutionNote ?? '');
  }, [issue.id, issue.resolutionNote, issue.status]);
  const save = useMutation({
    mutationFn: () => api.updateReconciliationIssue(issue.id, { version: issue.version, status, note: note.trim() }),
    onSuccess: async (updated) => {
      queryClient.setQueryData(['reconciliation-detail', issue.id], updated);
      await Promise.all([
        queryClient.invalidateQueries({ queryKey: ['reconciliation-summary'] }),
        queryClient.invalidateQueries({ queryKey: ['reconciliation-issues'] })
      ]);
    }
  });
  const requiresNote = status !== 'pending';
  const evidenceEntries = Object.entries(issue.evidence ?? {});
  const statusOptions = reviewStatusOptions(issue);
  return <Card className={`v2-reconcile-detail${sheet ? ' is-sheet' : ''}`} bodyStyle={{ padding: 0 }} aria-label="差异证据与处置">
    {!sheet ? <WorkspacePanelHeader
      className="v2-reconcile-detail-heading"
      title={issue.title}
      description={ruleLabel(issue.ruleCode)}
      meta={<span className="v2-reconcile-detail-status"><ReconciliationSeverityTag severity={issue.severity} /><ReconciliationStatusTag status={issue.status} /></span>}
      actions={<Button theme="borderless" type="tertiary" icon={<IconClose />} onClick={onClose} aria-label="关闭差异详情" />}
    /> : null}
    <div className="v2-reconcile-detail-body">
      {!sheet ? <section className="v2-reconcile-identity">
        <div><small>车辆</small><strong>{issue.plate || '未登记车牌'}</strong><span>{issue.vin || '非单车差异'}</span></div>
        <div><small>来源</small><strong>{[issue.protocolA, issue.protocolB].filter(Boolean).join(' ↔ ') || '平台口径'}</strong><span>累计发现 {issue.occurrenceCount.toLocaleString('zh-CN')} 次</span></div>
        <div><small>时间</small><strong><PlatformTime className="v2-ops-time" value={issue.lastSeenAt} sourceZone="utc" /></strong><span>首次 <PlatformTime className="v2-ops-time" value={issue.firstSeenAt} sourceZone="utc" /></span></div>
      </section> : null}
      <section className="v2-reconcile-decision-guide">
        <IconAlertTriangle />
        <div><strong>复核提示</strong><p>{reviewGuidance(issue)}</p></div>
      </section>
      <p className="v2-reconcile-summary"><strong>命中说明</strong><span>{issue.summary}</span></p>
      <Card className="v2-reconcile-evidence" bodyStyle={{ padding: 0 }}>
        <WorkspacePanelHeader
          variant="compact"
          title="规则证据"
          description="中文口径优先，保留原始字段名便于技术追溯"
          meta={`${evidenceEntries.length} 项`}
        />
        <dl>{evidenceEntries.map(([key, value]) => <div className={value && typeof value === 'object' && !Array.isArray(value) ? 'is-object' : undefined} key={key}>
          <dt><strong>{evidenceLabel(key)}</strong>{evidenceLabel(key) !== key ? <code>{key}</code> : null}</dt>
          <dd><EvidenceValue name={key} value={value} /></dd>
        </div>)}</dl>
      </Card>
      <Card className="v2-reconcile-review" bodyStyle={{ padding: 0 }}>
        <WorkspacePanelHeader variant="compact" title="复核结论" description="结论会写入处理履历，不改写原始证据" meta={`版本 v${issue.version}`} />
        <label><span id="reconcile-review-status-label">选择结论</span><RadioGroup aria-labelledby="reconcile-review-status-label" type="button" buttonSize="small" value={status} options={statusOptions} onChange={(event) => setStatus(String(event.target.value))} /></label>
        <label><span>说明{requiresNote ? '（必填）' : '（可选）'}</span><TextArea aria-label={`说明${requiresNote ? '（必填）' : '（可选）'}`} value={note} maxCount={500} autosize={{ minRows: 3, maxRows: 6 }} onChange={setNote} placeholder="记录核对来源、原始值、责任人或修复结果" /></label>
        <Button theme="solid" disabled={save.isPending || (requiresNote && !note.trim())} loading={save.isPending} onClick={() => save.mutate()}>保存结论并记录履历</Button>
        {save.isError ? <p role="alert">{save.error instanceof Error ? save.error.message : '保存失败'}</p> : null}
      </Card>
      <Card className="v2-reconcile-actions" bodyStyle={{ padding: 0 }}>
        <WorkspacePanelHeader variant="compact" title="处理履历" meta={`${issue.actions?.length ?? 0} 条`} />
        {issue.actions?.length ? <ol>{issue.actions.map((action) => <li key={action.id}><i /><div><strong>{statusLabel(action.toStatus)}</strong><p>{action.note || action.action}</p><span>{action.actor} · <PlatformTime className="v2-ops-time" value={action.createdAt} sourceZone="utc" /></span></div></li>)}</ol> : <p>暂无处理记录。</p>}
      </Card>
    </div>
  </Card>;
}

export default function ReconciliationCenter() {
  const mobileLayout = useMobileLayout();
  const pageSize = mobileLayout ? MOBILE_PAGE_SIZE : DESKTOP_PAGE_SIZE;
  const [keyword, setKeyword] = useState('');
  const deferredKeyword = useDeferredValue(keyword.trim());
  const [status, setStatus] = useState('active');
  const [severity, setSeverity] = useState('all');
  const [ruleCode, setRuleCode] = useState('all');
  const [offset, setOffset] = useState(0);
  const [selectedID, setSelectedID] = useState('');
  const [filtersCollapsed, setFiltersCollapsed] = useState(true);
  const [trendExpanded, setTrendExpanded] = useState(false);
  const closeDetail = () => setSelectedID('');
  const openDetail = (id: string) => {
    setTrendExpanded(false);
    setSelectedID(id);
  };
  const toggleTrend = () => {
    setSelectedID('');
    setTrendExpanded((value) => !value);
  };
  useEffect(() => setOffset(0), [deferredKeyword, mobileLayout, ruleCode, severity, status]);

  const summary = useQuery({
    queryKey: ['reconciliation-summary', 30],
    queryFn: ({ signal }) => api.reconciliationSummary(30, signal),
    staleTime: 60_000,
    gcTime: QUERY_MEMORY.summaryGcTime
  });
  const issues = useQuery({
    queryKey: ['reconciliation-issues', deferredKeyword, ruleCode, severity, status, pageSize, offset],
    queryFn: ({ signal }) => api.reconciliationIssues({
      keyword: deferredKeyword, ruleCode, severity, status, limit: pageSize, offset
    }, signal),
    staleTime: 20_000,
    gcTime: QUERY_MEMORY.highVolumeGcTime
  });
  const detail = useQuery({
    queryKey: ['reconciliation-detail', selectedID],
    queryFn: ({ signal }) => api.reconciliationIssue(selectedID, signal),
    enabled: Boolean(selectedID),
    staleTime: 10_000,
    gcTime: QUERY_MEMORY.optionGcTime
  });
  const maxTrend = useMemo(() => {
    let result = 1;
    for (const item of summary.data?.trend ?? []) result = Math.max(result, item.active, item.new, item.recovered);
    return result;
  }, [summary.data?.trend]);
  const refresh = () => Promise.all([summary.refetch(), issues.refetch(), selectedID ? detail.refetch() : Promise.resolve()]);
  const data = summary.data;
  const page = issues.data;
  const activeCount = page?.items.filter((item) => activeStatuses.has(item.status)).length ?? 0;
  const currentPage = Math.floor(offset / pageSize) + 1;
  const totalPages = Math.max(1, Math.ceil((page?.total ?? 0) / pageSize));
  const issueRows = page?.items ?? [];
  const activeFilterCount = Number(Boolean(deferredKeyword)) + Number(severity !== 'all') + Number(ruleCode !== 'all');
  const filterSummary = `${status === 'active' ? '活跃差异' : statusLabel(status)} · ${(page?.total ?? 0).toLocaleString('zh-CN')} 条${activeFilterCount ? ` · 另 ${activeFilterCount} 项` : ''}`;
  const selectedIssue = detail.data ?? issueRows.find((item) => item.id === selectedID);
  const detailPanel = selectedID && detail.isPending
    ? <Card className="v2-reconcile-detail is-sheet" bodyStyle={{ padding: 0 }}><div className="v2-reconcile-detail-loading" role="status"><Spin size="middle" tip="正在读取差异证据…" /></div></Card>
    : selectedID && detail.isError
      ? <Card className="v2-reconcile-detail is-sheet" bodyStyle={{ padding: 0 }}><InlineError message={detail.error.message} onRetry={() => detail.refetch()} /></Card>
      : detail.data
        ? <ReconciliationDetail issue={detail.data} onClose={closeDetail} sheet />
        : null;
  const columns = [
    { title: '问题 / 等级', dataIndex: 'title', width: 286, render: (_value: string, item: ReconciliationIssue) => <span className="v2-reconcile-cell v2-reconcile-issue-cell"><span><strong>{item.title}</strong><ReconciliationSeverityTag severity={item.severity} /></span><small>{ruleLabel(item.ruleCode)} · 命中 {item.occurrenceCount.toLocaleString('zh-CN')} 次</small></span> },
    { title: '车辆', dataIndex: 'plate', width: 176, render: (_value: string, item: ReconciliationIssue) => <span className="v2-reconcile-cell"><strong>{item.plate || '非单车差异'}</strong><small>{item.vin || '平台级口径'}</small></span> },
    { title: '证据来源', dataIndex: 'protocolA', width: 164, render: (_value: string, item: ReconciliationIssue) => <span className="v2-reconcile-cell"><strong>{[item.protocolA, item.protocolB].filter(Boolean).join(' / ') || '平台口径'}</strong><small>{categoryLabel(item.category)}</small></span> },
    { title: '最近发现', dataIndex: 'lastSeenAt', width: 190, render: (_value: string, item: ReconciliationIssue) => <span className="v2-reconcile-cell"><strong><PlatformTime className="v2-ops-time" value={item.lastSeenAt} sourceZone="utc" /></strong><small>首次 <PlatformTime className="v2-ops-time" value={item.firstSeenAt} sourceZone="utc" /></small></span> },
    { title: '状态', dataIndex: 'status', width: 112, render: (value: string) => <ReconciliationStatusTag status={value} /> }
  ];

  return <><Card className="v2-reconcile-center" bodyStyle={{ padding: 0 }}>
    <WorkspacePanelHeader
      className="v2-reconcile-heading"
      eyebrow="自动对账"
      tone="health"
      title="质量问题队列"
      description="自动发现并合并重复问题；复核人员基于原始证据形成结论，不直接改写数据。"
      meta="每日自动对账"
      actions={<>
        <Button
          className="v2-reconcile-trend-open v2-workspace-mobile-tool-button is-muted"
          theme="borderless"
          type="tertiary"
          icon={trendExpanded ? <IconChevronUp /> : <IconChevronDown />}
          aria-label="30 天趋势"
          aria-expanded={trendExpanded}
          aria-controls="v2-reconcile-trend"
          onClick={toggleTrend}
        >30 天趋势</Button>
        <Button className="v2-workspace-mobile-tool-button is-muted" theme="light" icon={<IconRefresh />} aria-label="刷新结果" loading={summary.isFetching || issues.isFetching} onClick={refresh}>刷新结果</Button>
      </>}
    />
    {summary.isError ? <InlineError message={summary.error.message} onRetry={() => summary.refetch()} /> : null}
    <WorkspaceMetricRail
      variant="queue"
      className="v2-reconcile-metric-rail"
      ariaLabel="差异处置队列概览"
      items={[
        {
          label: '待复核',
          value: data?.pending.toLocaleString('zh-CN') ?? '—',
          note: `活跃 ${data?.active.toLocaleString('zh-CN') ?? '—'} 项`,
          tone: 'primary'
        },
        {
          label: 'SLA 超时',
          value: data?.overSla.toLocaleString('zh-CN') ?? '—',
          note: data?.overSla ? '超过 24 小时' : '当前无超时',
          tone: data?.overSla ? 'danger' : 'success'
        },
        {
          label: '已确认来源',
          value: data?.confirmed.toLocaleString('zh-CN') ?? '—',
          note: '保留活跃差异',
          emphasis: 'secondary'
        },
        {
          label: '已自动恢复',
          value: data?.recovered.toLocaleString('zh-CN') ?? '—',
          note: '规则不再命中',
          tone: 'success',
          emphasis: 'secondary'
        }
      ]}
      context={<span aria-live="polite"><small>当前筛选</small><strong>{(page?.total ?? 0).toLocaleString('zh-CN')}</strong><em>按严重程度、最近发现时间排序</em></span>}
    />
    <div className="v2-reconcile-layout">
      <div className="v2-reconcile-main">
        <MobileFilterToggle
          title="筛选差异"
          summary={filterSummary}
          expanded={!filtersCollapsed}
          collapsedLabel="修改"
          onToggle={() => setFiltersCollapsed((value) => !value)}
        />
        <div className={`v2-reconcile-toolbar${filtersCollapsed ? ' is-mobile-collapsed' : ''}`}>
          <Input className="v2-reconcile-search" prefix={<IconSearch />} aria-label="搜索差异车辆或规则" value={keyword} onChange={setKeyword} placeholder="车牌、VIN、标题或说明" />
          <span className="v2-sr-only" id="reconcile-status-filter-label">筛选差异状态</span>
          <Select aria-labelledby="reconcile-status-filter-label" value={status} onChange={(value) => setStatus(String(value))} optionList={[
            { value: 'active', label: '活跃差异' }, { value: 'pending', label: '待处理' },
            { value: 'confirmed_source_a', label: '确认来源 A' }, { value: 'confirmed_source_b', label: '确认来源 B' },
            { value: 'recovered', label: '已恢复' }, { value: 'fixed', label: '已修复' },
            { value: 'no_action', label: '无需处理' }, { value: 'all', label: '全部状态' }
          ]} />
          <span className="v2-sr-only" id="reconcile-severity-filter-label">筛选严重程度</span>
          <Select aria-labelledby="reconcile-severity-filter-label" value={severity} onChange={(value) => setSeverity(String(value))} optionList={[
            { value: 'all', label: '全部等级' }, { value: 'critical', label: '严重' },
            { value: 'major', label: '重要' }, { value: 'minor', label: '一般' }
          ]} />
          <span className="v2-sr-only" id="reconcile-rule-filter-label">筛选差异规则</span>
          <Select aria-labelledby="reconcile-rule-filter-label" value={ruleCode} onChange={(value) => setRuleCode(String(value))} optionList={[
            { value: 'all', label: '全部规则' },
            ...(data?.byRule.map((item) => ({ value: item.name, label: `${ruleLabel(item.name)} · ${item.count}` })) ?? [])
          ]} />
        </div>
        {issues.isError ? <InlineError message={issues.error.message} onRetry={() => issues.refetch()} /> : null}
        <div
          className="v2-reconcile-table-wrap is-scroll-region"
          role="region"
          aria-label="差异队列，可上下滚动"
          tabIndex={0}
        >
          {!mobileLayout ? <Table className="v2-reconcile-table" columns={columns} dataSource={issueRows} rowKey="id" pagination={false} scroll={{ x: 934 }} onRow={(item) => item ? detailTriggerRow({
            className: selectedID === item.id ? 'is-selected' : '',
            expanded: selectedID === item.id,
            label: `查看 ${item.plate || item.title} 差异证据`,
            testId: `reconcile-row-${item.id}`,
            onOpen: () => openDetail(item.id)
          }) : ({})} /> : <div className="v2-reconcile-mobile-list">{issueRows.map((item) => <Card key={item.id} className={`v2-reconcile-mobile-card${selectedID === item.id ? ' is-selected' : ''}`} bodyStyle={{ padding: 0 }}>
            <Button theme="borderless" type="tertiary" className="v2-reconcile-mobile-action" aria-pressed={selectedID === item.id} aria-expanded={selectedID === item.id} aria-label={`查看 ${item.plate || item.title} 差异证据`} onClick={() => openDetail(item.id)}>
              <span className="v2-reconcile-mobile-content">
                <header><strong>{item.plate || '平台级差异'}</strong><span><ReconciliationSeverityTag severity={item.severity} /><ReconciliationStatusTag status={item.status} /></span></header>
                <p>{item.title}</p>
                <span className="v2-reconcile-mobile-meta"><span>{[item.protocolA, item.protocolB].filter(Boolean).join(' / ') || '平台口径'}</span><PlatformTime className="v2-ops-time" value={item.lastSeenAt} sourceZone="utc" /></span>
                <footer><span>{ruleLabel(item.ruleCode)} · 命中 {item.occurrenceCount.toLocaleString('zh-CN')} 次</span><span>证据与处置<IconChevronRight /></span></footer>
              </span>
            </Button>
          </Card>)}</div>}
          {issues.isPending ? <div className="v2-reconcile-loading" role="status"><Spin size="middle" tip="正在读取差异队列…" /></div> : null}
          {!issues.isPending && !issueRows.length ? <Empty className="v2-reconcile-empty" title="当前筛选条件没有差异" description="调整状态、等级、规则或搜索条件后重试。" /> : null}
        </div>
        <footer className="v2-reconcile-pagination"><TablePagination page={currentPage} totalPages={totalPages} info={`共 ${(page?.total ?? 0).toLocaleString('zh-CN')} 条 · 本页 ${activeCount} 条活跃`} onPageChange={(next) => setOffset((next - 1) * pageSize)} /></footer>
      </div>
    </div>
  </Card>
  <WorkspaceSideSheet
    className="v2-reconcile-detail-sidesheet"
    variant="detail"
    visible={Boolean(selectedID)}
    placement={mobileLayout ? 'bottom' : 'right'}
    width={mobileLayout ? undefined : 680}
    height={mobileLayout ? 'min(88dvh, 780px)' : undefined}
    ariaLabel="差异证据与处置"
    closeLabel="关闭差异证据与处置"
    title={selectedIssue?.title ?? '差异证据与处置'}
    description={selectedIssue ? `${ruleLabel(selectedIssue.ruleCode)} · 原始证据、复核结论与处理履历` : '正在加载规则证据与处置履历'}
    badge={selectedIssue ? `${severityLabel(selectedIssue.severity)} · ${statusLabel(selectedIssue.status)}` : '读取中'}
    badgeColor={selectedIssue?.severity === 'critical' ? 'red' : selectedIssue?.severity === 'major' ? 'orange' : 'grey'}
    summaryItems={selectedIssue ? [
      {
        label: '车辆',
        value: selectedIssue.plate || '平台级差异',
        detail: selectedIssue.vin || '非单车口径',
        tone: 'primary'
      },
      {
        label: '证据来源',
        value: [selectedIssue.protocolA, selectedIssue.protocolB].filter(Boolean).join(' ↔ ') || '平台口径',
        detail: `累计发现 ${selectedIssue.occurrenceCount.toLocaleString('zh-CN')} 次`,
        tone: selectedIssue.severity === 'critical' ? 'danger' : 'warning'
      },
      {
        label: '最近发现',
        value: <PlatformTime className="v2-ops-time" value={selectedIssue.lastSeenAt} sourceZone="utc" />,
        detail: <span>首次 <PlatformTime className="v2-ops-time" value={selectedIssue.firstSeenAt} sourceZone="utc" /></span>,
        tone: 'neutral'
      }
    ] : []}
    onCancel={closeDetail}
  >
    {detailPanel}
  </WorkspaceSideSheet>
  <WorkspaceSideSheet
    className="v2-reconcile-trend-sidesheet"
    variant="detail"
    visible={trendExpanded}
    placement={mobileLayout ? 'bottom' : 'right'}
    width={mobileLayout ? undefined : 520}
    height={mobileLayout ? 'min(72dvh, 620px)' : undefined}
    ariaLabel="30 天差异趋势"
    closeLabel="关闭 30 天差异趋势"
    title="30 天差异趋势"
    description="查看自动对账的差异存量、新增与恢复走势"
    badge="每日自动对账"
    badgeColor="blue"
    summaryItems={[
      {
        label: '活跃差异',
        value: data?.active.toLocaleString('zh-CN') ?? '—',
        detail: `${data?.pending.toLocaleString('zh-CN') ?? '—'} 项待复核`,
        tone: 'warning'
      },
      {
        label: 'SLA 超时',
        value: data?.overSla.toLocaleString('zh-CN') ?? '—',
        detail: '超过 24 小时',
        tone: data?.overSla ? 'danger' : 'success'
      },
      {
        label: '已自动恢复',
        value: data?.recovered.toLocaleString('zh-CN') ?? '—',
        detail: '规则已不再命中',
        tone: 'success'
      }
    ]}
    onCancel={() => setTrendExpanded(false)}
  >
    {trendExpanded ? <div id="v2-reconcile-trend"><ReconciliationTrend data={data} maxTrend={maxTrend} /></div> : null}
  </WorkspaceSideSheet>
  </>;
}
