import { cleanup, fireEvent, render, screen, waitFor, within } from '@testing-library/react';
import { afterEach, expect, test, vi } from 'vitest';
import App from '../App';

afterEach(() => {
  cleanup();
  vi.useRealTimers();
  window.localStorage.clear();
  window.history.replaceState(null, '', '/');
  delete window.__LINGNIU_APP_CONFIG__;
  vi.restoreAllMocks();
});

test('mobile shell prioritizes customer vehicle service content before sidebar navigation', () => {
  vi.spyOn(globalThis, 'fetch').mockImplementation(async () => ({
    ok: true,
    json: async () => ({
      data: { items: [], total: 0, limit: 20, offset: 0, linkHealth: [], runtime: { requestTimeoutMs: 5000 } },
      traceId: 'trace-test',
      timestamp: 1783094400000
    })
  }) as Response);

  render(<App />);
  expect(screen.getByTestId('vehicle-platform-shell')).toHaveAttribute('data-mobile-layout', 'content-first');
});

test('renders vehicle platform shell', () => {
  vi.spyOn(globalThis, 'fetch').mockImplementation(async () => ({
    ok: true,
    json: async () => ({
      data: { items: [], total: 0, limit: 20, offset: 0, linkHealth: [], runtime: { requestTimeoutMs: 5000 } },
      traceId: 'trace-test',
      timestamp: 1783094400000
    })
  }) as Response);

  render(<App />);
  expect(screen.getByText('车辆服务中台')).toBeInTheDocument();
  expect(screen.getByText('实时地图 / 轨迹里程 / 告警通知')).toBeInTheDocument();
  expect(screen.getByText('客户主线')).toBeInTheDocument();
  expect(screen.getByText('先看车，再查时间窗，最后导出和通知。')).toBeInTheDocument();
  expect(screen.getByText('客户任务分发台')).toBeInTheDocument();
  expect(screen.getByText('输入 VIN / 车牌 / 手机号后，地图、轨迹、里程、导出和告警都围绕同一辆车服务。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户任务 地图看车 车辆在哪里' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户任务 轨迹回放 这段时间怎么跑' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户任务 里程统计 里程怎么算' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户任务 数据导出 证据怎么给客户' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户任务 告警通知 异常谁处理' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户主线 车辆地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户主线 轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户主线 里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户主线 数据导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户主线 告警通知' })).toBeInTheDocument();
  expect(screen.getByText('车辆监控')).toBeInTheDocument();
  expect(screen.getByText('地图看车、实时状态、异常优先')).toBeInTheDocument();
  expect(screen.getAllByText('车辆服务').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('按 VIN、车牌、手机号管理车辆')).toBeInTheDocument();
  expect(screen.getAllByText('轨迹里程').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('回放轨迹、里程统计、导出数据')).toBeInTheDocument();
  expect(screen.getAllByText('告警通知').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('接入运维')).toBeInTheDocument();
  expect(screen.getAllByText('客户').length).toBeGreaterThanOrEqual(3);
  expect(screen.getByText('内部')).toBeInTheDocument();
  expect(screen.getByText('客户主流程外查看链路质量')).toBeInTheDocument();
  expect(screen.getByText('内部证据区')).toBeInTheDocument();
  expect(screen.getByText('运维证据只在客户问题需要解释时展开，不作为客户默认入口。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '展开内部证据区' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '内部证据区 运维质量' })).toBeInTheDocument();
  expect(screen.getAllByText('车辆总览').length).toBeGreaterThanOrEqual(1);
  expect(getComputedStyle(document.body).minWidth).not.toBe('1280px');
});

test('sidebar frames the first navigation group as vehicle monitoring for customers', () => {
  vi.spyOn(globalThis, 'fetch').mockImplementation(async () => ({
    ok: true,
    json: async () => ({
      data: { items: [], total: 0, limit: 20, offset: 0, linkHealth: [], runtime: { requestTimeoutMs: 5000 } },
      traceId: 'trace-test',
      timestamp: 1783094400000
    })
  }) as Response);

  render(<App />);

  expect(screen.getByText('车辆监控')).toBeInTheDocument();
  expect(screen.getByText('地图看车、实时状态、异常优先')).toBeInTheDocument();
  expect(screen.getAllByText('车辆总览').length).toBeGreaterThanOrEqual(1);
  expect(screen.queryByText('实时运营')).not.toBeInTheDocument();
  expect(screen.queryByText('运营总览')).not.toBeInTheDocument();
});

test('dashboard opens with a customer vehicle service path instead of protocol entry points', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            linkHealth: [],
            kafkaLag: 0,
            activeConnections: 15,
            redisOnlineKeys: 92,
            tdengineWritable: true,
            mysqlWritable: true,
            runtime: { requestTimeoutMs: 5000, amapWebJsConfigured: true, amapSecurityProxyEnabled: true, amapSecurityCodeExposed: false }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 92,
            activeToday: 120,
            frameToday: 21000,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            boundVehicles: 988,
            onlineVehicles: 92,
            singleSourceVehicles: 170,
            multiSourceVehicles: 220,
            noDataVehicles: 15,
            identityRequiredVehicles: 9,
            serviceStatuses: [],
            protocols: [],
            missingSources: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 8, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByRole('heading', { name: '客户车辆服务路径' })).toBeInTheDocument();
  expect(screen.getByText('客户入口只回答车辆问题：在线在哪里、这段时间怎么跑、里程是否可信、异常是否通知、数据能否交付。')).toBeInTheDocument();
  expect(screen.getByText('32960 / 808 / MQTT 只是证据通道')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务路径 地图监控 在线在哪里 打开地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务路径 时间窗 这段时间怎么跑 开始复盘' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务路径 里程统计 里程是否可信 核对统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务路径 告警通知 异常是否通知 查看告警' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务路径 数据导出 数据能否交付 导出证据' })).toBeInTheDocument();
});

test('dashboard keeps advanced customer workbench collapsed behind the service path', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            linkHealth: [],
            kafkaLag: 0,
            activeConnections: 15,
            redisOnlineKeys: 92,
            tdengineWritable: true,
            mysqlWritable: true,
            runtime: { requestTimeoutMs: 5000, amapWebJsConfigured: true, amapSecurityProxyEnabled: true, amapSecurityCodeExposed: false }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 92,
            activeToday: 120,
            frameToday: 21000,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            boundVehicles: 988,
            onlineVehicles: 92,
            singleSourceVehicles: 170,
            multiSourceVehicles: 220,
            noDataVehicles: 15,
            identityRequiredVehicles: 9,
            serviceStatuses: [],
            protocols: [],
            missingSources: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 8, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByRole('heading', { name: '客户车辆服务路径' })).toBeInTheDocument();
  const summary = screen.getByText('客户进阶工作台');
  const details = summary.closest('details');
  expect(details).toBeInTheDocument();
  expect(details).not.toHaveAttribute('open');
  expect(within(details as HTMLElement).getByText('车辆服务指挥台')).toBeInTheDocument();
  expect(within(details as HTMLElement).getByText('车辆地图态势')).toBeInTheDocument();
});

test('dashboard gives a customer next best action directly on the first screen', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            linkHealth: [],
            kafkaLag: 0,
            activeConnections: 15,
            redisOnlineKeys: 92,
            tdengineWritable: true,
            mysqlWritable: true,
            runtime: { requestTimeoutMs: 5000, amapWebJsConfigured: true, amapSecurityProxyEnabled: true, amapSecurityCodeExposed: false }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 92,
            activeToday: 120,
            frameToday: 21000,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            boundVehicles: 988,
            onlineVehicles: 92,
            singleSourceVehicles: 170,
            multiSourceVehicles: 220,
            noDataVehicles: 15,
            identityRequiredVehicles: 9,
            serviceStatuses: [],
            protocols: [],
            missingSources: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 8, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByRole('heading', { name: '客户车辆服务路径' })).toBeInTheDocument();
  const nextActionRegion = screen.getByLabelText('建议下一步');
  expect(within(nextActionRegion).getByText('建议下一步')).toBeInTheDocument();
  expect(within(nextActionRegion).getByText('先处理告警车辆')).toBeInTheDocument();
  expect(within(nextActionRegion).getByRole('button', { name: '建议下一步 优先 先处理告警车辆 进入告警' })).toBeInTheDocument();
  expect(within(nextActionRegion).getByRole('button', { name: '建议下一步 范围 选择一辆车复盘 车辆中心' })).toBeInTheDocument();
  expect(within(nextActionRegion).getByRole('button', { name: '建议下一步 时间窗 按同一范围交付数据 复制复盘包' })).toBeInTheDocument();
});

test('dashboard exposes customer service scope delivery actions on the first screen', async () => {
  window.history.replaceState(null, '', '/#/dashboard?keyword=VIN-SCOPE-001&protocol=JT808&dateFrom=2026-07-01&dateTo=2026-07-03');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            linkHealth: [],
            kafkaLag: 0,
            activeConnections: 15,
            redisOnlineKeys: 92,
            tdengineWritable: true,
            mysqlWritable: true,
            runtime: { requestTimeoutMs: 5000, amapWebJsConfigured: true, amapSecurityProxyEnabled: true, amapSecurityCodeExposed: false }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 92,
            activeToday: 120,
            frameToday: 21000,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            boundVehicles: 988,
            onlineVehicles: 92,
            singleSourceVehicles: 170,
            multiSourceVehicles: 220,
            noDataVehicles: 15,
            identityRequiredVehicles: 9,
            serviceStatuses: [],
            protocols: [],
            missingSources: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 8, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByRole('heading', { name: '客户车辆服务路径' })).toBeInTheDocument();
  const scopeRegion = screen.getByLabelText('客户服务范围');
  expect(within(scopeRegion).getByText('当前服务范围')).toBeInTheDocument();
  expect(within(scopeRegion).getByText('VIN-SCOPE-001 / JT808 / 2026-07-01 至 2026-07-03')).toBeInTheDocument();
  expect(within(scopeRegion).getByRole('button', { name: '客户服务范围 复制范围' })).toBeInTheDocument();
  expect(within(scopeRegion).getByRole('button', { name: '客户服务范围 保存视图' })).toBeInTheDocument();
});

test('dashboard presents customer vehicle service modules before operations details', async () => {
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            linkHealth: [],
            kafkaLag: 0,
            activeConnections: 15,
            redisOnlineKeys: 92,
            tdengineWritable: true,
            mysqlWritable: true,
            runtime: { requestTimeoutMs: 5000, amapWebJsConfigured: true, amapSecurityProxyEnabled: true, amapSecurityCodeExposed: false }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 88,
            activeToday: 119,
            frameToday: 32000,
            issueVehicles: 3,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1000,
            boundVehicles: 960,
            onlineVehicles: 88,
            singleSourceVehicles: 180,
            multiSourceVehicles: 230,
            noDataVehicles: 20,
            identityRequiredVehicles: 10,
            serviceStatuses: [],
            protocols: [],
            missingSources: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 8, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  const servicePath = await screen.findByLabelText('客户车辆服务路径');
  const serviceOverview = within(servicePath).getByLabelText('客户服务总览');
  expect(within(serviceOverview).getByText('客户服务总览')).toBeInTheDocument();
  expect(within(serviceOverview).getByText('地图、轨迹、里程、导出是客户能直接使用的车辆服务；协议来源只进入证据和追溯。')).toBeInTheDocument();
  expect(within(serviceOverview).getByRole('button', { name: '客户服务总览 车辆地图 进入地图' })).toBeInTheDocument();
  expect(within(serviceOverview).getByRole('button', { name: '客户服务总览 轨迹回放 回放轨迹' })).toBeInTheDocument();
  expect(within(serviceOverview).getByRole('button', { name: '客户服务总览 里程统计 查看统计' })).toBeInTheDocument();
  expect(within(serviceOverview).getByRole('button', { name: '客户服务总览 历史数据 查询导出' })).toBeInTheDocument();
});

test('topbar frames customer work as a vehicle service delivery strip', () => {
  vi.spyOn(globalThis, 'fetch').mockImplementation(async () => ({
    ok: true,
    json: async () => ({
      data: { items: [], total: 0, limit: 20, offset: 0, linkHealth: [], runtime: { requestTimeoutMs: 5000 } },
      traceId: 'trace-test',
      timestamp: 1783094400000
    })
  }) as Response);

  render(<App />);

  expect(screen.getByText('车辆服务交付条')).toBeInTheDocument();
  expect(screen.getByText('服务对象')).toBeInTheDocument();
  expect(screen.getByText('未选择车辆')).toBeInTheDocument();
  expect(screen.getByText('服务时间')).toBeInTheDocument();
  expect(screen.getByText('实时范围')).toBeInTheDocument();
  expect(screen.getByText('服务动作')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务交付条 地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务交付条 轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务交付条 里程' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务交付条 导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务交付条 告警' })).toBeInTheDocument();
});

test('topbar exposes a mobile vehicle service shortcut dock', () => {
  vi.spyOn(globalThis, 'fetch').mockImplementation(async () => ({
    ok: true,
    json: async () => ({
      data: { items: [], total: 0, limit: 20, offset: 0, linkHealth: [], runtime: { requestTimeoutMs: 5000 } },
      traceId: 'trace-test',
      timestamp: 1783094400000
    })
  }) as Response);

  render(<App />);

  expect(screen.getByRole('navigation', { name: '移动端车辆服务快捷入口' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '移动端车辆服务快捷入口 地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '移动端车辆服务快捷入口 轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '移动端车辆服务快捷入口 里程' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '移动端车辆服务快捷入口 导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '移动端车辆服务快捷入口 告警' })).toBeInTheDocument();
});

test('exposes AMap operations shortcuts when map key is configured', async () => {
  window.__LINGNIU_APP_CONFIG__ = { amapWebJsKey: 'amap-web-key' };
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            linkHealth: [],
            kafkaLag: 0,
            redisOnlineKeys: 0,
            tdengineWritable: true,
            mysqlWritable: true,
            runtime: { requestTimeoutMs: 5000, platformRelease: 'platform-20260704153357' }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 0,
            activeToday: 0,
            frameToday: 0,
            issueVehicles: 0,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 0,
            boundVehicles: 0,
            onlineVehicles: 0,
            singleSourceVehicles: 0,
            multiSourceVehicles: 0,
            noDataVehicles: 0,
            identityRequiredVehicles: 0,
            archiveIncompleteVehicles: 0,
            serviceStatuses: [],
            protocols: [],
            missingSources: [],
            archiveMissingFields: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { vehicleCount: 0, recordCount: 0, sourceCount: 0, totalMileageKm: 0, averageMileagePerVin: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { issueVehicleCount: 0, issueRecordCount: 0, errorCount: 0, warningCount: 0, protocols: [], issueTypes: [] },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/notification-plan') || path.includes('/api/alert-events/notification-plan')) {
      return {
        ok: true,
        json: async () => ({
          data: { rules: [], policies: [], priorityIssues: [], activeRuleCount: 0, p0RuleCount: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/ops/source-readiness')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            onlineVehicles: 208,
            kafkaLag: 42,
            activeConnections: 1234,
            redisOnlineKeys: 368,
            platformRelease: 'platform-ops-test',
            sources: [
              {
                protocol: 'GB32960',
                role: '整车与氢能实时数据主来源',
                online: 73,
                total: 340,
                onlineRate: 21.47,
                missingVehicles: 693,
                severity: 'warning',
                status: '覆盖不足',
                evidence: '在线 73/340，缺失车辆 693，Kafka Lag 42',
                action: '核对现代和交投平台转发清单。',
                acceptance: '缺失来源车辆下降，单源车辆可解释。',
                vehiclesHash: '#/vehicles?protocol=GB32960&missingProtocol=GB32960',
                realtimeHash: '#/realtime?protocol=GB32960&online=online',
                historyHash: '#/history-query?protocol=GB32960&tab=raw&includeFields=true',
                alertHash: '#/alert-events?protocol=GB32960'
              },
              {
                protocol: 'JT808',
                role: '位置、总里程和设备在线主来源',
                online: 174,
                total: 424,
                onlineRate: 41.04,
                missingVehicles: 609,
                severity: 'ok',
                status: '生产可用',
                evidence: '在线 174/424，缺失车辆 609，Kafka Lag 42',
                action: '保持实时、轨迹、原始记录和统计链路巡检。',
                acceptance: '车辆服务可回查实时、轨迹和原始记录。',
                vehiclesHash: '#/vehicles?protocol=JT808&missingProtocol=JT808',
                realtimeHash: '#/realtime?protocol=JT808&online=online',
                historyHash: '#/history-query?protocol=JT808&tab=raw&includeFields=true',
                alertHash: '#/alert-events?protocol=JT808'
              }
            ]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 50, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('高德地图就绪')).toBeInTheDocument();
  expect(await screen.findByText((content) => content.includes('platform-20260704153357'))).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '顶部地图态势' }));
  expect(window.location.hash).toBe('#/map');
  expect(await screen.findByRole('heading', { name: '车辆实时地图' })).toBeInTheDocument();
  expect(screen.getAllByText('关注车辆').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('车辆列表')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '顶部实时监控' }));
  expect(window.location.hash).toBe('#/realtime');
  fireEvent.click(screen.getByRole('button', { name: '顶部轨迹回放' }));
  expect(window.location.hash.startsWith('#/history')).toBe(true);
  fireEvent.click(screen.getByRole('button', { name: '顶部数据导出' }));
  expect(window.location.hash.startsWith('#/history-query')).toBe(true);
  expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('tab')).toBe('raw');
  expect(await screen.findByRole('heading', { name: '历史查询导出' })).toBeInTheDocument();
  expect(screen.getByText('默认查询最近一天，不预置任何车辆；输入 VIN、车牌或手机号后进入单车查询导出。')).toBeInTheDocument();
  expect(screen.queryByDisplayValue('LB9A32A24R0LS1426')).not.toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '顶部里程统计' }));
  expect(window.location.hash.startsWith('#/mileage')).toBe(true);
  fireEvent.click(await screen.findByRole('button', { name: '顶部告警事件正常' }));
  expect(window.location.hash).toBe('#/alert-events');
});

test('shows global alert pressure from notification plan in topbar', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            linkHealth: [],
            kafkaLag: 0,
            redisOnlineKeys: 0,
            tdengineWritable: true,
            mysqlWritable: true,
            runtime: { requestTimeoutMs: 5000, platformRelease: 'platform-20260704180000' }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/notification-plan') || path.includes('/api/alert-events/notification-plan')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            summary: { issueVehicleCount: 5, issueRecordCount: 8, errorCount: 2, warningCount: 6, protocols: [], issueTypes: [] },
            rules: [],
            policies: [],
            priorityIssues: [],
            activeRuleCount: 4,
            p0RuleCount: 2
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/ops/source-readiness')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            onlineVehicles: 208,
            kafkaLag: 42,
            activeConnections: 1234,
            redisOnlineKeys: 368,
            platformRelease: 'platform-ops-test',
            sources: [
              {
                protocol: 'GB32960',
                role: '整车与氢能实时数据主来源',
                online: 73,
                total: 340,
                onlineRate: 21.47,
                missingVehicles: 693,
                severity: 'warning',
                status: '覆盖不足',
                evidence: '在线 73/340，缺失车辆 693，Kafka Lag 42',
                action: '核对现代和交投平台转发清单。',
                acceptance: '缺失来源车辆下降，单源车辆可解释。',
                vehiclesHash: '#/vehicles?protocol=GB32960&missingProtocol=GB32960',
                realtimeHash: '#/realtime?protocol=GB32960&online=online',
                historyHash: '#/history-query?protocol=GB32960&tab=raw&includeFields=true',
                alertHash: '#/alert-events?protocol=GB32960'
              },
              {
                protocol: 'JT808',
                role: '位置、总里程和设备在线主来源',
                online: 73,
                total: 340,
                onlineRate: 21.47,
                missingVehicles: 693,
                severity: 'warning',
                status: '覆盖不足',
                evidence: '在线 73/340，缺失车辆 693，Kafka Lag 42',
                action: '补齐手机号绑定和注册缺失车辆。',
                acceptance: '未知 VIN 注册下降，位置与里程持续可查。',
                vehiclesHash: '#/vehicles?protocol=JT808&missingProtocol=JT808',
                realtimeHash: '#/realtime?protocol=JT808&online=online',
                historyHash: '#/history-query?protocol=JT808&tab=raw&includeFields=true',
                alertHash: '#/alert-events?protocol=JT808'
              }
            ]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/ops/source-readiness')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            onlineVehicles: 208,
            kafkaLag: 0,
            activeConnections: 1234,
            redisOnlineKeys: 368,
            platformRelease: 'platform-dashboard-test',
            sources: [
              {
                protocol: 'GB32960',
                role: '整车与氢能实时数据主来源',
                online: 73,
                total: 340,
                onlineRate: 21.47,
                missingVehicles: 693,
                severity: 'warning',
                status: '覆盖不足',
                evidence: '在线 73/340，缺失车辆 693，Kafka Lag 0',
                action: '核对现代和交投平台转发清单。',
                acceptance: '缺失来源车辆下降，单源车辆可解释。',
                vehiclesHash: '#/vehicles?protocol=GB32960&missingProtocol=GB32960',
                realtimeHash: '#/realtime?protocol=GB32960&online=online',
                historyHash: '#/history-query?protocol=GB32960&tab=raw&includeFields=true',
                alertHash: '#/alert-events?protocol=GB32960'
              },
              {
                protocol: 'JT808',
                role: '位置、总里程和设备在线主来源',
                online: 174,
                total: 424,
                onlineRate: 41.04,
                missingVehicles: 609,
                severity: 'warning',
                status: '覆盖不足',
                evidence: '在线 174/424，缺失车辆 609，Kafka Lag 0',
                action: '补齐手机号绑定和注册缺失车辆。',
                acceptance: '未知 VIN 注册下降，位置与里程持续可查。',
                vehiclesHash: '#/vehicles?protocol=JT808&missingProtocol=JT808',
                realtimeHash: '#/realtime?protocol=JT808&online=online',
                historyHash: '#/history-query?protocol=JT808&tab=raw&includeFields=true',
                alertHash: '#/alert-events?protocol=JT808'
              },
              {
                protocol: 'YUTONG_MQTT',
                role: '宇通派生工况与燃料电池补充来源',
                online: 11,
                total: 50,
                onlineRate: 22,
                missingVehicles: 983,
                severity: 'ok',
                status: '生产可用',
                evidence: '在线 11/50，缺失车辆 983，Kafka Lag 0',
                action: '保持实时、轨迹、原始记录和统计链路巡检。',
                acceptance: '车辆服务可回查实时、轨迹和原始记录。',
                vehiclesHash: '#/vehicles?protocol=YUTONG_MQTT&missingProtocol=YUTONG_MQTT',
                realtimeHash: '#/realtime?protocol=YUTONG_MQTT&online=online',
                historyHash: '#/history-query?protocol=YUTONG_MQTT&tab=raw&includeFields=true',
                alertHash: '#/alert-events?protocol=YUTONG_MQTT'
              }
            ]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 8, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  const alertButton = await screen.findByRole('button', { name: '顶部告警事件 P0 2 / 活跃 4' });
  expect(alertButton).toHaveTextContent('告警事件 P0 2 / 活跃 4');
  fireEvent.click(alertButton);
  expect(window.location.hash).toBe('#/alert-events');
});

test('shows vehicle service status distribution on dashboard', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 3,
            activeToday: 4,
            frameToday: 1286320,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [
              { status: 'healthy', title: '服务正常', count: 12 },
              { status: 'degraded', title: '来源不完整', count: 39 }
            ],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('车辆服务覆盖')).toBeInTheDocument();
  expect(screen.getAllByText('服务状态').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('告警事件预览')).toBeInTheDocument();
});

test('dashboard renders vehicle service summary metrics', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 3,
            activeToday: 4,
            frameToday: 1286320,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            boundVehicles: 1024,
            onlineVehicles: 208,
            singleSourceVehicles: 391,
            multiSourceVehicles: 181,
            noDataVehicles: 461,
            identityRequiredVehicles: 9,
            archiveIncompleteVehicles: 366,
            serviceStatuses: [
              { status: 'healthy', title: '服务正常', count: 161 },
              { status: 'degraded', title: '来源不完整', count: 41 }
            ],
            protocols: [
              { protocol: 'JT808', online: 157, total: 388 },
              { protocol: 'YUTONG_MQTT', online: 0, total: 0 }
            ],
            missingSources: [{ protocol: 'YUTONG_MQTT', count: 1033 }],
            archiveMissingFields: [{ field: 'phone', title: '缺手机号', count: 353 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('总车辆')).toBeInTheDocument();
  expect(screen.getAllByText('1,033').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('今日活跃')).toBeInTheDocument();
  expect(screen.getAllByText('有效定位').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('告警车辆').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('今日数据')).toBeInTheDocument();
  expect(screen.getByText('档案不完整')).toBeInTheDocument();
  expect(screen.getByText('366')).toBeInTheDocument();
  expect(screen.queryByText('NaN%')).not.toBeInTheDocument();
  expect(fetchMock).toHaveBeenCalledWith('/api/vehicle-service/summary', undefined);
});

test('dashboard presents one vehicle service operating posture', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            linkHealth: [],
            kafkaLag: 0,
            activeConnections: 0,
            capacityFindings: [],
            redisOnlineKeys: 0,
            tdengineWritable: true,
            mysqlWritable: true,
            runtime: {
              requestTimeoutMs: 5000,
              amapWebJsConfigured: true,
              amapApiConfigured: true,
              amapSecurityProxyEnabled: true,
              amapSecurityCodeExposed: false,
              amapSecurityServiceHost: '/_AMapService'
            }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 3,
            activeToday: 4,
            frameToday: 1286320,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            boundVehicles: 1024,
            onlineVehicles: 208,
            singleSourceVehicles: 391,
            multiSourceVehicles: 181,
            noDataVehicles: 461,
            identityRequiredVehicles: 9,
            serviceStatuses: [],
            protocols: [],
            missingSources: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 8, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('车辆服务入口')).toBeInTheDocument();
  expect(screen.getAllByText('208 / 1,033 在线').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('今日活跃')).toBeInTheDocument();
  expect(screen.getAllByText('4').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('7 告警事件')).toBeInTheDocument();
});

test('dashboard prioritizes customer vehicle service command over data sources', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            linkHealth: [],
            kafkaLag: 0,
            activeConnections: 0,
            capacityFindings: [],
            redisOnlineKeys: 0,
            tdengineWritable: true,
            mysqlWritable: true,
            runtime: {
              requestTimeoutMs: 5000,
              amapWebJsConfigured: true,
              amapApiConfigured: true,
              amapSecurityProxyEnabled: true,
              amapSecurityCodeExposed: false,
              amapSecurityServiceHost: '/_AMapService'
            }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 3,
            activeToday: 4,
            frameToday: 1286320,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            boundVehicles: 1024,
            onlineVehicles: 208,
            singleSourceVehicles: 391,
            multiSourceVehicles: 181,
            noDataVehicles: 461,
            identityRequiredVehicles: 9,
            serviceStatuses: [],
            protocols: [],
            missingSources: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 8, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect((await screen.findAllByText('车辆服务指挥台')).length).toBeGreaterThan(0);
  expect(screen.getByText('10 秒内回答客户最关心的事：车辆在哪里、哪辆车异常、这段时间怎么跑、数据能不能导出。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务指挥台 看车在哪 实时地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务指挥台 查这段时间 轨迹里程' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务指挥台 导出数据 历史导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务指挥台 处理告警 告警事件' })).toBeInTheDocument();
  expect(screen.getAllByText('客户今日运营台').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('今天先看在线和地图，再处理告警、复盘轨迹、核对里程，最后把证据导出给客户。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户今日运营台 车辆在线 208 在线 打开地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户今日运营台 地图可见 0 有坐标 实时地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户今日运营台 轨迹复盘 4 活跃 回放轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户今日运营台 里程报表 1 天窗口 核对里程' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户今日运营台 告警通知 7 告警 闭环处理' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: /复制今日服务简报/ })).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: /复制今日服务简报/ }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【客户今日车辆服务简报】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('在线车辆：208 / 1,033'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('地图可见：0 辆有坐标'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('今日活跃：4'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('告警车辆：7'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆地图：http://localhost:3000/#/map?online=online'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('里程报表：http://localhost:3000/#/mileage'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('历史导出：http://localhost:3000/#/history-query?tab=raw&includeFields=true'));
  expect(screen.getByText('客户车队运营总控')).toBeInTheDocument();
  expect(screen.getByText('客户首屏只保留车辆地图、在线状态、轨迹回放、里程统计、数据导出和告警通知，协议来源退到证据层。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车队运营总控 实时地图 208 在线 打开地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车队运营总控 轨迹回放 4 活跃 回放轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车队运营总控 里程统计 1 天窗口 核对里程' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车队运营总控 数据导出 1,286,320 今日数据 导出报表' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车队运营总控 告警通知 7 告警 闭环处理' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: /复制运营总控包/ })).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: /复制运营总控包/ }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【客户车队运营总控包】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('首屏能力：实时地图 / 轨迹回放 / 里程统计 / 数据导出 / 告警通知'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('协议来源：仅作为证据层，不作为客户主入口'));
  expect(screen.getByRole('button', { name: '车辆服务指挥台优先队列 告警待处理 7 辆 告警事件' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务指挥台优先队列 无有效定位 208 辆 打开地图' })).toBeInTheDocument();
  expect(screen.getAllByText('车辆服务指挥台')[0].compareDocumentPosition(screen.getByText('车辆地图态势')) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
  expect(await screen.findByText('车辆服务驾驶舱')).toBeInTheDocument();
  expect(screen.getByText('先看车辆，不先看协议；客户进来先知道哪些车在线、在哪里、今天跑了多少、哪些异常要通知。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务驾驶舱 车辆总览 1,033 辆 车辆中心' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务驾驶舱 实时可见 208 在线 地图看车' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务驾驶舱 轨迹复盘 4 今日活跃 回放轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务驾驶舱 里程统计 1 天窗口 里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务驾驶舱 数据交付 1,286,320 今日数据 导出证据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务驾驶舱 告警通知 7 告警 闭环处理' })).toBeInTheDocument();
  expect(await screen.findByText('车辆服务闭环')).toBeInTheDocument();
  expect(screen.getByText('客户从首页开始，只需要按这五步完成找车、看车、复盘、统计、导出和通知。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务闭环 1 选车 1,033 车辆中心' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务闭环 2 实时看车 208 在线 实时地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务闭环 3 锁定时间窗 1 天窗口 自定义时间' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务闭环 4 复盘里程 4 活跃 轨迹里程' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务闭环 5 交付闭环 7 告警 导出通知' })).toBeInTheDocument();
  expect(screen.getByText('车辆地图态势')).toBeInTheDocument();
  expect(screen.getByText('先看在线车辆在哪里，再决定是否回放轨迹、核对里程、导出数据或通知处理。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆地图态势 打开实时地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆地图态势 回放轨迹' })).toBeInTheDocument();
  expect(screen.getByText('车辆地图态势').compareDocumentPosition(screen.getByText('客户车辆服务驾驶舱')) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
  expect(screen.getByText('客户服务配置和说明')).toBeInTheDocument();
  expect(screen.getByText('把蓝图、常用视图、处置说明和服务闭环收起，默认首页只保留地图、车辆、时间窗、导出和告警。')).toBeInTheDocument();
  expect(screen.getByText('客户服务配置和说明').compareDocumentPosition(screen.getByText('客户调度运营首页')) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
  expect(await screen.findByText('客户车辆服务总控台')).toBeInTheDocument();
  expect(screen.getByText('来源证据收敛层')).toBeInTheDocument();
  expect(screen.getByText('协议来源只用于证明车辆服务可信度，客户主流程仍然从车辆、地图、时间窗、统计、导出和告警进入。')).toBeInTheDocument();
  expect(screen.getByText('三个接入来源统一沉到证据层，首页只回答客户怎么监控车辆、回放轨迹、核对里程、导出数据和处理告警。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务总控台 实时地图 208 在线 进入地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务总控台 轨迹回放 4 活跃 回放轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务总控台 里程统计 1,033 车辆 查里程' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务总控台 时间窗监控 1 天窗口 自定义时间' })).toBeInTheDocument();
  expect(screen.getByText('同一时间窗贯穿轨迹、统计、历史导出和告警复盘。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务总控台 数据导出 1,286,320 今日数据 查询导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务总控台 告警通知 7 告警 闭环告警' })).toBeInTheDocument();
  expect(screen.getByText('来源证据：GB32960 / JT808 / YUTONG_MQTT')).toBeInTheDocument();
  expect(screen.getByText('客户车队分组工作台')).toBeInTheDocument();
  expect(screen.getByText('按客户责任范围组织车辆池：在线运营、告警处置、身份维护和报表交付都能保存成复用视图。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车队分组工作台 在线运营组 208 在线 打开地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车队分组工作台 告警处置组 7 告警 告警通知' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车队分组工作台 身份维护组 9 待绑定 维护车辆' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车队分组工作台 报表交付组 1,286,320 今日数据 历史导出' })).toBeInTheDocument();
  expect(screen.getByText('客户服务 SLA 承诺')).toBeInTheDocument();
  expect(screen.getByText('把断链发现、异常通知、恢复验收和数据交付变成客户能看懂的服务承诺。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户服务 SLA 承诺 断链发现 1 分钟内 在线状态' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户服务 SLA 承诺 异常通知 7 告警 通知闭环' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户服务 SLA 承诺 恢复验收 轨迹/统计可查 验收证据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户服务 SLA 承诺 数据交付 1,286,320 今日数据 导出证据' })).toBeInTheDocument();
  expect(screen.getByText('车辆健康与能耗工作台')).toBeInTheDocument();
  expect(screen.getByText('把实时工况、SOC/氢能字段、异常维护和里程能耗放到同一辆车的服务视角里。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆健康与能耗工作台 实时工况 208 在线 实时监控' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆健康与能耗工作台 SOC/氢能字段 1,286,320 今日数据 字段证据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆健康与能耗工作台 异常维护 7 告警 告警事件' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆健康与能耗工作台 里程能耗 1 天窗口 里程统计' })).toBeInTheDocument();
  expect(screen.getByText('地图服务能力清单')).toBeInTheDocument();
  expect(screen.getByText('把高德 Web JS 和服务端 API 翻译成客户能使用的车辆服务能力。')).toBeInTheDocument();
  expect(screen.getByText('实时底图')).toBeInTheDocument();
  expect(screen.getByText('轨迹路径')).toBeInTheDocument();
  expect(screen.getByText('区域围栏')).toBeInTheDocument();
  expect(screen.getByText('地址解析')).toBeInTheDocument();
  expect(await screen.findByText('客户车辆服务中枢')).toBeInTheDocument();
  expect(screen.getByText('客户只关心车怎么服务：在哪里、是否在线、这段时间怎么跑、里程是否可信、异常是否已通知、数据能否交付。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务中枢 实时地图 208 在线 看车在哪' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务中枢 轨迹回放 4 活跃 回放路线' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务中枢 里程统计 1 天窗口 核对里程' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务中枢 数据导出 1,286,320 今日数据 交付证据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务中枢 告警通知 7 告警 闭环通知' })).toBeInTheDocument();
  expect(screen.getByText('运维接入、队列、存储、协议解析降级为证据层，默认不打断客户主流程。')).toBeInTheDocument();
});

test('dashboard presents a customer dispatch operations home', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            linkHealth: [],
            kafkaLag: 0,
            activeConnections: 0,
            capacityFindings: [],
            redisOnlineKeys: 0,
            tdengineWritable: true,
            mysqlWritable: true,
            runtime: {
              requestTimeoutMs: 5000,
              amapWebJsConfigured: true,
              amapApiConfigured: true,
              amapSecurityProxyEnabled: true,
              amapSecurityCodeExposed: false
            }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 3,
            activeToday: 4,
            frameToday: 1286320,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            boundVehicles: 1024,
            onlineVehicles: 208,
            singleSourceVehicles: 391,
            multiSourceVehicles: 181,
            noDataVehicles: 461,
            identityRequiredVehicles: 9,
            serviceStatuses: [],
            protocols: [],
            missingSources: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 8, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('客户调度运营首页')).toBeInTheDocument();
  expect(screen.getByText('值班人员进来先看地图态势和异常队列，再进入轨迹回放、时间窗统计、历史导出和告警通知。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户调度运营首页 实时地图 0 有定位 打开地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户调度运营首页 今日重点 7 告警 处理告警' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '调度运营任务 看全域位置 208 在线 实时地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '调度运营任务 回放重点轨迹 4 活跃 轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '调度运营任务 核对时间窗里程 1,033 车辆 里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '调度运营任务 导出客户证据 1,286,320 帧 历史导出' })).toBeInTheDocument();
});

test('dashboard frames history query and export as a customer delivery workbench', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  vi.setSystemTime(new Date('2026-07-05T12:00:00+08:00'));
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            linkHealth: [],
            kafkaLag: 0,
            activeConnections: 0,
            capacityFindings: [],
            redisOnlineKeys: 0,
            tdengineWritable: true,
            mysqlWritable: true,
            runtime: {
              requestTimeoutMs: 5000,
              amapWebJsConfigured: true,
              amapApiConfigured: true,
              amapSecurityProxyEnabled: true,
              amapSecurityCodeExposed: false
            }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 3,
            activeToday: 4,
            frameToday: 1286320,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            boundVehicles: 1024,
            onlineVehicles: 208,
            singleSourceVehicles: 391,
            multiSourceVehicles: 181,
            noDataVehicles: 461,
            identityRequiredVehicles: 9,
            serviceStatuses: [],
            protocols: [],
            missingSources: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 8, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('客户查询导出交付台')).toBeInTheDocument();
  expect(screen.getByText('先锁定车辆和时间窗，再选择轨迹、里程、历史明细或告警说明，最终交付一份客户能看懂的数据包。')).toBeInTheDocument();
  expect(screen.getByText('当前交付范围')).toBeInTheDocument();
  expect(screen.getAllByText('全部车辆 / 全部数据通道 / 2026-07-04 至 2026-07-05').length).toBeGreaterThan(0);
  expect(screen.getByText('常用时间范围')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户常用时间范围 最近一天 2026-07-04 至 2026-07-05 应用' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户常用时间范围 最近三天 2026-07-02 至 2026-07-05 应用' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户常用时间范围 本周 2026-06-29 至 2026-07-05 应用' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户常用时间范围 自定义复盘 当前范围 应用' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户查询导出交付台 轨迹回放 4 活跃 回放轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户查询导出交付台 里程统计 1,033 车辆 里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户查询导出交付台 历史明细 1,286,320 帧 历史导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户查询导出交付台 告警说明 7 告警 告警通知' })).toBeInTheDocument();
});

test('dashboard separates customer vehicle service from operations evidence', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            linkHealth: [],
            kafkaLag: 0,
            activeConnections: 0,
            capacityFindings: [],
            redisOnlineKeys: 281,
            tdengineWritable: true,
            mysqlWritable: true,
            runtime: {
              requestTimeoutMs: 5000,
              amapWebJsConfigured: true,
              amapApiConfigured: true,
              amapSecurityProxyEnabled: true,
              amapSecurityCodeExposed: false
            }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 73,
            activeToday: 4,
            frameToday: 1286320,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            boundVehicles: 1024,
            onlineVehicles: 208,
            singleSourceVehicles: 391,
            multiSourceVehicles: 181,
            noDataVehicles: 461,
            identityRequiredVehicles: 9,
            serviceStatuses: [],
            protocols: [],
            missingSources: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 8, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('客户主流程')).toBeInTheDocument();
  expect(screen.getByText('默认展示车辆地图、时间窗、里程、导出和告警；三类数据源只作为二级证据。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户主流程 实时地图 208 在线 打开地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户主流程 时间窗复盘 1 天窗口 查询导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户主流程 里程统计 1,033 车辆 里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户主流程 告警通知 7 告警 通知处理' })).toBeInTheDocument();
  expect(screen.getByText('运维证据层')).toBeInTheDocument();
  expect(screen.getByText('用于解释车辆服务不可用的链路、队列、缓存、存储和来源证据，不作为客户默认入口。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '运维证据层 链路健康 正常 查看告警' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '运维证据层 消息积压 0 查看运维' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '运维证据层 缓存在线 281 缓存证据' })).toBeInTheDocument();
});

test('dashboard guides customer custom time window service path', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            linkHealth: [],
            kafkaLag: 0,
            activeConnections: 0,
            capacityFindings: [],
            redisOnlineKeys: 0,
            tdengineWritable: true,
            mysqlWritable: true,
            runtime: {
              requestTimeoutMs: 5000,
              amapWebJsConfigured: true,
              amapApiConfigured: true,
              amapSecurityProxyEnabled: true,
              amapSecurityCodeExposed: false
            }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 3,
            activeToday: 4,
            frameToday: 1286320,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            boundVehicles: 1024,
            onlineVehicles: 208,
            singleSourceVehicles: 391,
            multiSourceVehicles: 181,
            noDataVehicles: 461,
            identityRequiredVehicles: 9,
            serviceStatuses: [],
            protocols: [],
            missingSources: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 8, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('时间窗服务路径')).toBeInTheDocument();
  expect(screen.getByText('锁定同一辆车和同一时间范围后，按轨迹、里程、历史证据和告警说明形成客户可复盘的交付路径。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗服务路径 1 锁定范围 1 天窗口 应用时间窗' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗服务路径 2 回放轨迹 4 活跃 轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗服务路径 3 核对里程 1,033 车辆 里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗服务路径 4 导出证据 1,286,320 帧 数据导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗服务路径 5 告警说明 7 告警 告警通知' })).toBeInTheDocument();
  expect(screen.getByText('时间窗下一步行动条')).toBeInTheDocument();
  expect(screen.getByText('把客户选择的车辆和时间范围转成下一步动作：先处理风险，再核对轨迹、里程、历史证据和交付说明。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗下一步 首要动作 告警复盘' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗下一步 交付状态 带风险交付' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗下一步 证据状态 1,286,320 帧' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗下一步 服务范围 全域巡检' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗下一步动作 处理告警 告警复盘' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗下一步动作 导出证据 历史导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗下一步动作 核对里程 里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗下一步动作 复制复盘 交付包' })).toBeInTheDocument();
});

test('time monitor route exposes a dedicated customer time window workspace', async () => {
  window.history.replaceState(null, '', '/#/time-monitor?keyword=%E7%B2%A4A%E6%97%B6%E9%97%B4%E7%AA%97&protocol=JT808&dateFrom=2026-07-01&dateTo=2026-07-03');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            linkHealth: [],
            kafkaLag: 0,
            activeConnections: 0,
            capacityFindings: [],
            redisOnlineKeys: 0,
            tdengineWritable: true,
            mysqlWritable: true,
            runtime: { requestTimeoutMs: 5000, amapWebJsConfigured: true, amapSecurityProxyEnabled: true, amapSecurityCodeExposed: false }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 6,
            activeToday: 8,
            frameToday: 9988,
            issueVehicles: 2,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 12,
            boundVehicles: 12,
            onlineVehicles: 6,
            singleSourceVehicles: 3,
            multiSourceVehicles: 4,
            noDataVehicles: 2,
            identityRequiredVehicles: 0,
            serviceStatuses: [],
            protocols: [],
            missingSources: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 8, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByRole('heading', { name: '自定义时间监控' })).toBeInTheDocument();
  expect(screen.getByText('把客户选择的一辆车和一个时间窗固定下来，同时进入实时地图、轨迹回放、里程统计、历史导出和告警复盘。')).toBeInTheDocument();
  expect(screen.getAllByText('时间窗监控').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('当前监控范围：粤A时间窗 / JT808 / 2026-07-01 至 2026-07-03')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗监控入口 轨迹回放 3 天窗口' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗监控入口 里程统计 3 天窗口' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗监控入口 历史导出 9,988 帧' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗监控入口 告警复盘 2 告警' })).toBeInTheDocument();
  expect(screen.getByText('时间窗复盘 SLA 作业台')).toBeInTheDocument();
  expect(screen.getByText('把一个时间窗变成可交付作业：谁处理、多久交付、交付哪些证据、异常如何升级。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗复盘SLA 责任动作 客户复盘 复制作业' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗复盘SLA 交付时限 3 天窗口 轨迹复盘' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗复盘SLA 交付物 轨迹/统计/导出/告警 导出证据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗复盘SLA 升级条件 2 告警 告警复盘' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: /复制时间窗作业单/ })).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: /复制时间窗作业单/ }));
  await waitFor(() => {
    expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【客户时间窗复盘作业单】'));
  });
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('作业范围：粤A时间窗 / JT808 / 2026-07-01 至 2026-07-03'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('交付SLA：3 天窗口内完成轨迹复盘、统计核对、历史导出和告警说明'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('交付物：轨迹回放链接 / 里程统计链接 / 历史导出链接 / 告警复盘链接'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('升级条件：2 告警车辆需要先进入告警复盘'));
});

test('dashboard exposes vehicle data center capability matrix', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/source-readiness')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            onlineVehicles: 208,
            kafkaLag: 0,
            activeConnections: 1234,
            redisOnlineKeys: 368,
            platformRelease: 'platform-dashboard-test',
            sources: [
              {
                protocol: 'GB32960',
                role: '整车与氢能实时数据主来源',
                online: 73,
                total: 340,
                onlineRate: 21.47,
                missingVehicles: 693,
                severity: 'warning',
                status: '覆盖不足',
                evidence: '在线 73/340，缺失车辆 693，Kafka Lag 0',
                action: '核对现代和交投平台转发清单。',
                acceptance: '缺失来源车辆下降，单源车辆可解释。',
                vehiclesHash: '#/vehicles?protocol=GB32960&missingProtocol=GB32960',
                realtimeHash: '#/realtime?protocol=GB32960&online=online',
                historyHash: '#/history-query?protocol=GB32960&tab=raw&includeFields=true',
                alertHash: '#/alert-events?protocol=GB32960'
              },
              {
                protocol: 'JT808',
                role: '位置、总里程和设备在线主来源',
                online: 174,
                total: 424,
                onlineRate: 41.04,
                missingVehicles: 609,
                severity: 'warning',
                status: '覆盖不足',
                evidence: '在线 174/424，缺失车辆 609，Kafka Lag 0',
                action: '补齐手机号绑定和注册缺失车辆。',
                acceptance: '未知 VIN 注册下降，位置与里程持续可查。',
                vehiclesHash: '#/vehicles?protocol=JT808&missingProtocol=JT808',
                realtimeHash: '#/realtime?protocol=JT808&online=online',
                historyHash: '#/history-query?protocol=JT808&tab=raw&includeFields=true',
                alertHash: '#/alert-events?protocol=JT808'
              },
              {
                protocol: 'YUTONG_MQTT',
                role: '宇通派生工况与燃料电池补充来源',
                online: 11,
                total: 50,
                onlineRate: 22,
                missingVehicles: 983,
                severity: 'ok',
                status: '生产可用',
                evidence: '在线 11/50，缺失车辆 983，Kafka Lag 0',
                action: '保持实时、轨迹、原始记录和统计链路巡检。',
                acceptance: '车辆服务可回查实时、轨迹和原始记录。',
                vehiclesHash: '#/vehicles?protocol=YUTONG_MQTT&missingProtocol=YUTONG_MQTT',
                realtimeHash: '#/realtime?protocol=YUTONG_MQTT&online=online',
                historyHash: '#/history-query?protocol=YUTONG_MQTT&tab=raw&includeFields=true',
                alertHash: '#/alert-events?protocol=YUTONG_MQTT'
              }
            ]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            linkHealth: [],
            kafkaLag: 0,
            activeConnections: 0,
            capacityFindings: [],
            redisOnlineKeys: 0,
            tdengineWritable: true,
            mysqlWritable: true,
            runtime: {
              requestTimeoutMs: 5000,
              amapWebJsConfigured: true,
              amapApiConfigured: true,
              amapSecurityProxyEnabled: true,
              amapSecurityCodeExposed: false,
              amapSecurityServiceHost: '/_AMapService'
            }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 3,
            activeToday: 4,
            frameToday: 1286320,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            boundVehicles: 1024,
            onlineVehicles: 208,
            singleSourceVehicles: 391,
            multiSourceVehicles: 181,
            noDataVehicles: 461,
            identityRequiredVehicles: 9,
            serviceStatuses: [],
            protocols: [],
            missingSources: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { vehicleCount: 0, recordCount: 0, sourceCount: 0, totalMileageKm: 0, averageMileagePerVin: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { issueVehicleCount: 0, issueRecordCount: 0, errorCount: 0, warningCount: 0, protocols: [], issueTypes: [] },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/notification-plan') || path.includes('/api/alert-events/notification-plan')) {
      return {
        ok: true,
        json: async () => ({
          data: { rules: [], policies: [], priorityIssues: [], activeRuleCount: 0, p0RuleCount: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 8, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  const renderDashboard = async () => {
    window.history.replaceState(null, '', '/#/dashboard');
    render(<App />);
    expect(await screen.findByText('车辆服务能力矩阵')).toBeInTheDocument();
  };

  await renderDashboard();
  expect(screen.getAllByText('车辆服务工作台').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByRole('region', { name: '客户车辆服务驾驶舱' })).toBeInTheDocument();
  expect(screen.getByText('客户进来先看车辆，不先看协议。')).toBeInTheDocument();
  expect(screen.getByText('把实时地图、轨迹回放、里程统计、历史导出和告警通知收敛成一个可交付的车辆服务入口。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务驾驶舱 车辆在哪里 0 有定位 打开地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务驾驶舱 轨迹能否回放 4 活跃 轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务驾驶舱 里程怎么算 1,033 车辆 里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务驾驶舱 数据怎么导出 1,286,320 今日数据 历史导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务驾驶舱 异常谁处理 7 告警 告警通知' })).toBeInTheDocument();
  expect(screen.getByText('客户车辆服务中台蓝图')).toBeInTheDocument();
  expect(screen.getByText('参考主流车联网平台的信息架构，把 Live Map、Route Replay、Geofence Alerts、Reports Export 和 Vehicle Health 收敛成客户可直接使用的车辆服务。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务中台蓝图 实时地图 208 在线 打开地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务中台蓝图 轨迹回放 4 活跃 回放轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务中台蓝图 围栏告警 7 告警 告警通知' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务中台蓝图 报表导出 1,286,320 今日数据 历史导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务中台蓝图 车辆健康 1,033 车辆 车辆中心' })).toBeInTheDocument();
  expect(screen.getByRole('region', { name: '客户常用视图库' })).toBeInTheDocument();
  expect(screen.getByText('一个车辆服务入口，而不是三个数据源入口。')).toBeInTheDocument();
  expect(screen.getByText('把车队范围、时间窗、地图态势、轨迹回放、里程统计、导出模板和告警订阅保存成客户可复用的车辆服务入口。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户常用视图库 在线车队视图 208 在线 打开地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户常用视图库 轨迹日报模板 4 活跃 历史导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户常用视图库 里程周报模板 1,033 车辆 里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户常用视图库 告警订阅视图 7 告警 通知闭环' })).toBeInTheDocument();
  expect(screen.getByRole('region', { name: '十秒车辆处置台' })).toBeInTheDocument();
  expect(screen.getByText('登录后先知道哪三类车辆最需要处理，再进入地图、车辆中心或告警通知。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '十秒车辆处置台 告警车辆 7 辆 处理告警' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '十秒车辆处置台 无数据车辆 461 辆 查车辆' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '十秒车辆处置台 身份未绑定 9 辆 维护绑定' })).toBeInTheDocument();
  expect(screen.getByText('现代车队运营台')).toBeInTheDocument();
  expect(screen.getByText('一屏完成车辆地图、轨迹回放、里程统计、数据导出和告警闭环。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '现代车队运营台 Live Map 208 在线 打开地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '现代车队运营台 Route Replay 4 活跃 回放轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '现代车队运营台 Mileage 1,033 车辆 查里程' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '现代车队运营台 Export 1,286,320 今日数据 导出数据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '现代车队运营台 Alerts 7 告警 通知闭环' })).toBeInTheDocument();
  expect(screen.getByText('高德地图车辆服务底座')).toBeInTheDocument();
  expect(screen.getByText('高德能力不单独成为页面目标，而是支撑同一辆车的实时看车、路线回放、区域告警和时间窗数据交付。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '高德地图车辆服务底座 实时看车 0 有定位 打开地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '高德地图车辆服务底座 路线回放 1 天窗口 轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '高德地图车辆服务底座 区域告警 7 告警 告警通知' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '高德地图车辆服务底座 时间窗导出 1,286,320 今日数据 历史导出' })).toBeInTheDocument();
  expect(screen.getByText('车辆监控中心')).toBeInTheDocument();
  expect(screen.getByText('先看车在哪里，再处理轨迹、统计、告警和导出')).toBeInTheDocument();
  expect(screen.getByText('客户主流程围绕车辆本身：地图看车、实时状态、时间窗复盘、里程统计、历史数据导出和告警通知。接入来源只作为追溯证据。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆监控指标 车辆总数 1,033' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆监控指标 在线车辆 208' })).toBeInTheDocument();
  expect(screen.getByText('客户任务导航')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户任务导航 看车在哪 实时地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户任务导航 查这段时间 轨迹里程' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户任务导航 导出数据 历史导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户任务导航 处理告警 告警事件' })).toBeInTheDocument();
  expect(screen.getByText('客户问题处理台')).toBeInTheDocument();
  expect(screen.getByText('先回答客户问题，再下钻到车辆、轨迹、统计和证据。')).toBeInTheDocument();
  expect(screen.getByText('客户首先关心的是车辆、位置、时间、里程、告警和导出；接入来源只放在证据层。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户问题处理台 现在车辆在哪里 打开实时地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户问题处理台 这段时间跑了多少 查里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户问题处理台 轨迹能不能回放 打开轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户问题处理台 需要导出哪些证据 查询历史导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户问题处理台 哪些车需要通知处理 查看告警事件' })).toBeInTheDocument();
  expect(screen.getByText('车辆服务优先级队列')).toBeInTheDocument();
  expect(screen.getByText('先处理影响客户看车、查轨迹、算里程和收通知的问题车辆。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务优先级队列 告警待处理 7 辆 告警事件' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务优先级队列 无有效定位 208 辆 打开地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务优先级队列 身份未绑定 9 辆 维护绑定' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务优先级队列 暂无数据 461 辆 车辆列表' })).toBeInTheDocument();
  expect(screen.getByText('今日先处理')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务建议 先处理告警车辆 进入告警' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务建议 选择一辆车复盘 车辆中心' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务建议 按同一范围交付数据 复制复盘包' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆工作流 实时看车 实时地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆工作流 轨迹复盘 轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆工作流 里程统计 里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆工作流 数据交付 历史导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆工作流 告警通知 告警事件' })).toBeInTheDocument();
  expect(screen.getByText('客户常问')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户常问 这辆车现在在哪里？ 打开地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户常问 这段时间跑了多少公里？ 查里程' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户常问 能不能回放轨迹？ 轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户常问 数据能导出给客户吗？ 查询导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户常问 哪些车需要马上处理？ 告警事件' })).toBeInTheDocument();
  expect(screen.getByText('来源只作为可信度证据，不作为客户主导航。')).toBeInTheDocument();
  expect(screen.getByText('同一时间窗会同步用于轨迹回放、里程统计、历史查询和告警复盘。')).toBeInTheDocument();
  expect(screen.getByText('客户交付驾驶舱')).toBeInTheDocument();
  expect(screen.getByText('把车辆位置、时间窗、里程统计、历史证据和告警通知组织成客户可以直接使用的服务包。')).toBeInTheDocument();
  expect(screen.getByText('客户打开首页后，先知道今天能交付什么，再选择地图、轨迹、里程、导出或通知。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户交付驾驶舱 在线车辆交付 实时地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户交付驾驶舱 时间窗复盘 轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户交付驾驶舱 数据证据交付 历史导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户交付驾驶舱 告警通知交付 告警事件' })).toBeInTheDocument();
  expect(screen.getAllByText('车辆服务指挥台').length).toBeGreaterThan(0);
  expect(screen.getByText('全域车辆监控')).toBeInTheDocument();
  expect(screen.getByText('异常车辆闭环')).toBeInTheDocument();
  expect(screen.getByText('自定义时间复盘')).toBeInTheDocument();
  expect(screen.getByText('数据交付与导出')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务指挥台 全域车辆监控 打开车辆地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务指挥台 异常车辆闭环 告警事件' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务指挥台 自定义时间复盘 轨迹复盘' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务指挥台 数据交付与导出 历史导出' })).toBeInTheDocument();
  expect(screen.getByText('客户常用服务')).toBeInTheDocument();
  expect(screen.getAllByText('车辆地图').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('历史数据').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('今日车辆服务任务板')).toBeInTheDocument();
  expect(screen.getByText('先看全域在线')).toBeInTheDocument();
  expect(screen.getByText('再看重点车辆')).toBeInTheDocument();
  expect(screen.getByText('按时间窗复盘')).toBeInTheDocument();
  expect(screen.getByText('导出证据数据')).toBeInTheDocument();
  expect(screen.getByText('闭环告警通知')).toBeInTheDocument();
  expect(screen.getByText('更多车辆服务入口')).toBeInTheDocument();
  expect(screen.getByText('车辆服务入口')).toBeInTheDocument();
  expect(screen.getByText('客户服务路径')).toBeInTheDocument();
  expect(screen.getByText('从车辆出发，完成监控、复盘、统计、导出和告警闭环')).toBeInTheDocument();
  expect(screen.getByText('客户看到的是车辆服务结果；接入来源只在需要追溯时作为数据通道出现。')).toBeInTheDocument();
  expect(screen.getByText('找车')).toBeInTheDocument();
  expect(screen.getByText('看位置')).toBeInTheDocument();
  expect(screen.getAllByText('复盘时间').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('交付数据')).toBeInTheDocument();
  expect(screen.getByText('闭环异常')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务入口 找车 车辆中心' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务入口 看位置 实时地图' })).toBeInTheDocument();
  expect(screen.getAllByRole('button', { name: '复制客户服务说明' }).length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('自定义时间窗复盘')).toBeInTheDocument();
  expect(screen.getAllByText('应用时间窗').length).toBeGreaterThan(0);
  expect(screen.getByText('重置最近一天')).toBeInTheDocument();
  expect(screen.getByText('当前时间窗')).toBeInTheDocument();
  expect(screen.getByText('客户时间窗复盘包')).toBeInTheDocument();
  expect(screen.getByText('客户问某辆车、某段时间发生了什么时，直接按这个时间窗串起轨迹、统计、历史数据、告警通知和地图定位。')).toBeInTheDocument();
  expect(screen.getByText('车辆范围')).toBeInTheDocument();
  expect(screen.getAllByText('复盘时间').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('统计证据')).toBeInTheDocument();
  expect(screen.getByText('时间窗服务核对')).toBeInTheDocument();
  expect(screen.getByText('交付给客户前，先确认范围、轨迹、导出和异常闭环四件事都能讲清楚。')).toBeInTheDocument();
  expect(screen.getByText('范围可解释')).toBeInTheDocument();
  expect(screen.getByText('轨迹可复盘')).toBeInTheDocument();
  expect(screen.getByText('数据可导出')).toBeInTheDocument();
  expect(screen.getByText('异常可闭环')).toBeInTheDocument();
  expect(screen.getByText('时间窗交付决策')).toBeInTheDocument();
  expect(screen.getAllByText('带风险交付').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('存在告警车辆，交付时需要附带告警闭环和原始证据。').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByRole('button', { name: '时间窗交付 客户结论 复制复盘包' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗交付 证据准备 历史导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗交付 异常说明 告警复盘' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '复制复盘包' })).toBeInTheDocument();
  expect(screen.getByText('这个范围会同步带入轨迹、统计、历史证据和告警复盘，适合客户问询、BI 核对和异常解释。')).toBeInTheDocument();
  expect(screen.getByText('查看轨迹')).toBeInTheDocument();
  expect(screen.getAllByText('里程统计').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('历史查询导出')).toBeInTheDocument();
  expect(screen.getAllByText('轨迹回放').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('里程统计').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('历史证据').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('告警复盘').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByRole('button', { name: '时间窗作业 轨迹回放 打开轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗作业 历史证据 导出证据' })).toBeInTheDocument();
  expect(screen.getByText('今天车辆轨迹')).toBeInTheDocument();
  expect(screen.getByText('历史时间窗')).toBeInTheDocument();
  expect(screen.getAllByText('里程统计').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('告警复盘').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('运维和证据层')).toBeInTheDocument();
  expect(screen.getByText('来源覆盖、地图配置、告警预览和实时证据默认收起，客户主流程先看车辆监控、轨迹、统计、告警和导出。')).toBeInTheDocument();
  expect(screen.getAllByText('车辆服务工作台').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('地图监控能力')).toBeInTheDocument();
  expect(screen.getByText('车联网场景导航')).toBeInTheDocument();
  expect(screen.getByText('高德 Web JS')).toBeInTheDocument();
  expect(screen.getByText('服务端 API')).toBeInTheDocument();
  expect(screen.getByText('安全代理')).toBeInTheDocument();
  expect(screen.getByText('安全码暴露')).toBeInTheDocument();
  expect(screen.getAllByText('历史证据').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('已配置').length).toBeGreaterThanOrEqual(2);
  expect(screen.getByText('已启用')).toBeInTheDocument();
  expect(screen.getByText('未暴露')).toBeInTheDocument();
  expect(screen.getAllByText('实时地图').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('实时监控').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('轨迹回放').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('历史数据查询').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('告警事件触发与通知').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('里程统计').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('告警事件与通知')).toBeInTheDocument();
  expect(screen.getByText('区间闭合复核')).toBeInTheDocument();
  expect(screen.getByText('高德轨迹证据')).toBeInTheDocument();
  expect(screen.getByText('1,033 车辆口径')).toBeInTheDocument();
  expect(screen.getAllByText('在线 208').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('在线态势 208').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('有效定位 0').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('今日帧 1,286,320').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('告警 7').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('统计车辆 1,033').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('确认车辆是否在线、位置是否可信、必要接入来源是否齐全。')).toBeInTheDocument();
  expect(screen.getByText('按车辆复盘位置、速度、里程和断点，定位平台转发或车辆异常。')).toBeInTheDocument();
  expect(screen.getByText('优先返回必要字段，避免大 JSON 拖慢查询')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '复制功能蓝图' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【车辆服务中台功能蓝图】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('32960 / 808 / 宇通 MQTT 都只是车辆接入来源'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆规模：1,033；在线：208；告警：7'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('高德地图：Web JS 已配置；服务端 API 已配置；安全代理 已启用；安全码未暴露'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. 实时监控'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('SLA：目标 0-1 秒内进入实时视图'));

  fireEvent.click(screen.getByRole('button', { name: '客户工作流 车辆地图' }));
  expect(window.location.hash).toBe('#/map?online=online');
  cleanup();

  await renderDashboard();
  fireEvent.click(screen.getByRole('button', { name: '今日任务 先看全域在线 打开实时地图' }));
  expect(window.location.hash).toBe('#/map?online=online');
  cleanup();

  await renderDashboard();
  fireEvent.click(screen.getByRole('button', { name: '今日任务 按时间窗复盘 里程统计' }));
  expect(window.location.hash.startsWith('#/mileage')).toBe(true);
  expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('dateFrom')).toBeTruthy();
  cleanup();

  await renderDashboard();
  fireEvent.click(screen.getByRole('button', { name: '今日任务 导出证据数据 历史导出' }));
  expect(window.location.hash.startsWith('#/history-query')).toBe(true);
  expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('includeFields')).toBe('true');
  cleanup();

  await renderDashboard();
  fireEvent.click(screen.getByRole('button', { name: '客户工作流 历史数据' }));
  expect(window.location.hash.startsWith('#/history-query')).toBe(true);
  expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('tab')).toBe('raw');
  expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('includeFields')).toBe('true');
  cleanup();

  await renderDashboard();
  fireEvent.click(screen.getByRole('button', { name: '地图运营 实时监控' }));
  expect(window.location.hash).toBe('#/map?online=online');
  cleanup();

  await renderDashboard();
  fireEvent.click(screen.getByRole('button', { name: '地图运营 历史证据' }));
  expect(window.location.hash.startsWith('#/history-query')).toBe(true);
  expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('includeFields')).toBe('true');
  cleanup();

  await renderDashboard();
  fireEvent.click(screen.getByRole('button', { name: '查看轨迹' }));
  expect(window.location.hash.startsWith('#/history')).toBe(true);
  expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('dateFrom')).toBeTruthy();
  expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('dateTo')).toBeTruthy();
  cleanup();

  await renderDashboard();
  fireEvent.click(screen.getAllByRole('button', { name: '里程统计' })[0]);
  expect(window.location.hash.startsWith('#/mileage')).toBe(true);
  expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('dateFrom')).toBeTruthy();
  expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('dateTo')).toBeTruthy();
  cleanup();

  await renderDashboard();
  fireEvent.click(screen.getByRole('button', { name: '复制复盘包' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【客户时间窗复盘包】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('平台能力'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('交付结论：带风险交付；存在告警车辆，交付时需要附带告警闭环和原始证据。'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('时间窗判定：1 天窗口'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('交付核对：'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. 范围可解释'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('下一步动作：'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. 客户结论：带风险交付'));
  cleanup();

  await renderDashboard();
  fireEvent.click(screen.getByRole('button', { name: '历史查询导出' }));
  expect(window.location.hash.startsWith('#/history-query')).toBe(true);
  expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('tab')).toBe('raw');
  expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('includeFields')).toBe('true');
  expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('dateFrom')).toBeTruthy();
  expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('dateTo')).toBeTruthy();
  cleanup();

  await renderDashboard();
  fireEvent.click(screen.getByRole('button', { name: '时间监控 历史时间窗' }));
  expect(window.location.hash.startsWith('#/history-query')).toBe(true);
  expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('includeFields')).toBe('true');
  expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('dateFrom')).toBeTruthy();
  expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('dateTo')).toBeTruthy();
  cleanup();

  await renderDashboard();
  fireEvent.click(screen.getByRole('button', { name: '时间监控 里程统计' }));
  expect(window.location.hash.startsWith('#/mileage')).toBe(true);
  cleanup();

  await renderDashboard();
  fireEvent.click(screen.getByRole('button', { name: '作业台 实时监控 打开实时地图' }));
  expect(window.location.hash).toBe('#/map?online=online');
  cleanup();

  await renderDashboard();
  fireEvent.click(screen.getByRole('button', { name: '场景导航 实时监控 实时监控' }));
  expect(window.location.hash).toBe('#/realtime?online=online');
  cleanup();

  await renderDashboard();
  fireEvent.click(screen.getByRole('button', { name: '场景导航 轨迹回放 原始记录' }));
  expect(window.location.hash.startsWith('#/history-query')).toBe(true);
  expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('includeFields')).toBe('true');
  cleanup();

  await renderDashboard();
  fireEvent.click(screen.getByRole('button', { name: '能力入口 实时监控' }));
  expect(window.location.hash).toBe('#/realtime?online=online');
  cleanup();

  await renderDashboard();
  fireEvent.click(screen.getByRole('button', { name: '能力入口 实时地图' }));
  expect(window.location.hash).toBe('#/map?online=online');
  cleanup();

  await renderDashboard();
  fireEvent.click(screen.getByRole('button', { name: '能力入口 轨迹回放' }));
  expect(window.location.hash.startsWith('#/history')).toBe(true);
  expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('tab')).toBeNull();
  cleanup();

  await renderDashboard();
  fireEvent.click(screen.getByRole('button', { name: '能力入口 历史数据查询' }));
  expect(window.location.hash.startsWith('#/history-query')).toBe(true);
  expect(new URLSearchParams(window.location.hash.split('?')[1] ?? '').get('tab')).toBe('raw');
  cleanup();

  await renderDashboard();
  fireEvent.click(screen.getByRole('button', { name: '能力入口 告警事件触发与通知' }));
  expect(window.location.hash).toBe('#/alert-events');
  cleanup();

  await renderDashboard();
  fireEvent.click(screen.getByRole('button', { name: '能力入口 里程统计' }));
  expect(window.location.hash.startsWith('#/mileage')).toBe(true);
}, 10000);

test('dashboard exposes vehicle command center map actions', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, activeConnections: 0, capacityFindings: [], redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 3,
            activeToday: 4,
            frameToday: 1286320,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            boundVehicles: 1024,
            onlineVehicles: 208,
            singleSourceVehicles: 391,
            multiSourceVehicles: 181,
            noDataVehicles: 461,
            identityRequiredVehicles: 9,
            serviceStatuses: [],
            protocols: [],
            missingSources: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/realtime/vehicles')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [
              {
                vin: 'VIN-COMMAND-001',
                plate: '粤A指挥1',
                protocols: ['GB32960', 'JT808', 'YUTONG_MQTT'],
                primaryProtocol: 'GB32960',
                sourceCount: 3,
                onlineSourceCount: 2,
                online: true,
                longitude: 113.2,
                latitude: 23.1,
                speedKmh: 31,
                socPercent: 78,
                totalMileageKm: 100.5,
                lastSeen: '2026-07-03 20:12:10'
              },
              {
                vin: 'VIN-COMMAND-002',
                plate: '粤A指挥2',
                protocols: ['JT808'],
                primaryProtocol: 'JT808',
                sourceCount: 3,
                onlineSourceCount: 0,
                online: false,
                longitude: 0,
                latitude: 0,
                lastSeen: '2026-07-03 19:55:10'
              }
            ],
            total: 2,
            limit: 8,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/notification-plan') || path.includes('/api/alert-events/notification-plan')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            summary: {
              issueVehicleCount: 2,
              issueRecordCount: 2,
              errorCount: 1,
              warningCount: 1,
              protocols: [{ name: 'VEHICLE_SERVICE', count: 1 }, { name: 'JT808', count: 1 }],
              issueTypes: [{ name: 'NO_SOURCE', count: 1 }, { name: 'VIN_MISSING', count: 1 }]
            },
            rules: [],
            policies: [{ name: 'P0 实时中断', target: '接入运维 + 业务责任人', channel: '站内告警 / 邮件 / 企业微信', condition: '无来源、VIN 缺失、存储不可写', escalationMinutes: 30, acceptanceCriteria: '来源恢复并持续 10 分钟，车辆服务可查到实时与历史证据' }],
            priorityIssues: [],
            activeRuleCount: 1,
            p0RuleCount: 1
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{ vin: 'VIN-COMMAND-002', plate: '粤A指挥2', protocol: 'VEHICLE_SERVICE', issueType: 'NO_SOURCE', severity: 'error', detail: '无来源', lastSeen: '2026-07-03 19:55:10' }],
            total: 1,
            limit: 5,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 8, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('实时态势地图')).toBeInTheDocument();
  expect(screen.getByText('客户车辆监控台')).toBeInTheDocument();
  expect(screen.getByText('先回答客户最常问的车辆问题：车在哪里、这段时间怎么跑、里程怎么算、数据怎么导出、异常谁处理。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆监控台 车辆在哪里 1 辆有定位 打开地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆监控台 回放这段时间 4 今日活跃 轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆监控台 里程统计 1,033 辆可统计 查里程' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆监控台 导出客户数据 1,286,320 今日数据 导出数据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆监控台 告警通知 7 告警车辆 通知闭环' })).toBeInTheDocument();
  expect(screen.getByText('在线 1 / 2')).toBeInTheDocument();
  expect(screen.getAllByText('有效定位 1').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('降级/离线 2')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '处理高优先级告警' }));
  expect(window.location.hash).toBe('#/alert-events?issueType=NO_SOURCE');

  cleanup();
  window.history.replaceState(null, '', '/#/dashboard');
  render(<App />);

  expect(await screen.findByText('实时态势地图')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '查看实时态势' }));
  expect(window.location.hash).toBe('#/map?online=online');
});

test('dashboard shows vehicle service action queue', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, activeConnections: 0, capacityFindings: [], redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 3,
            activeToday: 4,
            frameToday: 1286320,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            boundVehicles: 1024,
            onlineVehicles: 208,
            singleSourceVehicles: 391,
            multiSourceVehicles: 181,
            noDataVehicles: 461,
            identityRequiredVehicles: 9,
            archiveIncompleteVehicles: 366,
            serviceStatuses: [],
            protocols: [],
            missingSources: [{ protocol: 'YUTONG_MQTT', count: 983 }],
            archiveMissingFields: [{ field: 'phone', title: '缺手机号', count: 353 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('车辆服务处置队列')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '确认平台转发 461' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '维护身份绑定 9' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '完善车辆档案 366' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '补齐缺手机号 353' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '补齐 YUTONG_MQTT 来源 983' })).toBeInTheDocument();

  fireEvent.click(screen.getByRole('button', { name: '补齐缺手机号 353' }));
  await waitFor(() => {
    expect(window.location.hash).toBe('#/vehicles?archiveMissing=phone');
  });
});

test.each([
  { buttonName: '查看在线车辆', expectedHash: '#/vehicles?online=online' },
  { buttonName: '客户车辆工作流 实时看车 实时地图', expectedHash: '#/map?online=online' },
  { buttonName: '顶部告警事件', expectedHash: '#/alert-events' }
])('dashboard posture opens %s', async ({ buttonName, expectedHash }) => {
  window.history.replaceState(null, '', '/#/dashboard');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, activeConnections: 0, capacityFindings: [], redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 3,
            activeToday: 4,
            frameToday: 1286320,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            boundVehicles: 1024,
            onlineVehicles: 208,
            singleSourceVehicles: 391,
            multiSourceVehicles: 181,
            noDataVehicles: 461,
            identityRequiredVehicles: 9,
            serviceStatuses: [],
            protocols: [],
            missingSources: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { issueVehicleCount: 7, issueRecordCount: 9, errorCount: 1, warningCount: 8, protocols: [], issueTypes: [] },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  fireEvent.click(await screen.findByRole('button', { name: buttonName }));

  await waitFor(() => {
    expect(window.location.hash).toBe(expectedHash);
  });
});

test('dashboard exposes end-to-end operations workflow entries', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, activeConnections: 0, capacityFindings: [], redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 88,
            activeToday: 96,
            frameToday: 1286320,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: [{ name: 'Kafka', status: 'ok' }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            boundVehicles: 1024,
            onlineVehicles: 88,
            singleSourceVehicles: 391,
            multiSourceVehicles: 181,
            noDataVehicles: 0,
            identityRequiredVehicles: 0,
            archiveIncompleteVehicles: 0,
            serviceStatuses: [],
            protocols: [],
            missingSources: [],
            archiveMissingFields: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { issueVehicleCount: 7, issueRecordCount: 9, errorCount: 1, warningCount: 8, protocols: [], issueTypes: [] },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('自定义时间窗复盘')).toBeInTheDocument();
  expect(screen.getByText('今天车辆轨迹')).toBeInTheDocument();
  expect(screen.getByText('历史时间窗')).toBeInTheDocument();
  expect(screen.getAllByText('里程统计').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('告警复盘').length).toBeGreaterThanOrEqual(1);

  fireEvent.click(screen.getByRole('button', { name: '时间监控 今天车辆轨迹' }));

  await waitFor(() => {
    expect(window.location.hash.startsWith('#/history')).toBe(true);
  });
});

test('dashboard surfaces highest priority quality issue with evidence shortcuts', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, activeConnections: 0, capacityFindings: [], redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 88,
            activeToday: 96,
            frameToday: 1286320,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            boundVehicles: 1024,
            onlineVehicles: 88,
            singleSourceVehicles: 391,
            multiSourceVehicles: 181,
            noDataVehicles: 0,
            identityRequiredVehicles: 0,
            archiveIncompleteVehicles: 0,
            serviceStatuses: [],
            protocols: [],
            missingSources: [],
            archiveMissingFields: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: '',
              plate: '粤A告警1',
              phone: '13307795425',
              sourceEndpoint: '115.231.168.135:43625',
              protocol: 'JT808',
              issueType: 'VIN_MISSING',
              severity: 'error',
              lastSeen: '2026-07-03 20:12:10',
              detail: 'JT808 数据缺少 VIN 映射'
            }],
            total: 1,
            limit: 5,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { issueVehicleCount: 7, issueRecordCount: 9, errorCount: 1, warningCount: 8, protocols: [], issueTypes: [] },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('重点车辆服务')).toBeInTheDocument();
  expect(screen.getByText('把实时、轨迹、原始记录、里程和告警集中到同一辆车处理。')).toBeInTheDocument();
  expect(screen.getByText('实时证据')).toBeInTheDocument();
  expect(screen.getAllByText('轨迹证据').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('告警证据')).toBeInTheDocument();
  expect(screen.getAllByText('统计证据').length).toBeGreaterThanOrEqual(1);
  fireEvent.click(screen.getByRole('button', { name: '复制处置卡' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【重点车辆服务处置卡】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆：粤A告警1 / 13307795425'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时监控：http://localhost:3000/#/realtime?keyword=%E7%B2%A4A%E5%91%8A%E8%AD%A61&protocol=JT808'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('原始记录：http://localhost:3000/#/history-query?keyword=%E7%B2%A4A%E5%91%8A%E8%AD%A61&protocol=JT808&tab=raw&includeFields=true'));
  expect(await screen.findByText('最高优先级告警')).toBeInTheDocument();
  expect(screen.getAllByText('粤A告警1 / 13307795425').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('VIN 缺失').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('JT808 数据缺少 VIN 映射').length).toBeGreaterThanOrEqual(1);

  fireEvent.click(screen.getByRole('button', { name: '首页告警轨迹证据' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/history?keyword=%E7%B2%A4A%E5%91%8A%E8%AD%A61&protocol=JT808&dateFrom=2026-07-03&dateTo=2026-07-04');
  });
});

test('dashboard copies highest priority quality issue notification text', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  const createObjectURL = vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:dashboard-snapshot');
  const revokeObjectURL = vi.spyOn(URL, 'revokeObjectURL').mockImplementation(() => undefined);
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, activeConnections: 0, capacityFindings: [], redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 88,
            activeToday: 96,
            frameToday: 1286320,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            boundVehicles: 1024,
            onlineVehicles: 88,
            singleSourceVehicles: 391,
            multiSourceVehicles: 181,
            noDataVehicles: 0,
            identityRequiredVehicles: 0,
            archiveIncompleteVehicles: 0,
            serviceStatuses: [],
            protocols: [],
            missingSources: [],
            archiveMissingFields: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: '',
              plate: '粤A告警1',
              phone: '13307795425',
              sourceEndpoint: '115.231.168.135:43625',
              protocol: 'JT808',
              issueType: 'VIN_MISSING',
              severity: 'error',
              lastSeen: '2026-07-03 20:12:10',
              detail: 'JT808 数据缺少 VIN 映射'
            }],
            total: 1,
            limit: 5,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { issueVehicleCount: 7, issueRecordCount: 9, errorCount: 1, warningCount: 8, protocols: [], issueTypes: [] },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('最高优先级告警')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '首页复制告警通知' }));

  await waitFor(() => {
    expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【P0 告警通知】VIN 缺失'));
  });
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆：粤A告警1 / 13307795425'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('数据来源：JT808'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('详情：JT808 数据缺少 VIN 映射'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹证据：http://localhost:3000/#/history?keyword=%E7%B2%A4A%E5%91%8A%E8%AD%A61&protocol=JT808&dateFrom=2026-07-03&dateTo=2026-07-04'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('原始记录：http://localhost:3000/#/history-query?keyword=%E7%B2%A4A%E5%91%8A%E8%AD%A61&protocol=JT808&tab=raw&dateFrom=2026-07-03&dateTo=2026-07-04&includeFields=true'));

  fireEvent.click(screen.getByRole('button', { name: '复制客户交付摘要' }));

  await waitFor(() => {
    expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【车辆服务中台客户交付摘要】'));
  });
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('在线车辆：88 / 1,033'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('今日活跃：96'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('今日帧量：1,286,320'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('最高告警：P0 VIN 缺失 / 粤A告警1 / 13307795425 / 2026-07-03 20:12:10'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时监控：http://localhost:3000/#/realtime?online=online'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时地图：http://localhost:3000/#/map?online=online'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹回放：http://localhost:3000/#/history?keyword=%E7%B2%A4A%E5%91%8A%E8%AD%A61&protocol=JT808&dateFrom=2026-07-03&dateTo=2026-07-04'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('原始记录：http://localhost:3000/#/history-query?keyword=%E7%B2%A4A%E5%91%8A%E8%AD%A61&protocol=JT808&tab=raw&dateFrom=2026-07-03&dateTo=2026-07-04&includeFields=true'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('告警事件：http://localhost:3000/#/alert-events?issueType=VIN_MISSING'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务：http://localhost:3000/#/detail?keyword=%E7%B2%A4A%E5%91%8A%E8%AD%A61&protocol=JT808'));

  fireEvent.click(screen.getByRole('button', { name: '导出驾驶舱 CSV' }));

  expect(createObjectURL).toHaveBeenCalled();
  expect(revokeObjectURL).toHaveBeenCalledWith('blob:dashboard-snapshot');
});

test('dashboard keeps core vehicle service metrics when preview requests fail', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 3,
            activeToday: 4,
            frameToday: 1286320,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            boundVehicles: 1024,
            onlineVehicles: 208,
            singleSourceVehicles: 391,
            multiSourceVehicles: 181,
            noDataVehicles: 461,
            identityRequiredVehicles: 9,
            serviceStatuses: [],
            protocols: [],
            missingSources: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: false,
        status: 500,
        json: async () => ({
          error: { message: '质量预览失败', detail: 'TDengine timeout' },
          traceId: 'trace-quality',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 8, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('总车辆')).toBeInTheDocument();
  expect(screen.getAllByText('1,033').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('今日活跃')).toBeInTheDocument();
  expect(screen.getAllByText('告警车辆').length).toBeGreaterThanOrEqual(1);
});

test('opens dashboard coverage from service action queue', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 3,
            activeToday: 4,
            frameToday: 1286320,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            boundVehicles: 1024,
            onlineVehicles: 208,
            singleSourceVehicles: 391,
            multiSourceVehicles: 181,
            noDataVehicles: 461,
            identityRequiredVehicles: 9,
            serviceStatuses: [],
            protocols: [{ protocol: 'YUTONG_MQTT', online: 1, total: 8 }],
            missingSources: [{ protocol: 'YUTONG_MQTT', count: 983 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 8, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  fireEvent.click(await screen.findByRole('button', { name: '补齐 YUTONG_MQTT 来源 983' }));

  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('missingProtocol=YUTONG_MQTT'), undefined);
  });
});

test('opens vehicle list filtered by service summary KPI', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { onlineVehicles: 3, activeToday: 4, frameToday: 1286320, issueVehicles: 7, kafkaLag: 0, protocols: [], serviceStatuses: [], linkHealth: [] },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            boundVehicles: 1024,
            onlineVehicles: 208,
            singleSourceVehicles: 391,
            multiSourceVehicles: 181,
            noDataVehicles: 461,
            identityRequiredVehicles: 9,
            serviceStatuses: [],
            protocols: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  fireEvent.click(await screen.findByRole('button', { name: '告警车辆 7' }));

  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/vehicles/coverage?limit=20&offset=0&serviceStatus=degraded'), undefined);
  });
  expect(window.location.hash).toBe('#/vehicles?serviceStatus=degraded');
});

test('shows vehicle service result summary on vehicle list filters', async () => {
  window.history.replaceState(null, '', '/#/vehicles?coverage=multi');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/coverage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 181,
            onlineVehicles: 73,
            singleSourceVehicles: 0,
            multiSourceVehicles: 181,
            noDataVehicles: 4,
            unboundVehicles: 9,
            archiveIncompleteVehicles: 12,
            archiveMissingFields: [{ field: 'phone', title: '缺手机号', count: 7 }],
            missingSources: [{ protocol: 'YUTONG_MQTT', count: 181 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/coverage')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [
              {
                vin: 'VIN-MULTI-001',
                plate: '粤A服务1',
                phone: '13307795425',
                oem: 'G7s',
                protocols: ['GB32960', 'JT808'],
                missingProtocols: ['YUTONG_MQTT'],
                sourceStatus: [
                  { protocol: 'GB32960', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: false, hasRaw: false, hasMileage: false },
                  { protocol: 'JT808', online: false, lastSeen: '2026-07-03 20:10:00', hasRealtime: true, hasHistory: false, hasRaw: false, hasMileage: false },
                  { protocol: 'YUTONG_MQTT', online: false, lastSeen: '', hasRealtime: false, hasHistory: false, hasRaw: false, hasMileage: false }
                ],
                sourceCount: 2,
                onlineSourceCount: 1,
                online: true,
                lastSeen: '2026-07-03 20:12:10',
                bindingStatus: 'bound',
                serviceStatus: {
                  status: 'degraded',
                  severity: 'warning',
                  title: '来源不完整',
                  detail: '1/2 个来源在线',
                  sourceCount: 2,
                  onlineSourceCount: 1
                }
              },
              {
                vin: 'VIN-MISSING-ARCHIVE',
                plate: '粤A缺项',
                phone: '',
                oem: '',
                protocols: ['JT808'],
                missingProtocols: ['GB32960', 'YUTONG_MQTT'],
                sourceStatus: [],
                sourceCount: 1,
                onlineSourceCount: 1,
                online: true,
                lastSeen: '2026-07-03 20:12:09',
                bindingStatus: 'bound'
              }
            ],
            total: 181,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('当前车辆结果')).toBeInTheDocument();
  expect(screen.getByText('客户车队指挥台')).toBeInTheDocument();
  expect(screen.getByText('从车辆池直接判断运营状态：哪些车在线、哪些车可服务、哪些车要回放、统计、导出或触发告警。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车队指挥台 在线率 40.3% 车辆地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车队指挥台 可服务车辆 2 辆 实时监控' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车队指挥台 待处理车辆 13 辆 告警通知' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车队指挥台 当前交付 2 辆 数据导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车队指挥台 轨迹复盘 先选车辆 轨迹回放' })).toBeInTheDocument();
  expect(screen.getByText('高德地图车辆服务底座')).toBeInTheDocument();
  expect(screen.getByText('用地图把车辆监控、轨迹回放、围栏告警、里程统计和历史导出串成客户能理解的一条服务路径。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '高德地图车辆服务底座 实时看车 73 在线 打开地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '高德地图车辆服务底座 轨迹复盘 先选车辆 回放轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '高德地图车辆服务底座 围栏告警 13 待处理 告警通知' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '高德地图车辆服务底座 数据交付 2 当前页 导出清单' })).toBeInTheDocument();
  expect(screen.getByText('客户车辆服务台')).toBeInTheDocument();
  expect(screen.getByText('三个接入源最终服务同一辆车，客户从这里进入找车、实时、轨迹、统计、导出和告警。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务台 车辆池 181 车辆 找车' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务台 实时地图 73 在线 打开地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务台 轨迹回放 先选车辆 回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务台 里程统计 单车后可用 查询里程' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务台 告警通知 13 待处理 查看告警' })).toBeInTheDocument();
  expect(screen.getByText('车辆资产运营台')).toBeInTheDocument();
  expect(screen.getByText('像车队资产系统一样，先判断车辆能不能服务，再进入地图、实时、轨迹、统计、导出和告警。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆资产运营台 资产健康 181 车辆 查看车辆池' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆资产运营台 在线车辆 73 在线 打开实时' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆资产运营台 待处理 13 待处理 异常优先' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆资产运营台 服务交付 2 当前页 复制交付包' })).toBeInTheDocument();
  expect(screen.getByText('车辆池快捷视图')).toBeInTheDocument();
  expect(screen.getByText('按客户服务场景一键切换车辆池：在线运营、离线断链、无来源、身份维护和报表交付。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆池快捷视图 在线运营 73 在线 打开地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆池快捷视图 离线断链 108 离线 排查离线' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆池快捷视图 无来源车辆 4 无来源 平台转发' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆池快捷视图 身份维护 9 未绑定 维护绑定' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆池快捷视图 报表交付 2 当前页 导出清单' })).toBeInTheDocument();
  expect(screen.getByText('车辆服务台')).toBeInTheDocument();
  expect(screen.getByText('客户视角')).toBeInTheDocument();
  expect(screen.getByText('客户查车路径')).toBeInTheDocument();
  expect(screen.getAllByText('车辆池服务').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('从车辆池筛出客户要看的车，再进入单车服务')).toBeInTheDocument();
  expect(screen.getByText('客户侧只关心车辆服务结果，接入来源只作为证据参与筛选和可信度判断。')).toBeInTheDocument();
  expect(screen.getByText('车辆身份')).toBeInTheDocument();
  expect(screen.getByText('VIN / 车牌 / 手机号')).toBeInTheDocument();
  expect(screen.getByText('轨迹与位置')).toBeInTheDocument();
  expect(screen.getAllByText('数据交付').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByRole('button', { name: /复制客户查车路径/ })).toBeInTheDocument();
  expect(screen.getByText('车辆资产作业栏')).toBeInTheDocument();
  expect(screen.getByText('像车队资产系统一样，把车辆清单直接转成最近上报、待关注、身份维护和报表导出。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆资产作业 最近上报 只看在线' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆资产作业 待关注车辆 查看异常' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆资产作业 身份待维护 维护清单' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆资产作业 报表导出 导出车辆' })).toBeInTheDocument();
  expect(screen.getByText('车辆客户常问')).toBeInTheDocument();
  expect(screen.getByText('把车辆池问题翻译成找车、在线、异常和交付动作')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆客户常问 客户要找某辆车怎么办？ 聚焦找车' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆客户常问 现在有哪些车在线？ 只看在线' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆客户常问 哪些车暂时不能服务？ 异常车辆' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆客户常问 车辆清单能交付吗？ 复制交付包' })).toBeInTheDocument();
  expect(screen.getByText('车辆服务决策板')).toBeInTheDocument();
  expect(screen.getAllByText('异常车辆').length).toBeGreaterThan(0);
  expect(screen.getAllByText('4 无来源').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('单车服务')).toBeInTheDocument();
  expect(screen.getByText('输入车辆')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务决策 在线车辆 实时监控' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务决策 异常车辆 维护身份绑定' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务决策 数据交付 导出清单' })).toBeInTheDocument();
  expect(screen.getByText('客户车辆服务交付包')).toBeInTheDocument();
  expect(screen.getByText('客户交付')).toBeInTheDocument();
  expect(screen.getByText('车辆池包')).toBeInTheDocument();
  expect(screen.getByText('把车辆地图、实时监控、轨迹回放、里程统计、告警通知和历史导出放在同一份服务包里，客户不用理解不同接入来源的差异。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务交付 车辆地图 打开地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务交付 轨迹回放 回放轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务交付 里程统计 里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务交付 告警通知 查看告警' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆服务交付 历史导出 导出清单' })).toBeInTheDocument();
  expect(screen.getAllByText('车辆地图').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('里程统计').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('数据导出').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('当前页服务判读')).toBeInTheDocument();
  expect(screen.getByText('建议下一步')).toBeInTheDocument();
  expect(screen.getAllByText('降级可服务').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('2 辆').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('车辆仍可查询，但需要补齐缺失来源后再做跨来源校验。')).toBeInTheDocument();
  expect(screen.getAllByText('181').length).toBeGreaterThanOrEqual(2);
  expect(screen.getByText('过滤车辆')).toBeInTheDocument();
  expect(screen.getByText('73')).toBeInTheDocument();
  expect(screen.getAllByText('在线车辆').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('单源车辆')).toBeInTheDocument();
  expect(screen.getByText('暂无来源车辆')).toBeInTheDocument();
  expect(screen.getByText('待绑定')).toBeInTheDocument();
  expect(screen.getByText('档案不完整')).toBeInTheDocument();
  expect(screen.getAllByText('缺手机号').length).toBeGreaterThanOrEqual(2);
  expect(screen.getByText('处置队列')).toBeInTheDocument();
  expect(screen.getAllByText('P0').length).toBeGreaterThanOrEqual(2);
  expect(screen.getAllByText('P1').length).toBeGreaterThanOrEqual(2);
  expect(screen.getAllByText('已有数据但无法归并到 VIN，先补绑定再看跨来源服务。').length).toBeGreaterThan(0);
  expect(screen.getByText('车辆没有任何来源证据，优先确认平台转发、端口和订阅配置。')).toBeInTheDocument();
  expect(screen.getByText('缺手机号会影响车辆档案检索、绑定确认和运营侧筛选。')).toBeInTheDocument();
  expect(screen.getByText('YUTONG_MQTT 来源缺失会降低跨来源定位、里程和实时判断可信度。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '维护身份绑定 9' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '确认平台转发 4' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '补齐缺手机号 7' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '补齐 YUTONG_MQTT 来源 181' })).toBeInTheDocument();
  expect(screen.getByText('档案完整度')).toBeInTheDocument();
  expect(screen.getByText('4/4')).toBeInTheDocument();
  expect(screen.getAllByText('2/4').length).toBeGreaterThan(0);
  expect(screen.getByText('缺OEM')).toBeInTheDocument();
  expect(screen.getByText('VIN-MULTI-001')).toBeInTheDocument();
  expect(screen.getByText('VIN-MISSING-ARCHIVE')).toBeInTheDocument();
  expect(screen.getAllByText('来源证据').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('GB32960 在线')).toBeInTheDocument();
  expect(screen.getAllByText('JT808 离线').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('YUTONG_MQTT 未接入')).toBeInTheDocument();
  expect(screen.getByText('1/2 来源在线')).toBeInTheDocument();
  expect(screen.getByText('1/2 来源在线，缺 YUTONG_MQTT')).toBeInTheDocument();
  expect(screen.getByText('建议动作')).toBeInTheDocument();
  expect(screen.getByText('补齐 YUTONG_MQTT 来源')).toBeInTheDocument();

  fireEvent.click(screen.getAllByRole('button', { name: /复制交付包/ })[0]);
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【客户车辆服务交付包】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('服务范围：当前车辆池'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆地图：http://localhost:3000/#/map?'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹回放：http://localhost:3000/#/history?tab=location'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('里程统计：http://localhost:3000/#/mileage'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('告警通知：http://localhost:3000/#/alert-events?coverage=multi'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('历史数据导出：http://localhost:3000/#/history?tab=raw&includeFields=true'));
});

test('exports current vehicle coverage page as csv', async () => {
  window.history.replaceState(null, '', '/#/vehicles?missingProtocol=YUTONG_MQTT');
  const createObjectURL = vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:vehicle-coverage');
  const revokeObjectURL = vi.spyOn(URL, 'revokeObjectURL').mockImplementation(() => undefined);
  vi.spyOn(HTMLAnchorElement.prototype, 'click').mockImplementation(() => undefined);
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, activeConnections: 0, capacityFindings: [], redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/coverage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1,
            onlineVehicles: 1,
            singleSourceVehicles: 1,
            multiSourceVehicles: 0,
            noDataVehicles: 0,
            unboundVehicles: 0,
            archiveIncompleteVehicles: 0,
            archiveMissingFields: [],
            missingSources: [{ protocol: 'YUTONG_MQTT', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/coverage')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-EXPORT-VEHICLE',
              plate: '粤A车辆导出',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['JT808'],
              missingProtocols: ['YUTONG_MQTT'],
              sourceStatus: [{ protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }],
              sourceCount: 1,
              onlineSourceCount: 1,
              online: true,
              lastSeen: '2026-07-03 20:12:10',
              bindingStatus: 'bound',
              serviceStatus: { status: 'degraded', severity: 'warning', title: '来源不完整', detail: '缺少 YUTONG_MQTT', sourceCount: 1, onlineSourceCount: 1 },
              sourceConsistency: { sourceCount: 1, onlineSourceCount: 1, locatedSourceCount: 1, missingProtocols: ['YUTONG_MQTT'], mileageDeltaKm: 0, sourceTimeDeltaSeconds: 0, scope: 'overview', status: 'degraded', severity: 'warning', title: '来源不完整', detail: '缺少 YUTONG_MQTT' }
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('VIN-EXPORT-VEHICLE')).toBeInTheDocument();
  expect(screen.getByText('当前页 1 辆')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '导出车辆当前页 CSV' }));

  expect(createObjectURL).toHaveBeenCalled();
  expect(revokeObjectURL).toHaveBeenCalledWith('blob:vehicle-coverage');
});

test('copies vehicle governance summary from vehicle center', async () => {
  window.history.replaceState(null, '', '/#/vehicles?missingProtocol=YUTONG_MQTT');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, activeConnections: 0, capacityFindings: [], redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/coverage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 12,
            onlineVehicles: 5,
            singleSourceVehicles: 7,
            multiSourceVehicles: 2,
            noDataVehicles: 3,
            unboundVehicles: 4,
            archiveIncompleteVehicles: 6,
            archiveMissingFields: [{ field: 'phone', title: '缺手机号', count: 3 }],
            missingSources: [{ protocol: 'YUTONG_MQTT', count: 8 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/coverage')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-GOV-001',
              plate: '粤A治理1',
              phone: '',
              oem: 'G7s',
              protocols: ['JT808'],
              missingProtocols: ['YUTONG_MQTT'],
              sourceStatus: [{ protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }],
              sourceCount: 1,
              onlineSourceCount: 1,
              online: true,
              lastSeen: '2026-07-03 20:12:10',
              bindingStatus: 'bound',
              serviceStatus: { status: 'degraded', severity: 'warning', title: '来源不完整', detail: '缺少 YUTONG_MQTT', sourceCount: 1, onlineSourceCount: 1 },
              sourceConsistency: { sourceCount: 1, onlineSourceCount: 1, locatedSourceCount: 1, missingProtocols: ['YUTONG_MQTT'], mileageDeltaKm: 0, sourceTimeDeltaSeconds: 0, scope: 'overview', status: 'degraded', severity: 'warning', title: '来源不完整', detail: '缺少 YUTONG_MQTT' }
            }],
            total: 12,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('VIN-GOV-001')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '复制治理摘要' }));

  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【车辆治理摘要】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('当前筛选：缺失来源：YUTONG_MQTT'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆总数：12，在线：5'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('单源：7，多源：2，暂无来源：3'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('待绑定：4，档案不完整：6'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('档案缺项：缺手机号 3'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('缺失来源：YUTONG_MQTT 8'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('处置队列：'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('[P0] 维护身份绑定 4'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('[P1] 补齐 YUTONG_MQTT 来源 8'));

  fireEvent.click(screen.getByRole('button', { name: '复制当前页处置清单' }));

  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【车辆处置清单】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('当前筛选：缺失来源：YUTONG_MQTT'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('当前页：1 辆 / 总计 12 辆'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. 车牌 粤A治理1 / VIN VIN-GOV-001 / 手机号 - / OEM G7s'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('状态：来源不完整；在线：在线；来源：1/1 来源在线，缺 YUTONG_MQTT；最后时间：2026-07-03 20:12:10'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('建议动作：补齐 YUTONG_MQTT 来源'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务：http://localhost:3000/#/detail?keyword=VIN-GOV-001&protocol=JT808'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时监控：http://localhost:3000/#/realtime?keyword=VIN-GOV-001&protocol=JT808'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹回放：http://localhost:3000/#/history?keyword=VIN-GOV-001&protocol=JT808'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('告警事件：http://localhost:3000/#/alert-events?keyword=VIN-GOV-001&protocol=JT808'));

  fireEvent.click(screen.getByRole('button', { name: '复制身份维护清单' }));

  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【车辆身份维护清单】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('当前页待维护：1 辆 / 当前筛选 12 辆'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('粤A治理1 / VIN-GOV-001 / - / G7s'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('档案缺项：缺手机号'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('关联键：vin=VIN-GOV-001；plate=粤A治理1；oem=G7s'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('建议动作：补齐档案字段：缺手机号'));
});

test('filters vehicle list from recommended action', async () => {
  window.history.replaceState(null, '', '/#/vehicles');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, activeConnections: 0, capacityFindings: [], redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/coverage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 181,
            onlineVehicles: 73,
            singleSourceVehicles: 0,
            multiSourceVehicles: 181,
            noDataVehicles: 0,
            unboundVehicles: 0,
            missingSources: [{ protocol: 'YUTONG_MQTT', count: 181 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/coverage')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-MISSING-MQTT',
              plate: '粤A服务2',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              missingProtocols: ['YUTONG_MQTT'],
              sourceStatus: [],
              sourceCount: 2,
              onlineSourceCount: 2,
              online: true,
              lastSeen: '2026-07-03 20:12:10',
              bindingStatus: 'bound'
            }],
            total: 181,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  fireEvent.click(await screen.findByRole('button', { name: '补齐 YUTONG_MQTT 来源' }));

  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/vehicles/coverage?limit=20&offset=0&missingProtocol=YUTONG_MQTT'), undefined);
  });
  expect(window.location.hash).toBe('#/vehicles?missingProtocol=YUTONG_MQTT');
});

test('filters vehicle list from action queue', async () => {
  window.history.replaceState(null, '', '/#/vehicles');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, activeConnections: 0, capacityFindings: [], redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/coverage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 181,
            onlineVehicles: 73,
            singleSourceVehicles: 0,
            multiSourceVehicles: 181,
            noDataVehicles: 4,
            unboundVehicles: 9,
            missingSources: [{ protocol: 'YUTONG_MQTT', count: 181 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  fireEvent.click(await screen.findByRole('button', { name: '确认平台转发 4' }));

  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/vehicles/coverage?limit=20&offset=0&serviceStatus=no_data'), undefined);
  });
  expect(window.location.hash).toBe('#/vehicles?serviceStatus=no_data');
});

test('shows and clears current vehicle service filters', async () => {
  window.history.replaceState(null, '', '/#/vehicles?keyword=%E7%B2%A4A&protocol=JT808&coverage=multi&missingProtocol=YUTONG_MQTT&serviceStatus=degraded&online=online&bindingStatus=bound&archiveStatus=incomplete');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/coverage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1,
            onlineVehicles: 1,
            singleSourceVehicles: 0,
            multiSourceVehicles: 1,
            noDataVehicles: 0,
            unboundVehicles: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/coverage')) {
      return {
        ok: true,
        json: async () => ({
          data: { items: [], total: 1, limit: 20, offset: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('当前车辆筛选')).toBeInTheDocument();
  expect(screen.getByText('关键词：粤A')).toBeInTheDocument();
  expect(screen.getAllByText('数据通道：JT808').length).toBeGreaterThan(0);
  expect(screen.getByText('车辆覆盖：多源')).toBeInTheDocument();
  expect(screen.getByText('缺失来源：YUTONG_MQTT')).toBeInTheDocument();
  expect(screen.getByText('服务状态：来源不完整')).toBeInTheDocument();
  expect(screen.getByText('在线：在线')).toBeInTheDocument();
  expect(screen.getByText('绑定：已绑定')).toBeInTheDocument();
  expect(screen.getByText('档案：不完整')).toBeInTheDocument();
  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('archiveStatus=incomplete'), undefined);
  });
  fireEvent.click(screen.getByRole('button', { name: '清空筛选' }));

  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/vehicles/coverage?limit=20&offset=0'), undefined);
  });
  expect(window.location.hash).toBe('#/vehicles');
});

test('filters vehicle list from result summary actions', async () => {
  window.history.replaceState(null, '', '/#/vehicles');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/coverage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: path.includes('online=online') ? 73 : 181,
            onlineVehicles: 73,
            singleSourceVehicles: 108,
            multiSourceVehicles: 73,
            unboundVehicles: 9
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/coverage')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [],
            total: path.includes('online=online') ? 73 : 181,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  fireEvent.click(await screen.findByRole('button', { name: /^在线车辆 73$/ }));

  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/vehicles/coverage?limit=20&offset=0&online=online'), undefined);
  });
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/vehicles/coverage/summary?online=online'), undefined);
  expect(window.location.hash).toBe('#/vehicles?online=online');
});

test('filters vehicle list by missing source evidence', async () => {
  window.history.replaceState(null, '', '/#/vehicles');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/coverage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1,
            onlineVehicles: 0,
            singleSourceVehicles: 0,
            multiSourceVehicles: 1,
            noDataVehicles: 0,
            unboundVehicles: 0,
            missingSources: [{ protocol: 'YUTONG_MQTT', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/coverage')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN001',
              plate: '粤AG18312',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              missingProtocols: ['YUTONG_MQTT'],
              sourceCount: 2,
              onlineSourceCount: 0,
              online: false,
              lastSeen: '2026-07-03 20:12:10',
              bindingStatus: 'bound'
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  await waitFor(() => {
    expect(screen.getAllByText('缺失来源').length).toBeGreaterThanOrEqual(2);
  });
  expect(screen.getAllByText('缺 YUTONG_MQTT').length).toBeGreaterThanOrEqual(1);
  fireEvent.click(screen.getByRole('button', { name: '缺 YUTONG_MQTT 1' }));
  expect(window.location.hash).toBe('#/vehicles?missingProtocol=YUTONG_MQTT');
  fireEvent.click(screen.getByRole('button', { name: '缺 YUTONG_MQTT' }));
  expect(window.location.hash).toBe('#/vehicles?missingProtocol=YUTONG_MQTT');
  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('missingProtocol=YUTONG_MQTT'), undefined);
  });
  fireEvent.click(screen.getByTestId('missing-protocol-filter'));
  const mqttOptions = await screen.findAllByText('缺 YUTONG_MQTT');
  fireEvent.click(mqttOptions[mqttOptions.length - 1]);
  fireEvent.click(screen.getByRole('button', { name: '查询' }));

  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('missingProtocol=YUTONG_MQTT'), undefined);
  });
});

test('copies shareable vehicle service filter link', async () => {
  window.history.replaceState(null, '', '/#/vehicles?keyword=%E7%B2%A4A&coverage=multi&serviceStatus=degraded');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/coverage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 181,
            onlineVehicles: 73,
            singleSourceVehicles: 0,
            multiSourceVehicles: 181,
            noDataVehicles: 12,
            unboundVehicles: 9
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/coverage')) {
      return {
        ok: true,
        json: async () => ({
          data: { items: [], total: 181, limit: 20, offset: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  fireEvent.click(await screen.findByRole('button', { name: /复制筛选链接/ }));

  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('/#/vehicles?keyword=%E7%B2%A4A&coverage=multi&serviceStatus=degraded'));
});

test('shows resolved vehicle service status after topbar search', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { onlineVehicles: 3, activeToday: 4, frameToday: 1286320, issueVehicles: 7, kafkaLag: 0, protocols: [], serviceStatuses: [], linkHealth: [] },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/overview')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'LB9A32A24R0LS1426',
            plate: '粤AG18312',
            phone: '13307795425',
            oem: 'G7s',
            protocols: ['GB32960', 'JT808'],
            primaryProtocol: 'JT808',
            coverageStatus: 'partial',
            sourceCount: 2,
            onlineSourceCount: 1,
            lastSeen: '2026-07-03 20:12:10',
            realtimeCount: 2,
            historyCount: 0,
            rawCount: 0,
            mileageCount: 0,
            qualityIssueCount: 0,
            sourceConsistency: {
              sourceCount: 2,
              onlineSourceCount: 1,
              locatedSourceCount: 2,
              missingProtocols: ['YUTONG_MQTT'],
              mileageDeltaKm: 0,
              sourceTimeDeltaSeconds: 0,
              scope: 'all_sources',
              status: 'degraded',
              severity: 'warning',
              title: '来源不完整',
              detail: '1/2 个来源在线，车辆服务可用但缺少 YUTONG_MQTT 来源。'
            }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'LB9A32A24R0LS1426',
            lookupKey: 'LB9A32A24R0LS1426',
            lookupResolved: true,
            sources: ['GB32960', 'JT808'],
            sourceStatus: [],
            realtime: [],
            history: { items: [], total: 0, limit: 20, offset: 0 },
            raw: { items: [], total: 0, limit: 10, offset: 0 },
            mileage: { items: [], total: 0, limit: 20, offset: 0 },
            quality: { items: [], total: 0, limit: 20, offset: 0 }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  fireEvent.change(screen.getByPlaceholderText('搜索 VIN / 车牌 / 手机号'), { target: { value: '粤AG18312' } });
  fireEvent.click(screen.getByRole('button', { name: '查询车辆' }));

  expect(await screen.findByText('当前车辆：数据通道不完整')).toBeInTheDocument();
  expect(screen.getByText('粤AG18312 / LB9A32A24R0LS1426')).toBeInTheDocument();
  expect(screen.getByText('一致性：数据通道不完整')).toBeInTheDocument();
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/vehicle-service/overview?keyword=%E7%B2%A4AG18312'), undefined);
  expect(fetchMock).not.toHaveBeenCalledWith(expect.stringContaining('/api/vehicles/resolve'), undefined);

  fireEvent.click(screen.getByRole('button', { name: '顶部地图态势' }));
  expect(window.location.hash).toBe('#/map?keyword=LB9A32A24R0LS1426&protocol=JT808');
  fireEvent.click(screen.getByRole('button', { name: '顶部实时监控' }));
  expect(window.location.hash).toBe('#/realtime?keyword=LB9A32A24R0LS1426&protocol=JT808');
  fireEvent.click(screen.getByRole('button', { name: '顶部数据导出' }));
  expect(window.location.hash).toBe('#/history-query?keyword=LB9A32A24R0LS1426&protocol=JT808&tab=raw');
  fireEvent.click(await screen.findByRole('button', { name: '顶部告警事件正常' }));
  expect(window.location.hash).toBe('#/alert-events?keyword=LB9A32A24R0LS1426&protocol=JT808');

  fireEvent.click(screen.getByRole('button', { name: '客户任务 轨迹回放 这段时间怎么跑' }));
  expect(window.location.hash).toBe('#/history?keyword=LB9A32A24R0LS1426&protocol=JT808');
  fireEvent.click(screen.getByRole('button', { name: '客户任务 里程统计 里程怎么算' }));
  expect(window.location.hash).toBe('#/mileage?keyword=LB9A32A24R0LS1426&protocol=JT808');
  fireEvent.click(screen.getByRole('button', { name: '客户任务 数据导出 证据怎么给客户' }));
  expect(window.location.hash).toBe('#/history-query?keyword=LB9A32A24R0LS1426&protocol=JT808&tab=raw');
  fireEvent.click(screen.getByRole('button', { name: '客户任务 告警通知 异常谁处理' }));
  expect(window.location.hash).toBe('#/alert-events?keyword=LB9A32A24R0LS1426&protocol=JT808');
});

test('shows row service status in dashboard vehicle previews', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 3,
            activeToday: 4,
            frameToday: 1286320,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/coverage')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-COVERAGE-WARN',
              plate: '粤A预警1',
              phone: '',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              missingProtocols: ['YUTONG_MQTT'],
              sourceStatus: [
                { protocol: 'GB32960', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: false, hasRaw: false, hasMileage: false },
                { protocol: 'JT808', online: false, lastSeen: '2026-07-03 20:10:00', hasRealtime: true, hasHistory: false, hasRaw: false, hasMileage: false },
                { protocol: 'YUTONG_MQTT', online: false, lastSeen: '', hasRealtime: false, hasHistory: false, hasRaw: false, hasMileage: false }
              ],
              sourceCount: 2,
              onlineSourceCount: 1,
              online: true,
              lastSeen: '2026-07-03 20:12:10',
              bindingStatus: 'bound',
              serviceStatus: {
                status: 'degraded',
                severity: 'warning',
                title: '覆盖部分离线',
                detail: '覆盖预览由 API 判定',
                sourceCount: 2,
                onlineSourceCount: 1
              }
            }],
            total: 1,
            limit: 8,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/realtime/vehicles')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-REALTIME-OFFLINE',
              plate: '粤A离线1',
              phone: '',
              oem: 'G7s',
              protocols: ['JT808'],
              sourceCount: 1,
              onlineSourceCount: 0,
              online: false,
              bindingStatus: 'bound',
              serviceStatus: {
                status: 'offline',
                severity: 'error',
                title: '实时车辆离线',
                detail: '实时预览由 API 判定',
                sourceCount: 1,
                onlineSourceCount: 0
              },
              primaryProtocol: 'JT808',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 0,
              socPercent: 0,
              totalMileageKm: 100,
              lastSeen: '2026-07-03 20:12:10'
            }],
            total: 1,
            limit: 8,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('VIN-COVERAGE-WARN')).toBeInTheDocument();
  expect(screen.getAllByText('来源证据').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('GB32960 在线')).toBeInTheDocument();
  expect(screen.getByText('JT808 离线')).toBeInTheDocument();
  expect(screen.getByText('YUTONG_MQTT 未接入')).toBeInTheDocument();
  expect(screen.getAllByText('1/2 来源在线').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('覆盖部分离线')).toBeInTheDocument();
  expect(screen.getByText('VIN-REALTIME-OFFLINE')).toBeInTheDocument();
  expect(screen.getAllByText('实时车辆离线').length).toBeGreaterThanOrEqual(1);
});

test('filters dashboard coverage from source consistency diagnosis', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 3,
            activeToday: 4,
            frameToday: 1286320,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1,
            boundVehicles: 1,
            onlineVehicles: 1,
            singleSourceVehicles: 1,
            multiSourceVehicles: 0,
            noDataVehicles: 0,
            identityRequiredVehicles: 0,
            serviceStatuses: [],
            protocols: [],
            missingSources: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/coverage')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-DASH-SINGLE',
              plate: '粤A总览',
              phone: '',
              oem: '',
              protocols: ['GB32960'],
              missingProtocols: ['JT808', 'YUTONG_MQTT'],
              sourceCount: 1,
              onlineSourceCount: 1,
              online: true,
              lastSeen: '2026-07-04 07:15:28',
              bindingStatus: 'bound',
              sourceConsistency: {
                sourceCount: 1,
                onlineSourceCount: 1,
                locatedSourceCount: 0,
                missingProtocols: ['JT808', 'YUTONG_MQTT'],
                mileageDeltaKm: 0,
                sourceTimeDeltaSeconds: 0,
                scope: 'coverage',
                status: 'single_source',
                severity: 'warning',
                title: '单一来源',
                detail: '当前车辆只有一个数据来源，无法做跨来源一致性校验。'
              }
            }],
            total: 1,
            limit: 8,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000, platformRelease: 'platform-20260704155844' } }
          : { items: [], total: 0, limit: 8, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('VIN-DASH-SINGLE')).toBeInTheDocument();
  expect(screen.getByText('来源一致性')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '单一来源' }));

  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('serviceStatus=degraded'), undefined);
  });
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('missingProtocol=JT808'), undefined);
  expect(screen.getByText('当前筛选：来源不完整 / 缺 JT808')).toBeInTheDocument();
});

test('opens full vehicle service list from dashboard coverage filters', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 3,
            activeToday: 4,
            frameToday: 1286320,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1,
            boundVehicles: 1,
            onlineVehicles: 1,
            singleSourceVehicles: 1,
            multiSourceVehicles: 0,
            noDataVehicles: 0,
            identityRequiredVehicles: 0,
            serviceStatuses: [],
            protocols: [],
            missingSources: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/coverage')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-DASH-FULL-LIST',
              plate: '粤A全量',
              phone: '',
              oem: '',
              protocols: ['GB32960'],
              missingProtocols: ['JT808', 'YUTONG_MQTT'],
              sourceCount: 1,
              onlineSourceCount: 1,
              online: true,
              lastSeen: '2026-07-04 07:20:14',
              bindingStatus: 'bound',
              sourceConsistency: {
                sourceCount: 1,
                onlineSourceCount: 1,
                locatedSourceCount: 0,
                missingProtocols: ['JT808', 'YUTONG_MQTT'],
                mileageDeltaKm: 0,
                sourceTimeDeltaSeconds: 0,
                scope: 'coverage',
                status: 'single_source',
                severity: 'warning',
                title: '单一来源',
                detail: '当前车辆只有一个数据来源，无法做跨来源一致性校验。'
              }
            }],
            total: 1,
            limit: path.includes('limit=20') ? 20 : 8,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000, platformRelease: 'platform-20260704155844' } }
          : { items: [], total: 0, limit: 8, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('VIN-DASH-FULL-LIST')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '单一来源' }));
  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('missingProtocol=JT808'), undefined);
  });

  fireEvent.click(screen.getByRole('button', { name: '查看全部车辆' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/vehicles?serviceStatus=degraded&missingProtocol=JT808');
  });
  const vehicleCoverageCall = fetchMock.mock.calls.find(([input]) => String(input).startsWith('/api/vehicles/coverage?limit=20&offset=0'));
  expect(vehicleCoverageCall).toBeTruthy();
  const vehicleCoverageParams = new URLSearchParams(String(vehicleCoverageCall?.[0]).split('?')[1]);
  expect(vehicleCoverageParams.get('serviceStatus')).toBe('degraded');
  expect(vehicleCoverageParams.get('missingProtocol')).toBe('JT808');
});

test('opens vehicle service from dashboard realtime preview with source evidence', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { onlineVehicles: 1, activeToday: 1, frameToday: 10, issueVehicles: 0, kafkaLag: 0, protocols: [], serviceStatuses: [], linkHealth: [] },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { totalVehicles: 1, boundVehicles: 1, onlineVehicles: 1, singleSourceVehicles: 1, multiSourceVehicles: 0, noDataVehicles: 0, identityRequiredVehicles: 0, serviceStatuses: [], protocols: [] },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/realtime/vehicles')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-DASH-RT',
              plate: '粤A实时1',
              phone: '',
              oem: 'G7s',
              protocols: ['JT808'],
              primaryProtocol: 'JT808',
              sourceCount: 1,
              onlineSourceCount: 1,
              online: true,
              bindingStatus: 'bound',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 12,
              socPercent: 80,
              totalMileageKm: 100,
              lastSeen: '2026-07-03 20:12:10'
            }],
            total: 1,
            limit: 8,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/overview')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN-DASH-RT',
            plate: '粤A实时1',
            sourceCount: 1,
            onlineSourceCount: 1,
            coverageStatus: 'online',
            primaryProtocol: 'JT808',
            lastSeen: '2026-07-03 20:12:10',
            historyCount: 0,
            rawCount: 0,
            mileageCount: 0,
            qualityIssueCount: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 8, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('VIN-DASH-RT')).toBeInTheDocument();
  const vehicleButtons = screen.getAllByRole('button', { name: '车辆服务' });
  fireEvent.click(vehicleButtons[vehicleButtons.length - 1]);

  await waitFor(() => {
    expect(window.location.hash).toBe('#/detail?keyword=VIN-DASH-RT&protocol=JT808');
  });
});

test('opens vehicle service from dashboard quality preview with issue source evidence', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { onlineVehicles: 1, activeToday: 1, frameToday: 10, issueVehicles: 1, kafkaLag: 0, protocols: [], serviceStatuses: [], linkHealth: [] },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: '',
              plate: '粤A质量1',
              phone: '13307795425',
              sourceEndpoint: '115.231.168.135:43625',
              protocol: 'JT808',
              issueType: 'VIN_MISSING',
              severity: 'error',
              lastSeen: '2026-07-03 20:12:10',
              detail: 'JT808 数据缺少 VIN 映射'
            }],
            total: 1,
            limit: 5,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/overview')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN-DASH-QUALITY',
            plate: '粤A质量1',
            sourceCount: 1,
            onlineSourceCount: 1,
            coverageStatus: 'online',
            primaryProtocol: 'JT808',
            lastSeen: '2026-07-03 20:12:10',
            historyCount: 0,
            rawCount: 0,
            mileageCount: 0,
            qualityIssueCount: 1
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/vehicle-service/summary')
          ? { totalVehicles: 1, boundVehicles: 1, onlineVehicles: 1, singleSourceVehicles: 1, multiSourceVehicles: 0, noDataVehicles: 0, identityRequiredVehicles: 0, serviceStatuses: [], protocols: [] }
          : { items: [], total: 0, limit: 8, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('13307795425')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '按车牌查车' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/detail?keyword=VIN-DASH-QUALITY&protocol=JT808');
  });
});

test('filters dashboard coverage by service status', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 3,
            activeToday: 4,
            frameToday: 1286320,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 8, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('车辆服务覆盖')).toBeInTheDocument();
  fireEvent.click(screen.getByTestId('dashboard-service-status-filter'));
  fireEvent.click(await screen.findByText('来源不完整'));
  fireEvent.click(screen.getByRole('button', { name: '筛选' }));

  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('serviceStatus=degraded'), undefined);
  });
  expect(screen.getByText('当前筛选：来源不完整')).toBeInTheDocument();
});

test('filters dashboard coverage by missing source evidence', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 3,
            activeToday: 4,
            frameToday: 1286320,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 8, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('车辆服务覆盖')).toBeInTheDocument();
  fireEvent.click(screen.getByTestId('dashboard-missing-protocol-filter'));
  fireEvent.click(await screen.findByText('缺 YUTONG_MQTT'));
  fireEvent.click(screen.getByRole('button', { name: '筛选' }));

  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('missingProtocol=YUTONG_MQTT'), undefined);
  });
  expect(screen.getByText('当前筛选：缺 YUTONG_MQTT')).toBeInTheDocument();
});

test('opens dashboard coverage from service status distribution', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            onlineVehicles: 3,
            activeToday: 4,
            frameToday: 1286320,
            issueVehicles: 7,
            kafkaLag: 0,
            protocols: [],
            serviceStatuses: [
              { status: 'degraded', title: '来源不完整', count: 39 }
            ],
            linkHealth: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 8, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('车辆服务覆盖')).toBeInTheDocument();
  fireEvent.click(screen.getByTestId('dashboard-service-status-filter'));
  fireEvent.click(await screen.findByText('来源不完整'));
  fireEvent.click(screen.getByRole('button', { name: '筛选' }));

  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('serviceStatus=degraded'), undefined);
  });
});

test('renders quality issues as vehicle-service governance labels', async () => {
  window.history.replaceState(null, '', '/#/alert-events');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            issueVehicleCount: 1,
            issueRecordCount: 1,
            errorCount: 0,
            warningCount: 1,
            protocols: [{ name: 'VEHICLE_SERVICE', count: 1 }],
            issueTypes: [{ name: 'NO_SOURCE', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-NO-SOURCE-001',
              plate: '粤A无源1',
              phone: '13300000002',
              sourceEndpoint: 'vehicle_identity_binding',
              protocol: 'VEHICLE_SERVICE',
              issueType: 'NO_SOURCE',
              severity: 'warning',
              lastSeen: '',
              detail: '车辆已绑定，但暂无 32960、808 或 MQTT 数据来源'
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('VIN-NO-SOURCE-001')).toBeInTheDocument();
  expect(screen.getAllByText('暂无数据来源').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('车辆服务').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('确认平台转发').length).toBeGreaterThan(0);
  expect(screen.getByText('绑定车辆连续无多接入来源证据')).toBeInTheDocument();
  expect(screen.getByText('车辆已绑定但没有任何来源证据，先确认平台转发、端口和订阅。')).toBeInTheDocument();
});

test('shows alert rule and notification policy workspace on quality page', async () => {
  window.history.replaceState(null, '', '/#/alert-events');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            linkHealth: [{ name: 'tdengine', status: 'error', detail: 'write unavailable' }],
            kafkaLag: 42,
            activeConnections: 120000,
            capacityFindings: ['kafka lag 42'],
            redisOnlineKeys: 368,
            tdengineWritable: false,
            mysqlWritable: true,
            runtime: { platformRelease: 'platform-20260704155607', requestTimeoutMs: 5000 }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            issueVehicleCount: 3,
            issueRecordCount: 5,
            errorCount: 1,
            warningCount: 4,
            protocols: [{ name: 'VEHICLE_SERVICE', count: 3 }],
            issueTypes: [
              { name: 'NO_SOURCE', count: 2 },
              { name: 'VIN_MISSING', count: 1 },
              { name: 'FIELD_MISSING', count: 2 }
            ]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [],
            total: 0,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('客户告警决策台')).toBeInTheDocument();
  expect(screen.getByText('先判断影响车辆，再通知责任团队并回到车辆证据闭环')).toBeInTheDocument();
  expect(screen.getByText('客户关心的是哪些车辆受影响、是否影响实时/轨迹/里程、是否需要通知、何时恢复；协议来源只作为证据辅助判断。')).toBeInTheDocument();
  expect(screen.getAllByText('立即处置').length).toBeGreaterThan(0);
  expect(screen.getByText('待通知车辆')).toBeInTheDocument();
  expect(screen.getAllByText('证据闭环').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('客户告警闭环时间线')).toBeInTheDocument();
  expect(screen.getByText('从告警触发、车辆影响、责任通知、证据复核到恢复验收，形成客户可追踪的闭环。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户告警闭环时间线 1 告警触发 5 条事件 查看事件' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户告警闭环时间线 2 影响车辆 3 辆车 影响摘要' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户告警闭环时间线 3 责任通知 接入运维 + 业务责任人 通知策略' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户告警闭环时间线 4 证据复核 实时/轨迹/里程 证据交接' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户告警闭环时间线 5 恢复验收 有标准 复制回执' })).toBeInTheDocument();
  expect(screen.getByText('客户告警闭环台')).toBeInTheDocument();
  expect(screen.getByText('先判断影响车辆，再通知责任人，随后打开实时、轨迹、原始记录完成处置，最后按恢复标准验收。')).toBeInTheDocument();
  expect(screen.getByText('今日客户处置队列')).toBeInTheDocument();
  expect(screen.getByText('把待通知车辆压缩成客户能执行的首要动作：先处理 P0 焦点车，再按 SLA 升级、证据复核和恢复验收闭环。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '今日客户处置队列 首要车辆 暂无待处置 车辆服务' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '今日客户处置队列 SLA状态 暂无 复制交接' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '今日客户处置队列 通知对象 接入运维 + 业务责任人 通知闭环' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '今日客户处置队列 恢复验收 有标准 复制回执' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户告警闭环台 1 影响车辆 3 辆' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户告警闭环台 2 通知升级 P0 实时中断' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户告警闭环台 3 处置证据 实时/轨迹/原始记录' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户告警闭环台 4 恢复验收 实时/轨迹/里程/导出' })).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '复制告警闭环包' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【客户告警闭环包】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('影响范围：3 辆 / 5 条'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('主通知策略：P0 实时中断 / 接入运维 + 业务责任人 / 站内告警 / 邮件 / 企业微信 / 30 分钟升级'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('证据入口：'));
  fireEvent.click(screen.getByRole('button', { name: '复制告警决策' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【客户告警决策说明】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('决策结论：立即处置'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('影响车辆：3 辆；问题记录：5 条'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('客户处置路径：'));
  expect(await screen.findByText('告警事件与通知中心')).toBeInTheDocument();
  expect(screen.getByText('客户处置：先判断影响车辆和优先级，再通知责任团队，并用实时、轨迹、原始记录、里程证据闭环。', { exact: false })).toBeInTheDocument();
  expect(screen.getByText('链路状态')).toBeInTheDocument();
  expect(screen.getAllByText('优先队列').length).toBeGreaterThanOrEqual(1);
  expect(await screen.findByText('告警触发规则')).toBeInTheDocument();
  expect(screen.getByText('业务影响评估')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '复制业务影响报告' })).toBeInTheDocument();
  expect(screen.getAllByText('高风险').length).toBeGreaterThan(0);
  expect(screen.getByText('3 辆受影响')).toBeInTheDocument();
  expect(screen.getAllByText('影响车辆').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('3 辆').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('P0 0 / P1 0').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('车辆服务').length).toBeGreaterThan(0);
  expect(screen.getAllByText('暂无数据来源').length).toBeGreaterThan(0);
  expect(screen.getAllByText('异常').length).toBeGreaterThan(0);
  expect(screen.getByText('4 类活跃')).toBeInTheDocument();
  expect(screen.getByText('3 类 P0')).toBeInTheDocument();
  expect(screen.getByText('容量与存储风险')).toBeInTheDocument();
  expect(screen.getByText('15 分钟恢复')).toBeInTheDocument();
  expect(screen.getByText('告警分派看板')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '分派团队 平台接入' })).toHaveTextContent('2 条');
  expect(screen.getByRole('button', { name: '分派团队 车辆档案' })).toHaveTextContent('主问题：VIN 缺失');
  expect(screen.getByRole('button', { name: '分派团队 基础设施' })).toHaveTextContent('SLA：15 分钟恢复');
  expect(screen.getAllByText('通知策略').length).toBeGreaterThan(0);
  expect(screen.getAllByText('P0 实时中断').length).toBeGreaterThan(0);
  expect(screen.getAllByText('接入运维 + 业务责任人').length).toBeGreaterThan(0);
  expect(screen.getByText('站内告警 / 邮件 / 企业微信')).toBeInTheDocument();
  expect(screen.getAllByText('30 分钟升级').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('验收：来源恢复并持续 10 分钟，车辆服务可查到实时与历史证据').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('客户通知渠道闭环')).toBeInTheDocument();
  expect(screen.getByText('把站内、邮件、企业微信这些触达方式按客户能理解的处置路径展示：谁收到、为什么收到、多久升级、恢复后怎么验收。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '通知渠道闭环 站内告警 复制交接包' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '通知渠道闭环 邮件 复制交接包' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '通知渠道闭环 企业微信 复制交接包' })).toBeInTheDocument();
  expect(screen.getAllByText('接入运维 + 业务责任人').length).toBeGreaterThan(1);
  expect(screen.getAllByText('无来源、VIN 缺失、存储不可写').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('客户恢复验收台')).toBeInTheDocument();
  expect(screen.getByText('告警恢复不是把状态改绿，而是要证明客户能重新看实时、查轨迹、核里程、导出证据，并留下一份恢复回执。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户恢复验收台 实时恢复 实时确认' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户恢复验收台 轨迹恢复 轨迹复核' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户恢复验收台 里程恢复 里程核对' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户恢复验收台 客户回执 复制回执' })).toBeInTheDocument();

  fireEvent.click(screen.getByRole('button', { name: '复制业务影响报告' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【告警业务影响报告】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('运行版本：platform-20260704155607'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('影响等级：高风险'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('影响车辆：3 辆'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('主要来源：车辆服务 3 条'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('主要问题：暂无数据来源 2 条'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('容量风险：存储异常；Kafka Lag 42；容量发现 1 项'));
  fireEvent.click(screen.getByRole('button', { name: '复制通知策略Runbook' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【告警通知策略Runbook】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('运行版本：platform-20260704155607'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('P0 实时中断 / 接入运维 + 业务责任人'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('升级：30 分钟升级'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('验收：来源恢复并持续 10 分钟，车辆服务可查到实时与历史证据'));
  fireEvent.click(screen.getByRole('button', { name: '客户恢复验收台 客户回执 复制回执' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【客户告警恢复验收回执】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('恢复标准：实时可看 / 轨迹可回放 / 里程可核对 / 历史证据可导出'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('当前影响：3 辆车 / 5 条告警 / 高风险'));
  fireEvent.click(screen.getByRole('button', { name: '分派团队 车辆档案' }));
  expect(window.location.hash).toBe('#/alert-events?issueType=VIN_MISSING');
});

test('shows actionable priority queue on quality page', async () => {
  window.history.replaceState(null, '', '/#/alert-events');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, activeConnections: 120000, capacityFindings: [], redisOnlineKeys: 368, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            issueVehicleCount: 2,
            issueRecordCount: 2,
            errorCount: 1,
            warningCount: 1,
            protocols: [{ name: 'VEHICLE_SERVICE', count: 1 }, { name: 'JT808', count: 1 }],
            issueTypes: [{ name: 'NO_SOURCE', count: 1 }, { name: 'VIN_MISSING', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [
              {
                vin: 'VIN-P0-001',
                plate: '粤A优先1',
                phone: '13307795425',
                sourceEndpoint: '115.231.168.135:43625',
                protocol: 'VEHICLE_SERVICE',
                issueType: 'NO_SOURCE',
                severity: 'error',
                lastSeen: '2026-07-03 20:12:10',
                detail: '车辆无任何来源'
              },
              {
                vin: '',
                plate: '粤A优先2',
                phone: '13307795426',
                sourceEndpoint: '115.231.168.135:43626',
                protocol: 'JT808',
                issueType: 'VIN_MISSING',
                severity: 'warning',
                lastSeen: '2026-07-03 20:10:10',
                detail: '手机号未映射 VIN'
              }
            ],
            total: 2,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/overview')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN-P0-001',
            plate: '粤A优先1',
            sourceCount: 0,
            onlineSourceCount: 0,
            coverageStatus: 'no_data',
            primaryProtocol: 'VEHICLE_SERVICE',
            lastSeen: '2026-07-03 20:12:10',
            historyCount: 0,
            rawCount: 0,
            mileageCount: 0,
            qualityIssueCount: 1
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('处置优先队列')).toBeInTheDocument();
  expect(screen.getByText('今日客户处置队列')).toBeInTheDocument();
  expect(screen.getByText('把待通知车辆压缩成客户能执行的首要动作：先处理 P0 焦点车，再按 SLA 升级、证据复核和恢复验收闭环。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '今日客户处置队列 首要车辆 粤A优先1 / VIN-P0-001 车辆服务' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: /今日客户处置队列 SLA状态 .* 复制交接/ })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '今日客户处置队列 通知对象 接入运维 + 业务责任人 通知闭环' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '今日客户处置队列 恢复验收 有标准 复制回执' })).toBeInTheDocument();
  expect(screen.getByText('车辆风险处置任务板')).toBeInTheDocument();
  expect(screen.getAllByText('粤A优先1 / VIN-P0-001').length).toBeGreaterThan(0);
  expect(screen.getAllByText('实时确认').length).toBeGreaterThan(0);
  expect(screen.getAllByText('轨迹证据').length).toBeGreaterThan(0);
  expect(screen.getByText('里程复核')).toBeInTheDocument();
  expect(screen.getAllByText('原始证据').length).toBeGreaterThan(0);
  expect(screen.getByText('先确认车辆是否仍在线、最后上报来源和实时字段是否恢复。')).toBeInTheDocument();
  expect(screen.getAllByText('P0').length).toBeGreaterThan(0);
  expect(screen.getAllByText('粤A优先1 / VIN-P0-001').length).toBeGreaterThan(0);
  expect(screen.getAllByText('确认平台转发').length).toBeGreaterThan(0);
  expect(screen.getAllByText('30 分钟确认').length).toBeGreaterThan(0);
  expect(screen.getAllByText('P1').length).toBeGreaterThan(0);
  expect(screen.getAllByText('粤A优先2 / 13307795426').length).toBeGreaterThan(0);
  fireEvent.click(screen.getAllByRole('button', { name: '进入车辆服务' })[0]);

  await waitFor(() => {
    expect(window.location.hash).toBe('#/detail?keyword=VIN-P0-001&protocol=VEHICLE_SERVICE');
  });
});

test('uses backend quality notification plan on quality page', async () => {
  window.history.replaceState(null, '', '/#/alert-events');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, activeConnections: 120000, capacityFindings: [], redisOnlineKeys: 368, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/notification-plan') || path.includes('/api/alert-events/notification-plan')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            summary: { issueVehicleCount: 9, issueRecordCount: 11, errorCount: 2, warningCount: 9, protocols: [], issueTypes: [{ name: 'NO_SOURCE', count: 4 }] },
            rules: [{ issueType: 'NO_SOURCE', title: '后端无来源规则', level: 'P0', owner: '平台接入', trigger: '后端统一触发', notify: '后端统一通知', sla: '10 分钟确认', count: 4 }],
            policies: [{ name: '后端 P0 通知策略', target: '接入运维', channel: '邮件 / 企业微信', condition: '后端统一条件' }],
            priorityIssues: [{
              vin: 'VIN-PLAN-001',
              plate: '粤A计划1',
              phone: '',
              sourceEndpoint: 'vehicle_identity_binding',
              protocol: 'VEHICLE_SERVICE',
              issueType: 'NO_SOURCE',
              severity: 'error',
              lastSeen: '2026-07-03 20:12:10',
              detail: '后端计划生成的优先告警',
              priority: 'P0',
              actionLabel: '后端建议动作',
              actionDetail: '后端建议说明',
              sla: '10 分钟确认',
              vehicleLabel: '后端车辆标签 / VIN-PLAN-001',
              realtimeHash: '#/realtime?keyword=VIN-PLAN-001',
              historyHash: '#/history?keyword=VIN-PLAN-001',
              rawHash: '#/history-query?keyword=VIN-PLAN-001&tab=raw',
              vehicleHash: '#/detail?keyword=VIN-PLAN-001',
              notificationText: '【P0 告警通知】后端计划'
            }],
            activeRuleCount: 1,
            p0RuleCount: 1
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { issueVehicleCount: 1, issueRecordCount: 1, errorCount: 1, warningCount: 0, protocols: [], issueTypes: [] },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: { items: [], total: 0, limit: 20, offset: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect((await screen.findAllByText('后端车辆标签 / VIN-PLAN-001')).length).toBeGreaterThan(0);
  expect(screen.getAllByText('后端建议动作').length).toBeGreaterThan(0);
  expect(screen.getAllByText('10 分钟确认').length).toBeGreaterThan(0);
  expect(screen.getByText('后端无来源规则')).toBeInTheDocument();
  expect(screen.getAllByText('后端 P0 通知策略').length).toBeGreaterThan(0);
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/alert-events/notification-plan?limit=20'), undefined);
});

test('renders notification rules as a standalone operations page', async () => {
  window.history.replaceState(null, '', '/#/notification-rules');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  const createObjectURL = vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:notification-rules');
  const revokeObjectURL = vi.spyOn(URL, 'revokeObjectURL').mockImplementation(() => undefined);
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            linkHealth: [],
            kafkaLag: 0,
            activeConnections: 1000,
            capacityFindings: [],
            redisOnlineKeys: 800,
            tdengineWritable: true,
            mysqlWritable: true,
            runtime: { requestTimeoutMs: 5000, platformRelease: 'platform-rules-test' }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/notification-plan') || path.includes('/api/alert-events/notification-plan')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            summary: { issueVehicleCount: 5, issueRecordCount: 8, errorCount: 2, warningCount: 6, protocols: [], issueTypes: [{ name: 'NO_SOURCE', count: 3 }] },
            rules: [
              { issueType: 'NO_SOURCE', title: '无来源规则', level: 'P0', owner: '平台接入', trigger: '车辆无任何来源证据', notify: '立即通知接入运维', sla: '30 分钟确认', count: 3 },
              { issueType: 'FIELD_MISSING', title: '字段缺失规则', level: 'P1', owner: '协议解析', trigger: '核心字段缺失', notify: '进入每日治理清单', sla: '当日闭环', count: 1 }
            ],
            policies: [
              { name: 'P0 实时中断', target: '接入运维 + 业务责任人', channel: '邮件 / 企业微信', condition: '无来源、VIN 缺失、存储不可写', escalationMinutes: 30, acceptanceCriteria: '来源恢复并持续 10 分钟' },
              { name: 'P1 数据质量', target: '协议解析 + 数据治理', channel: '每日汇总邮件', condition: '字段缺失、链路间断', escalationMinutes: 120, acceptanceCriteria: '核心字段恢复解析' }
            ],
            priorityIssues: [{
              vin: 'VIN-RULES-001',
              plate: '粤A规则1',
              phone: '',
              sourceEndpoint: 'vehicle_identity_binding',
              protocol: 'VEHICLE_SERVICE',
              issueType: 'NO_SOURCE',
              severity: 'error',
              lastSeen: '2026-07-03 20:12:10',
              detail: '通知规则页待通知告警',
              priority: 'P0',
              actionLabel: '确认平台转发',
              actionDetail: '核对平台转发配置',
              sla: '30 分钟确认',
              vehicleLabel: '粤A规则1 / VIN-RULES-001',
              realtimeHash: '#/realtime?keyword=VIN-RULES-001',
              historyHash: '#/history?keyword=VIN-RULES-001',
              rawHash: '#/history-query?keyword=VIN-RULES-001&tab=raw',
              vehicleHash: '#/detail?keyword=VIN-RULES-001',
              notificationText: '【P0 告警通知】通知规则页待通知'
            }],
            activeRuleCount: 2,
            p0RuleCount: 1
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByRole('heading', { name: '通知闭环' })).toBeInTheDocument();
  expect(screen.getByText('面向客户交付告警影响、通知对象、升级时限和恢复验收；规则配置只作为闭环背后的证据。')).toBeInTheDocument();
  expect(screen.getByText('客户告警闭环')).toBeInTheDocument();
  expect(screen.getByText('从告警触发到通知升级，再到验收恢复')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户告警闭环 发现问题 告警队列' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户告警闭环 补齐证据 证据矩阵' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户告警闭环 通知责任人 复制通知' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户告警闭环 超时升级 SLA报告' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户告警闭环 验收恢复 运行手册' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '复制值班清单' })).toBeInTheDocument();
  expect(screen.getByText('客户通知编排条')).toBeInTheDocument();
  expect(screen.getByText('先把规则命中转成可执行的通知编排：触发条件、受影响车辆、责任方、渠道、SLA 和验收口径在一行里完成确认。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户通知编排 告警触发 2 类活跃规则' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户通知编排 受影响车辆 1 待通知' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户通知编排 通知对象 接入运维 + 业务责任人' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户通知编排 升级时钟 已超时 1' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户通知编排 恢复验收 来源恢复并持续 10 分钟' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户通知编排动作 查看告警 告警队列' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户通知编排动作 复制通知 待通知' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户通知编排动作 复制SLA SLA报告' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户通知编排动作 导出演练 演练包' })).toBeInTheDocument();
  expect(screen.getByText('客户通知常问')).toBeInTheDocument();
  expect(screen.getByText('把通知规则翻译成客户能理解的影响、责任、升级和恢复口径')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户通知常问 哪些车受影响？ 影响摘要' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户通知常问 应该通知谁？ 复制通知' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户通知常问 多久没有恢复会升级？ SLA报告' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户通知常问 恢复后怎么验收？ 验收口径' })).toBeInTheDocument();
  expect(screen.getByText('无来源规则')).toBeInTheDocument();
  expect(screen.getByText('车辆无任何来源证据')).toBeInTheDocument();
  expect(screen.getAllByText('P0 实时中断').length).toBeGreaterThan(0);
  expect(screen.getAllByText('接入运维 + 业务责任人').length).toBeGreaterThan(0);
  expect(screen.getByText('告警处置队列')).toBeInTheDocument();
  expect(screen.getByText('下一步处置')).toBeInTheDocument();
  expect(screen.getByText('主责任方')).toBeInTheDocument();
  expect(screen.getAllByText('证据完整').length).toBeGreaterThan(0);
  expect(screen.getByText('核对平台转发配置')).toBeInTheDocument();
  expect(screen.getByText('通知执行矩阵')).toBeInTheDocument();
  expect(screen.getByText('无来源规则 / 平台接入')).toBeInTheDocument();
  expect(screen.getAllByText('邮件 / 企业微信').length).toBeGreaterThan(0);
  expect(screen.getAllByText('30 分钟升级').length).toBeGreaterThan(0);
  expect(screen.getByText('SLA升级态势')).toBeInTheDocument();
  expect(screen.getAllByText('已超时').length).toBeGreaterThan(0);
  expect(screen.getByText('即将升级')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '复制SLA升级报告' })).toBeInTheDocument();
  expect(screen.getByText('当前待通知告警')).toBeInTheDocument();
  expect(screen.getAllByText('粤A规则1 / VIN-RULES-001').length).toBeGreaterThan(0);
  expect(screen.getByText('确认平台转发')).toBeInTheDocument();
  expect(screen.getByText('通知规则页待通知告警')).toBeInTheDocument();
  expect(screen.getAllByText('platform-rules-test').length).toBeGreaterThan(0);
  expect(screen.getByText('通知覆盖与升级风险')).toBeInTheDocument();
  expect(screen.getByText('责任覆盖')).toBeInTheDocument();
  expect(screen.getByText('活跃命中')).toBeInTheDocument();
  expect(screen.getByText('P0压力')).toBeInTheDocument();
  expect(screen.getByText('升级风险')).toBeInTheDocument();
  expect(screen.getByText('链路前置')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '复制通知覆盖报告' })).toBeInTheDocument();
  expect(screen.getByText('通知可达性检查')).toBeInTheDocument();
  expect(screen.getByText('避免告警已经触发但无人收到或无法闭环。', { exact: false })).toBeInTheDocument();
  expect(screen.getByText('阻断 1 / 风险 0')).toBeInTheDocument();
  expect(screen.getAllByText('已超时').length).toBeGreaterThan(0);
  expect(screen.getAllByText('1 条待通知').length).toBeGreaterThan(0);
  expect(screen.getAllByText('对象：接入运维 + 业务责任人；渠道：邮件 / 企业微信；升级：30 分钟升级').length).toBeGreaterThan(0);
  expect(screen.getByText('立即升级责任人并同步业务侧')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '复制通知可达性检查' })).toBeInTheDocument();
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/alert-events/notification-plan?limit=50'), undefined);

  fireEvent.click(screen.getByRole('button', { name: '复制通知规则Runbook' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【通知规则Runbook】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('无来源规则 / P0 / 平台接入'));
  fireEvent.click(screen.getByRole('button', { name: '导出通知规则CSV' }));
  expect(createObjectURL).toHaveBeenCalled();
  expect(revokeObjectURL).toHaveBeenCalledWith('blob:notification-rules');
  fireEvent.click(screen.getByRole('button', { name: '复制待通知汇总' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【当前待通知告警】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('粤A规则1 / VIN-RULES-001'));
  fireEvent.click(screen.getByRole('button', { name: '复制升级值班清单' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【告警升级值班清单】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('值班结论：P0 1 条，需要立即确认'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('主通知策略：P0 实时中断 / 接入运维 + 业务责任人 / 邮件 / 企业微信'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('升级窗口：30 分钟升级'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. [P0] 无来源规则 / 平台接入 / 命中 3 / SLA 30 分钟确认'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('建议动作：确认平台转发 - 核对平台转发配置'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务：http://localhost:3000/#/detail?keyword=VIN-RULES-001'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('原始记录：http://localhost:3000/#/history-query?keyword=VIN-RULES-001&tab=raw'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('超过升级窗口未恢复时'));
  fireEvent.click(screen.getByRole('button', { name: '复制通知演练包' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【告警通知演练包】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('运行版本：platform-rules-test'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('规则规模：P0 1 类，P1 1 类，待通知 1 条'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('P0策略：P0 实时中断 / 接入运维 + 业务责任人 / 邮件 / 企业微信 / 30 分钟升级'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('2. 对第一条待通知告警打开车辆服务、实时、轨迹、原始记录四类证据'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('通知文本：【P0 告警通知】通知规则页待通知'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹证据：http://localhost:3000/#/history?keyword=VIN-RULES-001'));
  fireEvent.click(screen.getByRole('button', { name: '复制通知执行矩阵' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【通知执行矩阵】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('执行态势：P0 1 / 已超时 1 / 证据不完整 0'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('通知：接入运维 + 业务责任人 / 邮件 / 企业微信'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('规则：无来源规则 / 平台接入'));
  fireEvent.click(screen.getByRole('button', { name: '复制通知覆盖报告' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【通知覆盖与升级风险】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('责任覆盖：2 个责任方，2 类活跃规则'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('告警压力：待通知 1 / P0 1 / 证据不完整 0'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('升级风险：已超时 1 / 即将升级 0'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('通知渠道：邮件 / 企业微信；每日汇总邮件'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('平台接入'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('主策略：P0 实时中断 / 接入运维 + 业务责任人 / 邮件 / 企业微信'));
  fireEvent.click(screen.getByRole('button', { name: '复制通知可达性检查' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【通知可达性检查】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('可达结论：阻断 1 / 需跟进 0 / 可触达 1'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('平台接入 / 已超时'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('渠道：邮件 / 企业微信'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('证据：1/1 完整'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('下一步：立即升级责任人并同步业务侧'));
  fireEvent.click(screen.getByRole('button', { name: '复制SLA升级报告' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【告警SLA升级报告】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('升级态势：已超时 1 / 即将升级 0 / 正常跟进 0'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('[P0] 粤A规则1 / VIN-RULES-001'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('策略：P0 实时中断 / 接入运维 + 业务责任人 / 30 分钟升级'));
  fireEvent.click(screen.getByRole('button', { name: '复制单条告警通知' }));
  expect(writeText).toHaveBeenCalledWith('【P0 告警通知】通知规则页待通知');
  fireEvent.click(screen.getAllByRole('button', { name: '车辆服务' })[0]);
  expect(window.location.hash).toBe('#/detail?keyword=VIN-RULES-001');
});

test('renders ops quality as a separated evidence layer for customer vehicle service', async () => {
  window.history.replaceState(null, '', '/#/ops-quality');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            linkHealth: [
              { name: 'gb32960-gateway', status: 'ok', detail: 'listening :32960' },
              { name: 'kafka', status: 'warning', detail: 'lag 42' }
            ],
            kafkaLag: 42,
            activeConnections: 1234,
            capacityMetrics: {
              activeConnections: 1234,
              kafkaLag: 42,
              bridgeConsumerPending: 1482047,
              bridgeAckPending: 4000,
              bridgeBatchPendingMessages: 12,
              fastWriterConsumerPending: 8,
              fastWriterAckPending: 0,
              fastWriterBatchPending: 0,
              historyBatchPending: 2,
              historyRowsPending: 320
            },
            capacityFindings: [
              'Kafka lag 42',
              'bridge ack pending 4000 exceeds 100',
              'bridge consumer pending 1482047 exceeds 10000',
              'Redis online key below expected'
            ],
            redisOnlineKeys: 368,
            tdengineWritable: true,
            mysqlWritable: false,
            runtime: {
              requestTimeoutMs: 5000,
              platformRelease: 'platform-ops-test',
              amapWebJsConfigured: true,
              amapApiConfigured: true,
              amapSecurityProxyEnabled: true,
              amapSecurityCodeExposed: false,
              amapSecurityServiceHost: '/_AMapService'
            }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/ops/source-readiness')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1033,
            onlineVehicles: 208,
            kafkaLag: 42,
            activeConnections: 1234,
            redisOnlineKeys: 368,
            platformRelease: 'platform-ops-test',
            sources: [
              {
                protocol: 'GB32960',
                role: '整车与氢能实时数据主来源',
                online: 73,
                total: 340,
                onlineRate: 21.47,
                missingVehicles: 693,
                severity: 'warning',
                status: '覆盖不足',
                evidence: '在线 73/340，缺失车辆 693，Kafka Lag 42',
                action: '核对现代和交投平台转发清单。',
                acceptance: '缺失来源车辆下降，单源车辆可解释。',
                vehiclesHash: '#/vehicles?protocol=GB32960&missingProtocol=GB32960',
                realtimeHash: '#/realtime?protocol=GB32960&online=online',
                historyHash: '#/history-query?protocol=GB32960&tab=raw&includeFields=true',
                alertHash: '#/alert-events?protocol=GB32960'
              },
              {
                protocol: 'JT808',
                role: '位置、总里程和设备在线主来源',
                online: 73,
                total: 340,
                onlineRate: 21.47,
                missingVehicles: 693,
                severity: 'warning',
                status: '覆盖不足',
                evidence: '在线 73/340，缺失车辆 693，Kafka Lag 42',
                action: '补齐手机号绑定和注册缺失车辆。',
                acceptance: '未知 VIN 注册下降，位置与里程持续可查。',
                vehiclesHash: '#/vehicles?protocol=JT808&missingProtocol=JT808',
                realtimeHash: '#/realtime?protocol=JT808&online=online',
                historyHash: '#/history-query?protocol=JT808&tab=raw&includeFields=true',
                alertHash: '#/alert-events?protocol=JT808'
              }
            ]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByRole('heading', { name: '运维证据层' })).toBeInTheDocument();
  expect(screen.getByText('客户主流程外的链路、容量、存储和地图配置证据，用来解释车辆实时、轨迹、统计、导出或告警为何不可用。')).toBeInTheDocument();
  expect(screen.getByText('客户问题解释路径')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户问题解释路径 车辆不在线 看实时写入' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户问题解释路径 轨迹缺数据 看历史消费' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户问题解释路径 无法导出 看存储/API' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户问题解释路径 地图不可用 看高德配置' })).toBeInTheDocument();
  expect(screen.getByText('运维证据只在客户问题需要解释时打开，默认不参与车辆服务主流程。')).toBeInTheDocument();
  expect(screen.getByText('platform-ops-test')).toBeInTheDocument();
  expect(screen.getAllByText('42').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('1,234').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('368')).toBeInTheDocument();
  expect(screen.getByText('TDengine 写入')).toBeInTheDocument();
  expect(screen.getByText('MySQL 写入')).toBeInTheDocument();
  expect(screen.getByText('服务端 API Key 已配置')).toBeInTheDocument();
  expect(screen.getByText('/_AMapService')).toBeInTheDocument();
  expect(screen.getByText('安全码未暴露')).toBeInTheDocument();
  expect(screen.getByText('gb32960-gateway')).toBeInTheDocument();
  expect(screen.getByText('消息链路积压')).toBeInTheDocument();
  expect(screen.getByText('快速写入待消费')).toBeInTheDocument();
  expect(screen.getByText('历史行待写')).toBeInTheDocument();
  expect(screen.getByText('1,482,047')).toBeInTheDocument();
  expect(screen.getByText('4,000')).toBeInTheDocument();
  expect(screen.getByText('容量风险处置工作台')).toBeInTheDocument();
  expect(screen.getByText('稳定 NATS 到 Kafka 桥接')).toBeInTheDocument();
  expect(screen.getByText('恢复核心存储可写')).toBeInTheDocument();
  expect(screen.getByText(/历史待写 320 行/)).toBeInTheDocument();
  expect(screen.getByText('Kafka lag 42')).toBeInTheDocument();
  expect(screen.getByText('NATS 桥接 ACK 未确认')).toBeInTheDocument();
  expect(screen.getAllByText('NATS 桥接待消费').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('桥接处置顺序')).toBeInTheDocument();
  expect(screen.getByText('数据源生产就绪度')).toBeInTheDocument();
  expect(await screen.findByText('GB32960')).toBeInTheDocument();
  expect(screen.getByText('JT808')).toBeInTheDocument();
  expect(screen.getByText('整车与氢能实时数据主来源')).toBeInTheDocument();
  expect(screen.getAllByText('覆盖不足').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('在线 73/340，缺失车辆 693，Kafka Lag 42').length).toBeGreaterThanOrEqual(1);
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/ops/health'), undefined);
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/ops/source-readiness'), undefined);

  fireEvent.click(screen.getByRole('button', { name: '复制容量交接' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【车辆数据中台运维容量交接】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('运行版本：platform-ops-test'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('Kafka Lag：42'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('NATS桥接：consumer pending 1,482,047 / ack pending 4,000'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('存储状态：TDengine 正常 / MySQL 异常'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. 先看 Kafka Lag 与 NATS ACK pending'));

  fireEvent.click(screen.getByRole('button', { name: '复制处置计划' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【车辆数据中台容量处置计划】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('P0 稳定 NATS 到 Kafka 桥接'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('负责人：数据库 / 应用'));

  const sourceReadinessCopyButton = screen.getByText('复制就绪度报告').closest('button');
  expect(sourceReadinessCopyButton).not.toBeNull();
  fireEvent.click(sourceReadinessCopyButton as HTMLButtonElement);
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【数据源生产就绪度】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('GB32960 - 覆盖不足'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时监控：http://localhost:3000/#/realtime?protocol=GB32960&online=online'));
});

test('copies notification text from quality priority queue', async () => {
  window.history.replaceState(null, '', '/#/alert-events');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, activeConnections: 120000, capacityFindings: [], redisOnlineKeys: 368, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            issueVehicleCount: 1,
            issueRecordCount: 1,
            errorCount: 1,
            warningCount: 0,
            protocols: [{ name: 'JT808', count: 1 }],
            issueTypes: [{ name: 'VIN_MISSING', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: '',
              plate: '粤A通知1',
              phone: '13307795425',
              sourceEndpoint: '115.231.168.135:43625',
              protocol: 'JT808',
              issueType: 'VIN_MISSING',
              severity: 'error',
              lastSeen: '2026-07-03 20:12:10',
              detail: '手机号未映射 VIN，需要维护 binding'
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('处置优先队列')).toBeInTheDocument();
  fireEvent.click(screen.getAllByRole('button', { name: '复制通知' })[0]);

  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【P0 告警通知】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆：粤A通知1 / 13307795425'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('问题：VIN 缺失'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('SLA：2 小时修复'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('最后时间：2026-07-03 20:12:10'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('详情：手机号未映射 VIN，需要维护 binding'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时定位：http://localhost:3000/#/realtime?keyword=%E7%B2%A4A%E9%80%9A%E7%9F%A51&protocol=JT808'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹证据：http://localhost:3000/#/history?keyword=%E7%B2%A4A%E9%80%9A%E7%9F%A51&protocol=JT808&dateFrom=2026-07-03&dateTo=2026-07-04'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('原始记录：http://localhost:3000/#/history-query?keyword=%E7%B2%A4A%E9%80%9A%E7%9F%A51&protocol=JT808&tab=raw&dateFrom=2026-07-03&dateTo=2026-07-04&includeFields=true'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务：http://localhost:3000/#/detail?keyword=%E7%B2%A4A%E9%80%9A%E7%9F%A51&protocol=JT808'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('告警筛选：http://localhost:3000/#/alert-events'));
});

test('copies dispatch ticket from quality priority queue', async () => {
  window.history.replaceState(null, '', '/#/alert-events');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, activeConnections: 120000, capacityFindings: [], redisOnlineKeys: 368, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            issueVehicleCount: 1,
            issueRecordCount: 1,
            errorCount: 1,
            warningCount: 0,
            protocols: [{ name: 'JT808', count: 1 }],
            issueTypes: [{ name: 'VIN_MISSING', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: '',
              plate: '粤A工单1',
              phone: '13307795425',
              sourceEndpoint: '115.231.168.135:43625',
              protocol: 'JT808',
              issueType: 'VIN_MISSING',
              severity: 'error',
              lastSeen: '2026-07-03 20:12:10',
              detail: '手机号未映射 VIN，需要维护 binding'
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('处置优先队列')).toBeInTheDocument();
  fireEvent.click(await screen.findByRole('button', { name: '复制工单' }));

  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【告警处置工单】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('标题：[P0] VIN 缺失 - 粤A工单1 / 13307795425'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('责任团队：车辆档案'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('触发规则：来源有数据但无法归并到 VIN'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('数据来源：JT808'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('来源地址：115.231.168.135:43625'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('建议动作：维护身份绑定'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('处置步骤：'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. 打开车辆服务核对当前身份解析结果'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('2. 核对实时定位、轨迹证据和原始记录'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('3. 完成后确认车辆服务状态恢复，问题不再命中当前告警'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('验收标准：车辆能解析到 VIN，实时、历史和原始记录可通过同一车辆服务查询'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务：http://localhost:3000/#/detail?keyword=%E7%B2%A4A%E5%B7%A5%E5%8D%951&protocol=JT808'));
});

test('copies priority queue notification digest on quality page', async () => {
  window.history.replaceState(null, '', '/#/alert-events');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, activeConnections: 120000, capacityFindings: [], redisOnlineKeys: 368, tdengineWritable: true, mysqlWritable: true, runtime: { platformRelease: 'platform-20260704153951', requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            issueVehicleCount: 2,
            issueRecordCount: 2,
            errorCount: 1,
            warningCount: 1,
            protocols: [{ name: 'VEHICLE_SERVICE', count: 1 }, { name: 'JT808', count: 1 }],
            issueTypes: [{ name: 'NO_SOURCE', count: 1 }, { name: 'VIN_MISSING', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [
              {
                vin: 'VIN-DIGEST-001',
                plate: '粤A汇总1',
                phone: '13307795425',
                sourceEndpoint: '115.231.168.135:43625',
                protocol: 'VEHICLE_SERVICE',
                issueType: 'NO_SOURCE',
                severity: 'error',
                lastSeen: '2026-07-03 20:12:10',
                detail: '车辆无任何来源'
              },
              {
                vin: '',
                plate: '粤A汇总2',
                phone: '13307795426',
                sourceEndpoint: '115.231.168.135:43626',
                protocol: 'JT808',
                issueType: 'VIN_MISSING',
                severity: 'warning',
                lastSeen: '2026-07-03 20:10:10',
                detail: '手机号未映射 VIN'
              }
            ],
            total: 2,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('处置优先队列')).toBeInTheDocument();
  expect(await screen.findByText('platform-20260704153951')).toBeInTheDocument();
  expect(screen.getByText('客户通知 SLA')).toBeInTheDocument();
  expect(screen.getByText('把告警翻译成客户能理解的通知、时限和恢复标准')).toBeInTheDocument();
  expect(screen.getByText('每一条告警都先回答影响哪些车、通知谁、多久升级、恢复后如何验收；链路、协议和原始记录只作为证据附件。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户通知SLA 客户影响范围 影响摘要' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户通知SLA 通知负责人 通知闭环' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户通知SLA 升级时限 复制策略' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户通知SLA 恢复验收 交接包' })).toBeInTheDocument();
  expect(screen.getAllByText('接入运维 + 业务责任人').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('30 分钟升级').length).toBeGreaterThanOrEqual(1);
  fireEvent.click(screen.getAllByRole('button', { name: '复制通知交接包' })[0]);

  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【告警通知交接包】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('运行版本：platform-20260704153951'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('当前筛选：全部告警'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('问题车辆：2；问题记录：2'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('优先级：P0 1 条 / P1 1 条'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('活跃规则：2 类'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('主通知策略：P0 实时中断 / 接入运维 + 业务责任人 / 站内告警 / 邮件 / 企业微信 / 30 分钟升级'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('统一验收：来源恢复并持续 10 分钟，车辆服务可查到实时与历史证据'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. [P0] 粤A汇总1 / VIN-DIGEST-001'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时：http://localhost:3000/#/realtime?keyword=VIN-DIGEST-001&protocol=VEHICLE_SERVICE'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('原始记录：http://localhost:3000/#/history-query?keyword=VIN-DIGEST-001&protocol=VEHICLE_SERVICE&tab=raw&dateFrom=2026-07-03&dateTo=2026-07-04&includeFields=true'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('2. [P1] 粤A汇总2 / 13307795426'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('通知闭环：http://localhost:3000/#/notification-rules'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('运维质量：http://localhost:3000/#/ops-quality'));

  fireEvent.click(screen.getByRole('button', { name: '复制优先队列通知汇总' }));

  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【告警优先队列汇总】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('运行版本：platform-20260704153951'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('通知升级：P0 实时中断 / 30 分钟升级 / 接入运维 + 业务责任人'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('验收标准：来源恢复并持续 10 分钟，车辆服务可查到实时与历史证据'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('P0：1 条，P1：1 条'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. [P0] 粤A汇总1 / VIN-DIGEST-001'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('确认平台转发'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('2. [P1] 粤A汇总2 / 13307795426'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('维护身份绑定'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('告警筛选：http://localhost:3000/#/alert-events'));
});

test('copies notification text from regular quality issue row', async () => {
  window.history.replaceState(null, '', '/#/alert-events');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, activeConnections: 120000, capacityFindings: [], redisOnlineKeys: 368, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            issueVehicleCount: 1,
            issueRecordCount: 1,
            errorCount: 0,
            warningCount: 1,
            protocols: [{ name: 'GB32960', count: 1 }],
            issueTypes: [{ name: 'FIELD_MISSING', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-ROW-NOTIFY',
              plate: '粤A行通知',
              phone: '13307795425',
              sourceEndpoint: '115.29.187.205:32960',
              protocol: 'GB32960',
              issueType: 'FIELD_MISSING',
              severity: 'warning',
              lastSeen: '2026-07-03 20:12:10',
              detail: '位置字段缺失，影响轨迹回放'
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('质量问题')).toBeInTheDocument();
  const copyButtons = screen.getAllByRole('button', { name: '复制通知' });
  expect(copyButtons.length).toBeGreaterThanOrEqual(2);
  fireEvent.click(copyButtons[copyButtons.length - 1]);

  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【P1 告警通知】字段缺失'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆：粤A行通知 / VIN-ROW-NOTIFY'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('数据来源：GB32960'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('建议动作：核对解析字段'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('详情：位置字段缺失，影响轨迹回放'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹证据：http://localhost:3000/#/history?keyword=VIN-ROW-NOTIFY&protocol=GB32960&dateFrom=2026-07-03&dateTo=2026-07-04'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务：http://localhost:3000/#/detail?keyword=VIN-ROW-NOTIFY&protocol=GB32960'));

  const evidenceButtons = screen.getAllByRole('button', { name: '复制证据包' });
  expect(evidenceButtons.length).toBeGreaterThanOrEqual(2);
  fireEvent.click(evidenceButtons[evidenceButtons.length - 1]);

  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【告警证据包】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('优先级：P1'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('验收标准：缺失字段恢复解析，统计、定位或里程依赖的核心字段可查询'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时定位：http://localhost:3000/#/realtime?keyword=VIN-ROW-NOTIFY&protocol=GB32960'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹回放：http://localhost:3000/#/history?keyword=VIN-ROW-NOTIFY&protocol=GB32960&dateFrom=2026-07-03&dateTo=2026-07-04'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('原始记录：http://localhost:3000/#/history-query?keyword=VIN-ROW-NOTIFY&protocol=GB32960'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('includeFields=true'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('里程复核：http://localhost:3000/#/mileage?keyword=VIN-ROW-NOTIFY&protocol=GB32960&dateFrom=2026-07-03&dateTo=2026-07-04'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务：http://localhost:3000/#/detail?keyword=VIN-ROW-NOTIFY&protocol=GB32960'));
});

test('opens realtime map context from quality priority queue', async () => {
  window.history.replaceState(null, '', '/#/alert-events');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, activeConnections: 120000, capacityFindings: [], redisOnlineKeys: 368, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            issueVehicleCount: 1,
            issueRecordCount: 1,
            errorCount: 1,
            warningCount: 0,
            protocols: [{ name: 'JT808', count: 1 }],
            issueTypes: [{ name: 'LINK_GAP', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-ALERT-MAP',
              plate: '粤A地图告警',
              phone: '13307795425',
              sourceEndpoint: '115.231.168.135:43625',
              protocol: 'JT808',
              issueType: 'LINK_GAP',
              severity: 'error',
              lastSeen: '2026-07-03 20:12:10',
              detail: 'JT808 来源间断，需要定位当前车辆'
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/realtime/locations')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-ALERT-MAP',
              plate: '粤A地图告警',
              primaryProtocol: 'JT808',
              longitude: 113.24567,
              latitude: 23.12345,
              online: true,
              sourceCount: 1,
              onlineSourceCount: 1,
              lastSeen: '2026-07-03 20:12:11'
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('处置优先队列')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '实时定位' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/realtime?keyword=VIN-ALERT-MAP&protocol=JT808');
  });
  expect(await screen.findByRole('heading', { name: '实时监控' })).toBeInTheDocument();
});

test('opens same-day trajectory evidence from quality priority queue', async () => {
  window.history.replaceState(null, '', '/#/alert-events');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, activeConnections: 120000, capacityFindings: [], redisOnlineKeys: 368, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            issueVehicleCount: 1,
            issueRecordCount: 1,
            errorCount: 1,
            warningCount: 0,
            protocols: [{ name: 'GB32960', count: 1 }],
            issueTypes: [{ name: 'FIELD_MISSING', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-ALERT-TRACK',
              plate: '粤A轨迹告警',
              phone: '13307795425',
              sourceEndpoint: '115.29.187.205:32960',
              protocol: 'GB32960',
              issueType: 'FIELD_MISSING',
              severity: 'error',
              lastSeen: '2026-07-03 20:12:10',
              detail: 'GB32960 位置字段缺失，需要核对轨迹'
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/locations') || path.includes('/api/history/raw-frames')) {
      return {
        ok: true,
        json: async () => ({
          data: { items: [], total: 0, limit: 10, offset: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('处置优先队列')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '轨迹证据' }));

  await waitFor(() => {
    expect(window.location.hash.startsWith('#/history?')).toBe(true);
  });
  const params = new URLSearchParams(window.location.hash.slice('#/history?'.length));
  expect(params.get('keyword')).toBe('VIN-ALERT-TRACK');
  expect(params.get('protocol')).toBe('GB32960');
  expect(params.get('dateFrom')).toBe('2026-07-03');
  expect(params.get('dateTo')).toBe('2026-07-04');
  expect(params.get('tab')).toBeNull();
  expect(await screen.findByRole('heading', { name: '轨迹回放' })).toBeInTheDocument();
});

test('opens same-day raw evidence from quality priority queue', async () => {
  window.history.replaceState(null, '', '/#/alert-events');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, activeConnections: 120000, capacityFindings: [], redisOnlineKeys: 368, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            issueVehicleCount: 1,
            issueRecordCount: 1,
            errorCount: 1,
            warningCount: 0,
            protocols: [{ name: 'JT808', count: 1 }],
            issueTypes: [{ name: 'LINK_GAP', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-ALERT-RAW',
              plate: '粤ARAW告警',
              phone: '13307795425',
              sourceEndpoint: '115.231.168.135:43625',
              protocol: 'JT808',
              issueType: 'LINK_GAP',
              severity: 'error',
              lastSeen: '2026-07-03 20:12:10',
              detail: 'JT808 来源间断，需要核对原始记录'
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/locations') || path.includes('/api/history/raw-frames')) {
      return {
        ok: true,
        json: async () => ({
          data: { items: [], total: 0, limit: 10, offset: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('处置优先队列')).toBeInTheDocument();
  const rawEvidenceButtons = screen.getAllByRole('button', { name: '原始记录' });
  fireEvent.click(rawEvidenceButtons[rawEvidenceButtons.length - 1]);

  await waitFor(() => {
    expect(window.location.hash.startsWith('#/history-query?')).toBe(true);
  });
  const params = new URLSearchParams(window.location.hash.slice('#/history-query?'.length));
  expect(params.get('keyword')).toBe('VIN-ALERT-RAW');
  expect(params.get('protocol')).toBe('JT808');
  expect(params.get('dateFrom')).toBe('2026-07-03');
  expect(params.get('dateTo')).toBe('2026-07-04');
  expect(params.get('includeFields')).toBe('true');
  expect(params.get('tab')).toBe('raw');
  expect(await screen.findByRole('heading', { name: '历史查询导出' })).toBeInTheDocument();
});

test('opens vehicle service from quality issue with issue source evidence', async () => {
  window.history.replaceState(null, '', '/#/alert-events');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            issueVehicleCount: 1,
            issueRecordCount: 1,
            errorCount: 1,
            warningCount: 0,
            protocols: [{ name: 'JT808', count: 1 }],
            issueTypes: [{ name: 'VIN_MISSING', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: '',
              plate: '粤AG18312',
              phone: '13307795425',
              sourceEndpoint: '115.231.168.135:43625',
              protocol: 'JT808',
              issueType: 'VIN_MISSING',
              severity: 'error',
              lastSeen: '2026-07-03 20:12:10',
              detail: 'JT808 数据缺少 VIN 映射'
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/overview')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN-JT808-QUALITY',
            plate: '粤AG18312',
            sourceCount: 1,
            onlineSourceCount: 1,
            coverageStatus: 'online',
            primaryProtocol: 'JT808',
            lastSeen: '2026-07-03 20:12:10',
            historyCount: 0,
            rawCount: 0,
            mileageCount: 0,
            qualityIssueCount: 1
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('13307795425')).toBeInTheDocument();
  expect(screen.getAllByText('维护身份绑定').length).toBeGreaterThan(0);
  expect(screen.getByText('数据已有来源但无法归并到 VIN，优先用车牌/手机号补齐绑定。')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '按车牌查车' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/detail?keyword=VIN-JT808-QUALITY&protocol=JT808');
  });
});

test('opens same-day history evidence from quality issue row', async () => {
  window.history.replaceState(null, '', '/#/alert-events');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            issueVehicleCount: 1,
            issueRecordCount: 1,
            errorCount: 0,
            warningCount: 1,
            protocols: [{ name: 'JT808', count: 1 }],
            issueTypes: [{ name: 'LINK_GAP', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-QUALITY-HISTORY',
              plate: '粤A告警证',
              phone: '13307795425',
              sourceEndpoint: '115.231.168.135:43625',
              protocol: 'JT808',
              issueType: 'LINK_GAP',
              severity: 'warning',
              lastSeen: '2026-07-03 20:12:10',
              detail: 'JT808 来源存在上报间断'
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('VIN-QUALITY-HISTORY')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '核对历史' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/history?keyword=VIN-QUALITY-HISTORY&protocol=JT808&dateFrom=2026-07-03&dateTo=2026-07-04');
  });
});

test('quality page surfaces escalation clock for priority issues', async () => {
  window.history.replaceState(null, '', '/#/alert-events');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            issueVehicleCount: 2,
            issueRecordCount: 2,
            errorCount: 1,
            warningCount: 1,
            protocols: [{ name: 'VEHICLE_SERVICE', count: 1 }, { name: 'JT808', count: 1 }],
            issueTypes: [{ name: 'NO_SOURCE', count: 1 }, { name: 'LINK_GAP', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [
              {
                vin: 'VIN-QUALITY-OVERDUE',
                plate: '粤A超时',
                phone: '13307790001',
                sourceEndpoint: 'vehicle_identity_binding',
                protocol: 'VEHICLE_SERVICE',
                issueType: 'NO_SOURCE',
                severity: 'error',
                lastSeen: '2026-07-03 08:00:00',
                detail: '绑定车辆无来源'
              },
              {
                vin: 'VIN-QUALITY-RECENT',
                plate: '粤A正常',
                phone: '13307790002',
                sourceEndpoint: '115.231.168.135:43625',
                protocol: 'JT808',
                issueType: 'LINK_GAP',
                severity: 'warning',
                lastSeen: new Date().toISOString(),
                detail: '最近链路间断'
              }
            ],
            total: 2,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('告警升级时钟')).toBeInTheDocument();
  await waitFor(() => expect(screen.getAllByText('超 SLA').length).toBeGreaterThan(0));
  expect(screen.getByText('SLA 正常')).toBeInTheDocument();
  expect(screen.getByText('VIN-QUALITY-OVERDUE')).toBeInTheDocument();
});

test('opens same-day raw evidence from quality issue row', async () => {
  window.history.replaceState(null, '', '/#/alert-events');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            issueVehicleCount: 1,
            issueRecordCount: 1,
            errorCount: 0,
            warningCount: 1,
            protocols: [{ name: 'GB32960', count: 1 }],
            issueTypes: [{ name: 'FIELD_MISSING', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-QUALITY-RAW',
              plate: '粤A告警RAW',
              phone: '',
              sourceEndpoint: '115.29.187.205:32960',
              protocol: 'GB32960',
              issueType: 'FIELD_MISSING',
              severity: 'warning',
              lastSeen: '2026-07-03 20:12:10',
              detail: 'GB32960 实时帧缺少氢耗字段'
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/locations') || path.includes('/api/history/raw-frames')) {
      return {
        ok: true,
        json: async () => ({
          data: { items: [], total: 0, limit: 10, offset: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('VIN-QUALITY-RAW')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '核对原始' }));

  await waitFor(() => {
    expect(window.location.hash.startsWith('#/history-query?')).toBe(true);
  });
  const params = new URLSearchParams(window.location.hash.slice('#/history-query?'.length));
  expect(params.get('keyword')).toBe('VIN-QUALITY-RAW');
  expect(params.get('protocol')).toBe('GB32960');
  expect(params.get('dateFrom')).toBe('2026-07-03');
  expect(params.get('dateTo')).toBe('2026-07-04');
  expect(params.get('includeFields')).toBe('true');
  expect(params.get('tab')).toBe('raw');
  expect(await screen.findByRole('heading', { name: '历史查询导出' })).toBeInTheDocument();
});

test('copies trajectory playback summary from history page', async () => {
  window.history.replaceState(null, '', '/#/history?keyword=VIN-HISTORY-SUMMARY&protocol=JT808&dateFrom=2026-07-03&dateTo=2026-07-04');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, activeConnections: 0, capacityFindings: [], redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/locations')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [
              {
                vin: 'VIN-HISTORY-SUMMARY',
                plate: '粤A轨迹1',
                protocol: 'JT808',
                longitude: 113.201,
                latitude: 23.122,
                speedKmh: 12.5,
                totalMileageKm: 1000,
                deviceTime: '2026-07-03 10:00:00',
                serverTime: '2026-07-03 10:00:02'
              },
              {
                vin: 'VIN-HISTORY-SUMMARY',
                plate: '粤A轨迹1',
                protocol: 'JT808',
                longitude: 113.301,
                latitude: 23.222,
                speedKmh: 58.8,
                totalMileageKm: 1022.6,
                deviceTime: '2026-07-03 11:00:00',
                serverTime: '2026-07-03 11:00:02'
              }
            ],
            total: 2,
            limit: 10,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/raw-frames/query')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              id: 'raw-history-evidence-001',
              vin: 'VIN-HISTORY-SUMMARY',
              plate: '粤A轨迹1',
              protocol: 'JT808',
              frameType: '0x0200',
              rawSizeBytes: 96,
              deviceTime: '2026-07-03 10:00:00',
              serverTime: '2026-07-03 10:00:02',
              parsedFields: {
                'jt808.location.longitude': 113.201,
                'jt808.location.total_mileage_km': 1000
              }
            }],
            total: 1,
            limit: 10,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect((await screen.findAllByText('VIN-HISTORY-SUMMARY')).length).toBeGreaterThan(0);
  expect(screen.getByText('轨迹回放工作台')).toBeInTheDocument();
  expect(screen.getByText('客户复盘导航')).toBeInTheDocument();
  expect(screen.getByText('像 Trips History 一样，先锁定车辆和时间，再回放路线、核对里程、导出证据。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户复盘导航 选车和时间 调整筛选' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户复盘导航 回放路线 播放轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户复盘导航 核对里程 里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户复盘导航 导出证据 复制交付' })).toBeInTheDocument();
  expect(screen.getByText('客户时间窗证据链')).toBeInTheDocument();
  expect(screen.getByText('同一辆车和同一时间窗，串起实时轨迹、里程核对、历史导出、字段裁剪和告警复盘。')).toBeInTheDocument();
  expect(screen.getByText('接入来源只是证据来源；客户页面只回答车辆在哪里、跑了多少、能否导出、异常谁处理。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户时间窗证据链 轨迹复盘 查看轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户时间窗证据链 里程核对 里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户时间窗证据链 历史导出 明细证据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户时间窗证据链 字段裁剪 字段导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户时间窗证据链 告警复盘 告警事件' })).toBeInTheDocument();
  expect(screen.getByText('行程复盘摘要')).toBeInTheDocument();
  expect(screen.getByText('把客户最关心的起止时间、路线跨度、里程差、最高速度和异常提示放在轨迹前面。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '行程复盘摘要 起点 2026-07-03 10:00:00' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '行程复盘摘要 终点 2026-07-03 11:00:00' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '行程复盘摘要 行程里程 22.6 km' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '行程复盘摘要 最高速度 58.8 km/h' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '行程复盘摘要 异常提示 1 项' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '行程复盘动作 轨迹回放 播放轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '行程复盘动作 统计核对 里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '行程复盘动作 导出证据 复制交付' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '行程复盘动作 告警复盘 告警事件' })).toBeInTheDocument();
  expect(screen.getByText('按车辆和时间窗复盘每一段行程')).toBeInTheDocument();
  expect(screen.getByText('客户需要看到路线、起止时间、里程变化、速度异常和可导出的证据。明细证据和字段裁剪只作为轨迹复盘的依据。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '轨迹回放指标 轨迹点 2' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '轨迹回放工作台 轨迹复盘 播放轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '轨迹回放工作台 客户导出 复制交付' })).toBeInTheDocument();
  expect(screen.getByText('客户轨迹决策台')).toBeInTheDocument();
  expect(screen.getByText('先判断轨迹能否复盘，再进入证据、字段和里程统计')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户轨迹决策 复盘结论 复制决策' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户轨迹决策 定位覆盖 播放轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户轨迹决策 时间断点 复盘包' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户轨迹决策 里程速度 里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户轨迹决策 明细复核 明细证据' })).toBeInTheDocument();
  fireEvent.click(screen.getAllByRole('button', { name: /复制决策说明/ })[0]);
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【客户轨迹决策说明】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('决策结论：可复盘，需附异常说明'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('客户复盘路径：'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('4. 最后回到依据：异常点必须打开明细证据、字段裁剪和里程统计复核。'));

  fireEvent.click(screen.getByRole('button', { name: /复制轨迹摘要/ }));

  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【轨迹回放摘要】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆：VIN-HISTORY-SUMMARY'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('数据通道：JT808'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('查询范围：2026-07-03 至 2026-07-04'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹点：2，有效定位：2'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('区间里程：22.6 km'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('最高速度：58.8 km/h'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('起点：2026-07-03 10:00:00'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('终点：2026-07-03 11:00:00'));

  fireEvent.click(screen.getAllByRole('button', { name: /复制数据复核包/ })[0]);

  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【数据复核包】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆：VIN-HISTORY-SUMMARY'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('位置记录：2，有效定位：2'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('明细证据：1，字段裁剪：2'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('异常：断点 1 / 里程回退 0 / 超速 0'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹回放：http://localhost:3000/#/history?keyword=VIN-HISTORY-SUMMARY&protocol=JT808&dateFrom=2026-07-03&dateTo=2026-07-04'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('历史位置：http://localhost:3000/#/history-query?keyword=VIN-HISTORY-SUMMARY&protocol=JT808&tab=location&dateFrom=2026-07-03&dateTo=2026-07-04'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('明细证据：http://localhost:3000/#/history-query?keyword=VIN-HISTORY-SUMMARY&protocol=JT808&tab=raw&dateFrom=2026-07-03&dateTo=2026-07-04&includeFields=true'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('字段裁剪：http://localhost:3000/#/history-query?keyword=VIN-HISTORY-SUMMARY&protocol=JT808&tab=fields&dateFrom=2026-07-03&dateTo=2026-07-04&includeFields=true'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('里程统计：http://localhost:3000/#/mileage?keyword=VIN-HISTORY-SUMMARY&protocol=JT808&dateFrom=2026-07-03&dateTo=2026-07-04'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务：http://localhost:3000/#/detail?keyword=VIN-HISTORY-SUMMARY&protocol=JT808'));

  fireEvent.click(screen.getByRole('button', { name: /复制客户影响/ }));

  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【客户轨迹影响】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆：VIN-HISTORY-SUMMARY'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('数据通道：JT808'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('业务状态：需要复核'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹范围：位置 2 / 有效定位 2 / 明细证据 1 / 字段裁剪 2'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('定位覆盖率：100%'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('回放跨度：60 分钟，采样间隔：60 分钟/点'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('异常影响：断点 1 / 里程回退 0 / 超速 0'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('地图能力：高德 JS API 待配置'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('历史查询导出：http://localhost:3000/#/history-query?keyword=VIN-HISTORY-SUMMARY&protocol=JT808&tab=location&dateFrom=2026-07-03&dateTo=2026-07-04'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('明细证据：http://localhost:3000/#/history-query?keyword=VIN-HISTORY-SUMMARY&protocol=JT808&tab=raw&dateFrom=2026-07-03&dateTo=2026-07-04&includeFields=true'));

  fireEvent.click(screen.getByRole('button', { name: /复制轨迹复盘包/ }));

  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【客户轨迹复盘包】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆：VIN-HISTORY-SUMMARY'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹规模：位置 2 / 有效定位 2 / 明细证据 1 / 字段裁剪 2'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹质量：定位覆盖率 100% / 回放跨度 60 分钟 / 采样间隔 60 分钟/点'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('里程速度：区间里程 22.6 km / 最高速度 58.8 km/h'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('当前回放点：点 1/2，2026-07-03 10:00:00，12.5 km/h，1,000 km'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('异常判读：断点 1 / 里程回退 0 / 超速 0'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('下一步：建议核对明细证据、字段裁剪、当日里程统计，并确认平台是否存在断链或补发。'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('明细证据：http://localhost:3000/#/history-query?keyword=VIN-HISTORY-SUMMARY&protocol=JT808&tab=raw&dateFrom=2026-07-03&dateTo=2026-07-04&includeFields=true'));
});

test('opens same-day mileage statistics from quality issue row', async () => {
  window.history.replaceState(null, '', '/#/alert-events');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            issueVehicleCount: 1,
            issueRecordCount: 1,
            errorCount: 0,
            warningCount: 1,
            protocols: [{ name: 'JT808', count: 1 }],
            issueTypes: [{ name: 'FIELD_MISSING', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-QUALITY-MILEAGE',
              plate: '粤A告警里',
              phone: '13307795425',
              sourceEndpoint: '115.231.168.135:43625',
              protocol: 'JT808',
              issueType: 'FIELD_MISSING',
              severity: 'warning',
              lastSeen: '2026-07-03 20:12:10',
              detail: 'JT808 位置帧缺少总里程字段'
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { vehicleCount: 1, recordCount: 1, sourceCount: 1, totalMileageKm: 18.8, averageMileagePerVin: 18.8 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/daily')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-QUALITY-MILEAGE',
              plate: '粤A告警里',
              source: 'JT808',
              date: '2026-07-03',
              startMileageKm: 200,
              endMileageKm: 218.8,
              dailyMileageKm: 18.8,
              anomalySeverity: 'warning'
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/statistics/online-summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vehicleCount: 4,
            onlineVehicleCount: 3,
            offlineVehicleCount: 1,
            onlineRatePercent: 75,
            redisOnlineKeys: 92,
            protocolStats: [
              { protocol: 'JT808', online: 2, total: 3 },
              { protocol: 'GB32960', online: 1, total: 2 }
            ],
            evidence: '由车辆服务覆盖汇总、实时来源状态和 Redis 在线 key 计算'
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('VIN-QUALITY-MILEAGE')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '核对里程' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/mileage?keyword=VIN-QUALITY-MILEAGE&protocol=JT808&dateFrom=2026-07-03&dateTo=2026-07-04');
  });
});

test('drills into quality issues by issue type', async () => {
  window.history.replaceState(null, '', '/#/alert-events');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            issueVehicleCount: 1,
            issueRecordCount: 1,
            errorCount: 0,
            warningCount: 1,
            protocols: [{ name: 'VEHICLE_SERVICE', count: 1 }],
            issueTypes: [{ name: 'NO_SOURCE', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  const issueButtons = await screen.findAllByRole('button', { name: /主要问题 暂无数据来源 1/ });
  const issueSummaryButton = issueButtons[issueButtons.length - 1];
  expect(issueButtons.length).toBeGreaterThanOrEqual(1);
  fireEvent.click(issueSummaryButton as HTMLElement);

  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/alert-events?issueType=NO_SOURCE&limit=20&offset=0'), undefined);
  });
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/alert-events/summary?issueType=NO_SOURCE'), undefined);
});

test('drills into quality issues by protocol bucket', async () => {
  window.history.replaceState(null, '', '/#/alert-events');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            issueVehicleCount: 2,
            issueRecordCount: 3,
            errorCount: 1,
            warningCount: 2,
            protocols: [
              { name: 'JT808', count: 2 },
              { name: 'VEHICLE_SERVICE', count: 1 }
            ],
            issueTypes: [{ name: 'VIN_MISSING', count: 2 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [],
            total: 3,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  fireEvent.click(await screen.findByRole('button', { name: '查看 JT808 质量问题' }));

  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/alert-events?protocol=JT808&limit=20&offset=0'), undefined);
  });
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/alert-events/summary?protocol=JT808'), undefined);
  expect(window.location.hash).toBe('#/alert-events?protocol=JT808');
});

test('drills into quality issues by issue type bucket', async () => {
  window.history.replaceState(null, '', '/#/alert-events');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            issueVehicleCount: 2,
            issueRecordCount: 4,
            errorCount: 2,
            warningCount: 2,
            protocols: [{ name: 'JT808', count: 4 }],
            issueTypes: [
              { name: 'VIN_MISSING', count: 2 },
              { name: 'LINK_GAP', count: 2 }
            ]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [],
            total: 4,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  fireEvent.click(await screen.findByRole('button', { name: '查看 VIN 缺失质量问题' }));

  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/alert-events?issueType=VIN_MISSING&limit=20&offset=0'), undefined);
  });
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/alert-events/summary?issueType=VIN_MISSING'), undefined);
  expect(window.location.hash).toBe('#/alert-events?issueType=VIN_MISSING');
});

test('applies shareable quality filters from hash', async () => {
  window.history.replaceState(null, '', '/#/alert-events?keyword=%E7%B2%A4A&protocol=VEHICLE_SERVICE&issueType=NO_SOURCE');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            issueVehicleCount: 1,
            issueRecordCount: 1,
            errorCount: 0,
            warningCount: 1,
            protocols: [{ name: 'VEHICLE_SERVICE', count: 1 }],
            issueTypes: [{ name: 'NO_SOURCE', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: { items: [], total: 1, limit: 20, offset: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/alert-events?keyword=%E7%B2%A4A&protocol=VEHICLE_SERVICE&issueType=NO_SOURCE&limit=20&offset=0'), undefined);
  });
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/alert-events/summary?keyword=%E7%B2%A4A&protocol=VEHICLE_SERVICE&issueType=NO_SOURCE'), undefined);
});

test('shows and clears current quality filters', async () => {
  window.history.replaceState(null, '', '/#/alert-events?keyword=%E7%B2%A4A&protocol=VEHICLE_SERVICE&issueType=NO_SOURCE');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            issueVehicleCount: 1,
            issueRecordCount: 1,
            errorCount: 0,
            warningCount: 1,
            protocols: [{ name: 'VEHICLE_SERVICE', count: 1 }],
            issueTypes: [{ name: 'NO_SOURCE', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: { items: [], total: 1, limit: 20, offset: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('当前筛选')).toBeInTheDocument();
  expect(screen.getByText('关键词：粤A')).toBeInTheDocument();
  expect(screen.getByText('数据来源：车辆服务')).toBeInTheDocument();
  expect(screen.getByText('问题类型：暂无数据来源')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '清空筛选' }));

  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/alert-events?limit=20&offset=0'), undefined);
  });
  expect(window.location.hash).toBe('#/alert-events');
});

test('copies shareable quality filter link', async () => {
  window.history.replaceState(null, '', '/#/alert-events?keyword=%E7%B2%A4A&protocol=VEHICLE_SERVICE&issueType=NO_SOURCE');
  const writeText = vi.fn().mockResolvedValue(undefined);
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            issueVehicleCount: 1,
            issueRecordCount: 1,
            errorCount: 0,
            warningCount: 1,
            protocols: [{ name: 'VEHICLE_SERVICE', count: 1 }],
            issueTypes: [{ name: 'NO_SOURCE', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: { items: [], total: 1, limit: 20, offset: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  fireEvent.click(await screen.findByRole('button', { name: /复制筛选链接/ }));

  await waitFor(() => {
    expect(writeText).toHaveBeenCalledWith(expect.stringContaining('/#/alert-events?keyword=%E7%B2%A4A&protocol=VEHICLE_SERVICE&issueType=NO_SOURCE'));
  });
});

test('opens vehicle detail from shareable hash', async () => {
  window.history.replaceState(null, '', '/#/detail?keyword=%E7%B2%A4AG18312');
  vi.spyOn(globalThis, 'fetch').mockResolvedValue({
    ok: true,
    json: async () => ({
      data: {
        vin: 'LB9A32A24R0LS1426',
        lookupKey: '粤AG18312',
        lookupResolved: true,
        resolution: {
          lookupKey: '粤AG18312',
          resolved: true,
          vin: 'LB9A32A24R0LS1426',
          plate: '粤AG18312',
          phone: '13307795425',
          oem: 'G7s',
          protocols: ['GB32960', 'JT808'],
          online: true,
          lastSeen: '2026-07-03 20:12:10'
        },
        sources: ['GB32960', 'JT808'],
        sourceStatus: [],
        realtime: [],
        history: { items: [], total: 0, limit: 20, offset: 0 },
        raw: { items: [], total: 0, limit: 10, offset: 0 },
        mileage: { items: [], total: 0, limit: 20, offset: 0 },
        quality: { items: [], total: 0, limit: 20, offset: 0 }
      },
      traceId: 'trace-test',
      timestamp: 1783094400000
    })
  } as Response);

  render(<App />);

  expect(await screen.findByDisplayValue('粤AG18312')).toBeInTheDocument();
  expect(screen.getAllByText('车辆服务').length).toBeGreaterThanOrEqual(1);
});

test('shows vehicle context when opening detail from shareable hash', async () => {
  window.history.replaceState(null, '', '/#/detail?keyword=VIN001');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service/overview')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN001',
            plate: '粤AG18312',
            phone: '13307795425',
            oem: 'G7s',
            protocols: ['GB32960', 'JT808'],
            primaryProtocol: 'JT808',
            coverageStatus: 'partial',
            sourceCount: 2,
            onlineSourceCount: 1,
            lastSeen: '2026-07-03 20:12:10',
            realtimeCount: 2,
            historyCount: 0,
            rawCount: 0,
            mileageCount: 0,
            qualityIssueCount: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN001',
            lookupKey: 'VIN001',
            lookupResolved: true,
            sources: ['GB32960', 'JT808'],
            sourceStatus: [],
            realtime: [],
            history: { items: [], total: 0, limit: 20, offset: 0 },
            raw: { items: [], total: 0, limit: 10, offset: 0 },
            mileage: { items: [], total: 0, limit: 20, offset: 0 },
            quality: { items: [], total: 0, limit: 20, offset: 0 }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000, platformRelease: 'platform-20260704155844' } }
          : { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('粤AG18312 / VIN001')).toBeInTheDocument();
  expect(screen.getByText('当前车辆：数据通道不完整')).toBeInTheDocument();
});

test('refreshes vehicle context when hash changes to another detail vehicle', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service/overview')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN002',
            plate: '粤B20002',
            phone: '',
            oem: 'G7s',
            protocols: ['JT808'],
            primaryProtocol: 'JT808',
            coverageStatus: 'offline',
            sourceCount: 1,
            onlineSourceCount: 0,
            lastSeen: '2026-07-03 20:10:10',
            realtimeCount: 1,
            historyCount: 0,
            rawCount: 0,
            mileageCount: 0,
            qualityIssueCount: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { onlineVehicles: 3, activeToday: 4, frameToday: 1286320, issueVehicles: 7, kafkaLag: 0, protocols: [], serviceStatuses: [], linkHealth: [] },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN002',
            lookupKey: 'VIN002',
            lookupResolved: true,
            sources: ['JT808'],
            sourceStatus: [],
            realtime: [],
            history: { items: [], total: 0, limit: 20, offset: 0 },
            raw: { items: [], total: 0, limit: 10, offset: 0 },
            mileage: { items: [], total: 0, limit: 20, offset: 0 },
            quality: { items: [], total: 0, limit: 20, offset: 0 }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }
          : { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  window.history.pushState(null, '', '/#/detail?keyword=VIN002');
  window.dispatchEvent(new HashChangeEvent('hashchange'));

  expect(await screen.findByText('粤B20002 / VIN002')).toBeInTheDocument();
  expect(screen.getByText('当前车辆：车辆离线')).toBeInTheDocument();
});

test('shows vehicle context when opening detail from sidebar navigation', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service/overview')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'LB9A32A24R0LS1426',
            plate: '粤AGQ8398',
            phone: '',
            oem: 'G7s',
            protocols: ['JT808'],
            primaryProtocol: 'JT808',
            coverageStatus: 'offline',
            sourceCount: 1,
            onlineSourceCount: 0,
            lastSeen: '2026-07-03 20:10:10',
            realtimeCount: 1,
            historyCount: 0,
            rawCount: 0,
            mileageCount: 0,
            qualityIssueCount: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/dashboard/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { onlineVehicles: 3, activeToday: 4, frameToday: 1286320, issueVehicles: 7, kafkaLag: 0, protocols: [], serviceStatuses: [], linkHealth: [] },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'LB9A32A24R0LS1426',
            lookupKey: 'LB9A32A24R0LS1426',
            lookupResolved: true,
            sources: ['JT808'],
            sourceStatus: [],
            realtime: [],
            history: { items: [], total: 0, limit: 20, offset: 0 },
            raw: { items: [], total: 0, limit: 10, offset: 0 },
            mileage: { items: [], total: 0, limit: 20, offset: 0 },
            quality: { items: [], total: 0, limit: 20, offset: 0 }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }
          : { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  fireEvent.change(screen.getByPlaceholderText('搜索 VIN / 车牌 / 手机号'), { target: { value: 'LB9A32A24R0LS1426' } });
  fireEvent.click(screen.getByRole('button', { name: '查询车辆' }));
  expect(await screen.findByText('粤AGQ8398 / LB9A32A24R0LS1426')).toBeInTheDocument();

  fireEvent.click(screen.getAllByText('车辆档案')[0]);

  expect(await screen.findByText('粤AGQ8398 / LB9A32A24R0LS1426')).toBeInTheDocument();
  expect(screen.getByText('当前车辆：车辆离线')).toBeInTheDocument();
});

test('applies protocol from shareable history hash to API requests', async () => {
  window.history.replaceState(null, '', '/#/history?keyword=%E7%B2%A4AG18312&protocol=JT808&dateFrom=2026-07-01&dateTo=2026-07-03');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input, init) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/history/locations?'), undefined);
  });
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('protocol=JT808'), undefined);
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('dateFrom=2026-07-01'), undefined);
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('dateTo=2026-07-03'), undefined);
  expect(fetchMock).toHaveBeenCalledWith('/api/history/raw-frames/query', expect.objectContaining({
    method: 'POST',
    body: expect.stringContaining('"protocol":"JT808"')
  }));
  expect(screen.getByText('当前车辆：粤AG18312')).toBeInTheDocument();
  expect(screen.getAllByText('来源证据：JT808').length).toBeGreaterThan(0);

  fireEvent.click(screen.getByRole('button', { name: '顶部里程统计' }));
  expect(window.location.hash).toBe('#/mileage?keyword=%E7%B2%A4AG18312&protocol=JT808&dateFrom=2026-07-01&dateTo=2026-07-03');
  fireEvent.click(screen.getByRole('button', { name: '顶部数据导出' }));
  expect(window.location.hash).toBe('#/history-query?keyword=%E7%B2%A4AG18312&protocol=JT808&tab=raw&dateFrom=2026-07-01&dateTo=2026-07-03');
  fireEvent.click(await screen.findByRole('button', { name: '顶部告警事件正常' }));
  expect(window.location.hash).toBe('#/alert-events?keyword=%E7%B2%A4AG18312&protocol=JT808&dateFrom=2026-07-01&dateTo=2026-07-03');
});

test('topbar customer time window scopes vehicle service tasks', async () => {
  window.history.replaceState(null, '', '/#/dashboard');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service/overview')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN-TOPBAR-WINDOW',
            plate: '粤A时间窗',
            primaryProtocol: 'JT808',
            protocols: ['JT808'],
            coverageStatus: 'online',
            sourceCount: 1,
            onlineSourceCount: 1,
            realtimeCount: 1,
            historyCount: 8,
            rawCount: 20,
            mileageCount: 3,
            qualityIssueCount: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(screen.getByText('客户时间窗')).toBeInTheDocument();
  expect(screen.getByText('同步约束地图、轨迹、里程、导出和告警')).toBeInTheDocument();
  expect(screen.getByLabelText('客户时间窗开始日期')).toHaveAttribute('placeholder', 'YYYY-MM-DD');
  expect(screen.getByLabelText('客户时间窗结束日期')).toHaveAttribute('placeholder', 'YYYY-MM-DD');

  fireEvent.change(screen.getByPlaceholderText('搜索 VIN / 车牌 / 手机号'), { target: { value: '粤A时间窗' } });
  fireEvent.change(screen.getByLabelText('客户时间窗开始日期'), { target: { value: '2026-07-01' } });
  fireEvent.change(screen.getByLabelText('客户时间窗结束日期'), { target: { value: '2026-07-03' } });
  fireEvent.click(screen.getByRole('button', { name: '查询车辆' }));
  expect(await screen.findByText('粤A时间窗 / VIN-TOPBAR-WINDOW')).toBeInTheDocument();

  fireEvent.click(screen.getByRole('button', { name: '顶部里程统计' }));
  expect(window.location.hash).toBe('#/mileage?keyword=VIN-TOPBAR-WINDOW&protocol=JT808&dateFrom=2026-07-01&dateTo=2026-07-03');

  fireEvent.click(screen.getByRole('button', { name: '顶部数据导出' }));
  expect(window.location.hash).toBe('#/history-query?keyword=VIN-TOPBAR-WINDOW&protocol=JT808&tab=raw&dateFrom=2026-07-01&dateTo=2026-07-03');

  fireEvent.click(await screen.findByRole('button', { name: '顶部告警事件正常' }));
  expect(window.location.hash).toBe('#/alert-events?keyword=VIN-TOPBAR-WINDOW&protocol=JT808&dateFrom=2026-07-01&dateTo=2026-07-03');
});

test('topbar customer time window presets support common monitoring ranges', async () => {
  vi.setSystemTime(new Date('2026-07-05T10:00:00+08:00'));
  window.history.replaceState(null, '', '/#/dashboard');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service/overview')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN-TOPBAR-PRESET',
            plate: '粤A快捷窗',
            primaryProtocol: 'GB32960',
            protocols: ['GB32960'],
            coverageStatus: 'online',
            sourceCount: 1,
            onlineSourceCount: 1,
            realtimeCount: 1,
            historyCount: 8,
            rawCount: 20,
            mileageCount: 3,
            qualityIssueCount: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  fireEvent.click(screen.getByRole('button', { name: '顶部时间窗 今天' }));
  expect(screen.getByLabelText('顶部时间窗开始')).toHaveValue('2026-07-05');
  expect(screen.getByLabelText('顶部时间窗结束')).toHaveValue('2026-07-05');

  fireEvent.click(screen.getByRole('button', { name: '顶部时间窗 近7天' }));
  expect(screen.getByLabelText('顶部时间窗开始')).toHaveValue('2026-06-29');
  expect(screen.getByLabelText('顶部时间窗结束')).toHaveValue('2026-07-05');
  expect(await screen.findByRole('button', { name: /时间窗服务路径 1 锁定范围 7 天窗口 应用时间窗/ })).toBeInTheDocument();

  fireEvent.change(screen.getByPlaceholderText('搜索 VIN / 车牌 / 手机号'), { target: { value: '粤A快捷窗' } });
  fireEvent.click(screen.getByRole('button', { name: '查询车辆' }));
  expect(await screen.findByText('粤A快捷窗 / VIN-TOPBAR-PRESET')).toBeInTheDocument();

  fireEvent.click(screen.getByRole('button', { name: '顶部数据导出' }));
  expect(window.location.hash).toBe('#/history-query?keyword=VIN-TOPBAR-PRESET&protocol=GB32960&tab=raw&dateFrom=2026-06-29&dateTo=2026-07-05');

  fireEvent.click(screen.getByRole('button', { name: '顶部时间窗 清空' }));
  expect(screen.getByLabelText('顶部时间窗开始')).toHaveValue('');
  expect(screen.getByLabelText('顶部时间窗结束')).toHaveValue('');

  fireEvent.click(screen.getByRole('button', { name: '顶部里程统计' }));
  expect(window.location.hash).toBe('#/mileage?keyword=VIN-TOPBAR-PRESET&protocol=GB32960');
});

test('topbar copies customer vehicle service scope links', async () => {
  vi.setSystemTime(new Date('2026-07-05T10:00:00+08:00'));
  window.history.replaceState(null, '', '/#/dashboard');
  const writeText = vi.spyOn(navigator.clipboard, 'writeText').mockResolvedValue(undefined);
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service/overview')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN-SHARE-SCOPE',
            plate: '粤A共享窗',
            primaryProtocol: 'JT808',
            protocols: ['JT808'],
            coverageStatus: 'online',
            sourceCount: 1,
            onlineSourceCount: 1,
            realtimeCount: 1,
            historyCount: 8,
            rawCount: 20,
            mileageCount: 3,
            qualityIssueCount: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  fireEvent.click(screen.getByRole('button', { name: '顶部时间窗 近7天' }));
  fireEvent.change(screen.getByPlaceholderText('搜索 VIN / 车牌 / 手机号'), { target: { value: '粤A共享窗' } });
  fireEvent.click(screen.getByRole('button', { name: '查询车辆' }));
  expect(await screen.findByText('粤A共享窗 / VIN-SHARE-SCOPE')).toBeInTheDocument();

  fireEvent.click(screen.getByRole('button', { name: '顶部复制客户服务范围' }));

  const copied = String(writeText.mock.lastCall?.[0] ?? '');
  expect(copied).toContain('【客户车辆服务范围】');
  expect(copied).toContain('车辆：粤A共享窗 / VIN-SHARE-SCOPE');
  expect(copied).toContain('时间窗：2026-06-29 至 2026-07-05');
  expect(copied).toContain('实时地图：http://localhost:3000/#/map?keyword=VIN-SHARE-SCOPE&protocol=JT808');
  expect(copied).toContain('轨迹回放：http://localhost:3000/#/history?keyword=VIN-SHARE-SCOPE&protocol=JT808&dateFrom=2026-06-29&dateTo=2026-07-05');
  expect(copied).toContain('里程统计：http://localhost:3000/#/mileage?keyword=VIN-SHARE-SCOPE&protocol=JT808&dateFrom=2026-06-29&dateTo=2026-07-05');
  expect(copied).toContain('数据导出：http://localhost:3000/#/history-query?keyword=VIN-SHARE-SCOPE&protocol=JT808&tab=raw&dateFrom=2026-06-29&dateTo=2026-07-05');
  expect(copied).toContain('告警通知：http://localhost:3000/#/alert-events?keyword=VIN-SHARE-SCOPE&protocol=JT808&dateFrom=2026-06-29&dateTo=2026-07-05');
});

test('topbar saves and restores reusable customer vehicle views', async () => {
  vi.setSystemTime(new Date('2026-07-05T10:00:00+08:00'));
  window.history.replaceState(null, '', '/#/dashboard');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service/overview')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN-SAVED-SCOPE',
            plate: '粤A复用窗',
            primaryProtocol: 'GB32960',
            protocols: ['GB32960'],
            coverageStatus: 'online',
            sourceCount: 1,
            onlineSourceCount: 1,
            realtimeCount: 1,
            historyCount: 8,
            rawCount: 20,
            mileageCount: 3,
            qualityIssueCount: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  fireEvent.click(screen.getByRole('button', { name: '顶部时间窗 近7天' }));
  fireEvent.change(screen.getByPlaceholderText('搜索 VIN / 车牌 / 手机号'), { target: { value: '粤A复用窗' } });
  fireEvent.click(screen.getByRole('button', { name: '查询车辆' }));
  expect(await screen.findByText('粤A复用窗 / VIN-SAVED-SCOPE')).toBeInTheDocument();

  fireEvent.click(screen.getByRole('button', { name: '顶部保存客户视图' }));
  expect(screen.getByRole('button', { name: '客户视图 粤A复用窗 / VIN-SAVED-SCOPE 2026-06-29 至 2026-07-05' })).toBeInTheDocument();

  fireEvent.click(screen.getByRole('button', { name: '顶部时间窗 清空' }));
  fireEvent.click(screen.getByRole('button', { name: '顶部地图态势' }));
  expect(window.location.hash).toBe('#/map?keyword=VIN-SAVED-SCOPE&protocol=GB32960');

  fireEvent.click(screen.getByRole('button', { name: '客户视图 粤A复用窗 / VIN-SAVED-SCOPE 2026-06-29 至 2026-07-05' }));
  expect(screen.getByLabelText('顶部时间窗开始')).toHaveValue('2026-06-29');
  expect(screen.getByLabelText('顶部时间窗结束')).toHaveValue('2026-07-05');

  fireEvent.click(screen.getByRole('button', { name: '顶部里程统计' }));
  expect(window.location.hash).toBe('#/mileage?keyword=VIN-SAVED-SCOPE&protocol=GB32960&dateFrom=2026-06-29&dateTo=2026-07-05');
});

test('shows and clears current history filters while keeping vehicle scope', async () => {
  window.history.replaceState(null, '', '/#/history-query?keyword=VIN-HISTORY-001&protocol=JT808&dateFrom=2026-07-01+00%3A00%3A00&dateTo=2026-07-01+23%3A59%3A59&includeFields=true&fields=jt808.location.longitude%2Cjt808.location.latitude&tab=raw');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input, init) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/locations')) {
      return {
        ok: true,
        json: async () => ({
          data: { items: [], total: 0, limit: 10, offset: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/raw-frames/query')) {
      return {
        ok: true,
        json: async () => ({
          data: { items: [], total: 0, limit: 10, offset: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('当前历史筛选')).toBeInTheDocument();
  expect(screen.getByText('车辆：VIN-HISTORY-001')).toBeInTheDocument();
  expect(screen.getAllByText('来源证据：JT808').length).toBeGreaterThan(0);
  expect(screen.getByText('开始时间：2026-07-01 00:00:00')).toBeInTheDocument();
  expect(screen.getByText('结束时间：2026-07-01 23:59:59')).toBeInTheDocument();
  expect(screen.getAllByText('返回字段明细').length).toBeGreaterThanOrEqual(2);
  expect(screen.getByText('字段裁剪：2 个')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '清空筛选' }));

  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/history/locations?limit=10&offset=0&keyword=VIN-HISTORY-001'), undefined);
  });
  expect(window.location.hash).toBe('#/history-query?keyword=VIN-HISTORY-001&tab=raw');
});

test('shows parsed field history as a flattened evidence table', async () => {
  window.history.replaceState(null, '', '/#/history-query?keyword=VIN-FIELDS-001&protocol=GB32960&tab=fields&fields=gb32960.vehicle.speed_kmh');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input, init) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/locations')) {
      return {
        ok: true,
        json: async () => ({
          data: { items: [], total: 0, limit: 10, offset: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/raw-frames/query')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              id: 'raw-field-001',
              vin: 'VIN-FIELDS-001',
              plate: '粤A字段1',
              protocol: 'GB32960',
              frameType: 'realtime',
              rawSizeBytes: 256,
              deviceTime: '2026-07-03 11:56:06',
              serverTime: '2026-07-03 11:56:07',
              parsedFields: {
                'gb32960.vehicle.speed_kmh': 42,
                'gb32960.vehicle.soc_percent': 78.4
              }
            }],
            total: 1,
            limit: 10,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByRole('heading', { name: '历史查询导出' })).toBeInTheDocument();
  expect(screen.getAllByText('客户导出向导').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('按客户交付顺序完成：先锁定车辆和时间窗，再选择轨迹、明细、字段或统计证据，最后复核质量并复制交付包。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户导出向导 1 锁定范围 VIN-FIELDS-001 / GB32960' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户导出向导 2 选择证据 字段证据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户导出向导 3 质量复核 可交付' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户导出向导 4 导出交付 复制交付包' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: /复制导出向导包/ })).toBeInTheDocument();
  expect(screen.getByText('客户数据交付封面')).toBeInTheDocument();
  expect(screen.getByText('先给客户一个可读封面：交付结论、车辆范围、时间窗、交付物、质量提示和下一步动作。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户数据交付封面 交付结论 可交付 复制封面' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户数据交付封面 轨迹报告 0 点 补查轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户数据交付封面 字段证据 2 字段 导出字段' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户数据交付封面 客户说明 可复制 复制说明' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: /复制交付封面包/ })).toBeInTheDocument();
  expect(screen.getByText('客户数据交付结论条')).toBeInTheDocument();
  expect(screen.getByText('先判断这次查询能交付什么，再选择导出位置、明细证据、字段裁剪或复制说明。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户数据交付结论 交付状态 可交付' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户数据交付结论 位置历史 0 条' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户数据交付结论 明细证据 1 帧' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户数据交付结论 字段裁剪 2 字段' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户数据交付动作 导出位置 位置CSV' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户数据交付动作 导出明细 明细CSV' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户数据交付动作 字段裁剪 字段CSV' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户数据交付动作 复制说明 交付包' })).toBeInTheDocument();
  expect(screen.getByText('导出下一步行动条')).toBeInTheDocument();
  expect(screen.getByText('当前查询先决定交付什么、缺什么、怎么复用，避免客户拿到一堆无解释 CSV。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '导出下一步行动 首要交付 字段证据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '导出下一步行动 轨迹状态 待补轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '导出下一步行动 证据状态 1 帧 / 2 字段' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '导出下一步行动 说明状态 可交付' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '导出下一步动作 导出优先项 执行导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '导出下一步动作 补查轨迹 位置历史' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '导出下一步动作 复制说明 交付包' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '导出下一步动作 保存视图 复用' })).toBeInTheDocument();
  expect(screen.getByText('车辆历史服务台')).toBeInTheDocument();
  expect(screen.getByText('客户证据包交付总览')).toBeInTheDocument();
  expect(screen.getByText('先给客户一个结论：当前筛选能交付哪些证据、还缺哪一类证据、下一步应该导出还是补查。')).toBeInTheDocument();
  expect(screen.getByText('客户交付准备度')).toBeInTheDocument();
  expect(screen.getByText('把一次历史查询收敛成四个交付判断：范围是否锁定、轨迹是否可用、字段是否裁剪、说明是否可复制。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户交付准备度 范围已锁定 VIN-FIELDS-001 查看范围' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户交付准备度 轨迹待补 0 点 查询轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户交付准备度 字段已裁剪 2 字段 字段导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户交付准备度 说明可复制 可交付 复制说明' })).toBeInTheDocument();
  expect(screen.getByText('客户导出验收清单')).toBeInTheDocument();
  expect(screen.getByText('客户交付前只看五个门禁：范围、轨迹、明细、字段、异常说明；任何一项不满足都能直接进入补查或导出。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户导出验收 范围锁定 VIN-FIELDS-001 / GB32960' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户导出验收 轨迹证据 0 点' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户导出验收 明细证据 1 帧' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户导出验收 字段裁剪 2 字段' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户导出验收 异常说明 可交付' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '导出验收动作 复制验收 交付说明' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '导出验收动作 导出位置 位置CSV' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '导出验收动作 导出字段 字段CSV' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '导出验收动作 查看明细 明细证据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户证据包交付总览 交付范围 VIN-FIELDS-001 / GB32960 查看范围' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户证据包交付总览 轨迹证据 0 点 查询轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户证据包交付总览 明细字段 2 字段 字段导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户证据包交付总览 交付说明 可交付 复制说明' })).toBeInTheDocument();
  expect(screen.getByText('客户先选车辆和时间窗，再拿到路线、里程、原始证据、字段裁剪和告警说明。')).toBeInTheDocument();
  expect(screen.getAllByText('来源证据：GB32960').length).toBeGreaterThan(0);
  expect(screen.getByText('当前服务范围：VIN-FIELDS-001 / GB32960')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆历史服务台 路线复盘 0 点 查询位置' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆历史服务台 里程核对 - 里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆历史服务台 明细证据 1 帧 查看证据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆历史服务台 字段裁剪 2 字段 字段配置' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆历史服务台 客户导出 可交付 复制交付' })).toBeInTheDocument();
  expect(screen.getByText('客户导出中心')).toBeInTheDocument();
  expect(screen.getByText('按客户用途选择报表：行程历史、里程对账、字段证据和告警说明。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户导出中心 行程历史 0 点 导出轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户导出中心 里程对账 - 里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户导出中心 字段证据 2 字段 导出字段' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户导出中心 告警说明 无明显异常 复制说明' })).toBeInTheDocument();
  expect(screen.getByText('自定义时间窗监控台')).toBeInTheDocument();
  expect(screen.getByText('客户选定车辆和时间范围后，直接判断这个窗口能否用于轨迹复盘、统计核对、历史证据导出和告警说明。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '自定义时间窗监控 轨迹复盘 位置历史' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '自定义时间窗监控 统计核对 里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '自定义时间窗监控 历史证据 明细证据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '自定义时间窗监控 告警复盘 告警说明' })).toBeInTheDocument();
  expect(screen.getByText('客户数据交付台')).toBeInTheDocument();
  expect(screen.getByText('轨迹回放工作台')).toBeInTheDocument();
  expect(screen.getByText('按车辆和时间窗复盘每一段行程')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '轨迹回放工作台 明细证据 查看证据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '轨迹回放工作台 客户导出 复制交付' })).toBeInTheDocument();
  expect(screen.getByText('面向客户的数据导出以车辆服务为中心：同一个筛选范围可以交付轨迹报告、位置历史、明细证据、字段裁剪、CSV 文件和里程统计。')).toBeInTheDocument();
  expect(screen.getByText('先锁定车辆和时间窗，再选择交付物并导出')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户数据交付台 选择范围 调整筛选' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户数据交付台 位置历史 导出位置' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户数据交付台 明细证据 导出证据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户数据交付台 字段裁剪 字段配置' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户数据交付台 交付说明 复制交付' })).toBeInTheDocument();
  expect(screen.getByText('报表交付模板')).toBeInTheDocument();
  expect(screen.getByText('按客户问题选择导出模板')).toBeInTheDocument();
  expect(screen.getByText('车队报表不只是下载 CSV：先选择用途，再生成轨迹报告、明细证据包、字段裁剪包或周期交付计划。')).toBeInTheDocument();
  expect(screen.getByText('客户历史问题')).toBeInTheDocument();
  expect(screen.getByText('用客户语言回答路线、里程、异常和导出问题')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户历史问题 这辆车这段时间在哪里？ 轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户历史问题 这段时间跑了多少公里？ 里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户历史问题 为什么定位或里程不连续？ 复核证据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户历史问题 明细能导出给客户吗？ 导出交付包' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '报表交付模板 轨迹报告 复制报告' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '报表交付模板 明细证据包 导出证据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '报表交付模板 字段裁剪包 字段配置' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '报表交付模板 周期交付计划 复制计划' })).toBeInTheDocument();
  expect(screen.getByText('客户报表交付节奏')).toBeInTheDocument();
  expect(screen.getByText('把现代车队平台的报表能力落到客户交付：日报复盘、周报对账、临时解释和字段模板复用。')).toBeInTheDocument();
  expect(screen.queryByText(/Fleetio|Samsara|Geotab/)).not.toBeInTheDocument();
  expect(screen.getByText('客户报表用途导航')).toBeInTheDocument();
  expect(screen.getByText('先选择客户要解决的问题，再决定导出位置、字段、统计或说明。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户报表用途导航 日报复盘 客户日报 生成日报' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户报表用途导航 周报对账 客户对账 生成周报' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户报表用途导航 临时解释 质量说明 复制说明' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户报表用途导航 字段模板 稳定字段 字段配置' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户报表交付节奏 每日客户复盘 日报导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户报表交付节奏 每周客户对账 周报计划' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户报表交付节奏 临时问题解释 复制说明' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户报表交付节奏 字段模板复用 字段配置' })).toBeInTheDocument();
  expect(screen.getByText('客户导出包模板库')).toBeInTheDocument();
  expect(screen.getByText('把一次性 CSV 下载变成客户可复用的报表模板：日报看车辆、周报做对账、临时解释处理质疑、字段模板保持口径稳定。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户导出包模板库 日报复盘包 客户日报 导出日报' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户导出包模板库 周报对账包 客户对账 复制周报计划' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户导出包模板库 问题解释包 客户问询 复制说明' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户导出包模板库 字段稳定包 数据对接 字段配置' })).toBeInTheDocument();
  expect(screen.getByText('保存报表视图')).toBeInTheDocument();
  expect(screen.getByText('把当前车辆、时间窗、来源证据、字段裁剪和交付节奏保存成客户可复用视图，后续不用重新拼查询条件。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '保存报表视图 当前筛选 VIN-FIELDS-001 保存视图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '保存报表视图 字段模板 2 字段 复用字段' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '保存报表视图 交付节奏 日报/周报 复制计划' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '保存报表视图 快速分享 可复制 复制链接' })).toBeInTheDocument();
  expect(screen.getByText('客户轨迹决策台')).toBeInTheDocument();
  expect(screen.getAllByRole('button', { name: /复制决策说明/ }).length).toBeGreaterThanOrEqual(1);
  expect(screen.getByRole('button', { name: '客户轨迹决策 明细复核 明细证据' })).toBeInTheDocument();
  expect(screen.getByText('客户查询导出包')).toBeInTheDocument();
  expect(screen.getByText('面向客户交付时，先确认车辆、时间、交付物和质量提示，再导出位置、明细证据或字段裁剪，避免把内部排查过程直接暴露给客户。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户查询导出 明细证据 导出证据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户查询导出 字段裁剪 字段配置' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户查询导出 质量提示 复制说明' })).toBeInTheDocument();
  expect(screen.getByText('客户交付物清单')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户交付清单 位置历史 导出位置' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户交付清单 轨迹回放 查看轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户交付清单 里程统计 里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户交付清单 明细证据 导出证据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户交付清单 字段裁剪 字段配置' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户交付清单 质量提示 复制清单' })).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: /复制交付封面包/ }));
  await waitFor(() => {
    expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【客户数据交付封面包】'));
  });
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('交付主题：VIN-FIELDS-001 / GB32960 / 2026-07-03 至 2026-07-04'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('交付物：轨迹报告 / 位置历史 CSV / 明细证据 CSV / 字段裁剪 CSV / 里程统计链接 / 质量提示'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('当前证据：位置 0 条 / 有效坐标 0 点 / 明细 1 帧 / 字段 2 个'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('建议动作：优先导出字段证据，并补查轨迹位置。'));
  fireEvent.click(screen.getByRole('button', { name: /复制导出向导包/ }));
  await waitFor(() => {
    expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【客户导出向导包】'));
  });
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('步骤1 锁定范围：VIN-FIELDS-001 / GB32960 / 2026-07-03 至 2026-07-04'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('步骤2 选择证据：字段证据'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('步骤3 质量复核：可交付'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('步骤4 导出交付：复制交付包'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('字段裁剪：http://localhost:3000/#/history-query?keyword=VIN-FIELDS-001&protocol=GB32960&tab=fields&dateFrom=2026-07-03&dateTo=2026-07-04&includeFields=true&fields=gb32960.vehicle.speed_kmh'));
  fireEvent.click(screen.getByRole('button', { name: /复制交付清单/ }));
  await waitFor(() => {
    expect(writeText).toHaveBeenCalledWith(expect.stringContaining('交付物：轨迹报告 / 位置历史 / 里程统计 / 明细证据 / 字段裁剪 / 质量提示'));
  });
  fireEvent.click(screen.getByRole('button', { name: '报表交付模板 周期交付计划 复制计划' }));
  await waitFor(() => {
    expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【历史数据周期交付计划】'));
  });
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('推荐频率：日报用于客户复盘，周报用于客户对账，临时导出用于问题解释。'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('交付内容：轨迹报告 / 位置历史 CSV / 明细证据 CSV / 字段裁剪 CSV / 里程统计链接 / 质量提示。'));
  fireEvent.click(screen.getByRole('button', { name: '保存报表视图 当前筛选 VIN-FIELDS-001 保存视图' }));
  await waitFor(() => {
    expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【保存报表视图】'));
  });
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('视图范围：VIN-FIELDS-001 / GB32960 / 2026-07-03 至 2026-07-04'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('字段模板：gb32960.vehicle.speed_kmh, gb32960.vehicle.soc_percent'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('复用链接：http://localhost:3000/#/history-query?keyword=VIN-FIELDS-001&protocol=GB32960&tab=fields&dateFrom=2026-07-03&dateTo=2026-07-04&includeFields=true&fields=gb32960.vehicle.speed_kmh%2Cgb32960.vehicle.soc_percent'));
  expect(await screen.findByRole('tab', { name: '字段明细' })).toHaveAttribute('aria-selected', 'true');
  expect(fetchMock).toHaveBeenCalledWith('/api/history/raw-frames/query', expect.objectContaining({
    method: 'POST',
    body: expect.stringContaining('"includeFields":true')
  }));
  expect(screen.getAllByText('gb32960.vehicle.speed_kmh').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('gb32960.vehicle.soc_percent')).toBeInTheDocument();
  expect(screen.getByText('42')).toBeInTheDocument();
  expect(screen.getByText('78.4')).toBeInTheDocument();
  expect(screen.getByText('当前页 2 个字段')).toBeInTheDocument();
});

test('updates history hash when vehicle history filters are submitted', async () => {
  window.history.replaceState(null, '', '/#/history?protocol=JT808');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input, init) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  await screen.findByRole('heading', { name: '轨迹回放' });
  fireEvent.change(screen.getByPlaceholderText('VIN / 车牌 / 手机号'), { target: { value: '粤AG18312' } });
  fireEvent.change(screen.getByPlaceholderText('2026-07-03 00:00:00'), { target: { value: '2026-07-01 00:00:00' } });
  fireEvent.change(screen.getByPlaceholderText('2026-07-03 23:59:59'), { target: { value: '2026-07-01 23:59:59' } });
  fireEvent.click(screen.getByRole('button', { name: 'search 查询' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/history?keyword=%E7%B2%A4AG18312&protocol=JT808&dateFrom=2026-07-01+00%3A00%3A00&dateTo=2026-07-01+23%3A59%3A59');
  });
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/history/locations?'), undefined);
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('dateFrom=2026-07-01+00%3A00%3A00'), undefined);
  expect(fetchMock).toHaveBeenCalledWith('/api/history/raw-frames/query', expect.objectContaining({
    method: 'POST',
    body: expect.stringContaining('"dateFrom":"2026-07-01 00:00:00"')
  }));
});

test('prevents unscoped raw parsed-field query from history form', async () => {
  window.history.replaceState(null, '', '/#/history?keyword=VIN-SAFE-RAW');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  await screen.findByRole('heading', { name: '轨迹回放' });
  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith('/api/history/raw-frames/query', expect.objectContaining({
      body: expect.stringContaining('VIN-SAFE-RAW')
    }));
  });
  fetchMock.mockClear();

  const keywordInput = screen.getByPlaceholderText('VIN / 车牌 / 手机号');
  fireEvent.change(keywordInput, { target: { value: '' } });
  fireEvent.input(keywordInput, { target: { value: '' } });
  await waitFor(() => {
    expect(keywordInput).toHaveValue('');
  });
  fireEvent.change(screen.getByPlaceholderText('2026-07-03 00:00:00'), { target: { value: '' } });
  fireEvent.change(screen.getByPlaceholderText('2026-07-03 23:59:59'), { target: { value: '' } });
  fireEvent.click(screen.getByRole('checkbox', { name: '返回字段明细' }));
  fireEvent.click(screen.getByRole('button', { name: 'search 查询' }));

  expect((await screen.findAllByText('字段明细查询需要车辆、时间范围或字段裁剪')).length).toBeGreaterThanOrEqual(1);
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/history/locations?'), undefined);
  const unscopedRawQueries = fetchMock.mock.calls.filter(([url, init]) => {
    if (url !== '/api/history/raw-frames/query') return false;
    const body = JSON.parse(String((init as RequestInit | undefined)?.body ?? '{}')) as Record<string, unknown>;
    return !body.keyword && !body.dateFrom && !body.dateTo && !body.fields;
  });
  expect(unscopedRawQueries).toHaveLength(0);
});

test('shows trajectory playback workspace from history locations', async () => {
  window.history.replaceState(null, '', '/#/history?keyword=VIN-TRACK-001&protocol=JT808');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input, init) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/locations')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [
              { vin: 'VIN-TRACK-001', plate: '粤A轨迹1', protocol: 'JT808', longitude: 113.2, latitude: 23.1, speedKmh: 10, socPercent: 0, totalMileageKm: 100, lastSeen: '2026-07-03 10:00:00', deviceTime: '2026-07-03 10:00:00', serverTime: '2026-07-03 10:00:01' },
              { vin: 'VIN-TRACK-001', plate: '粤A轨迹1', protocol: 'JT808', longitude: 113.3, latitude: 23.2, speedKmh: 42, socPercent: 0, totalMileageKm: 112.4, lastSeen: '2026-07-03 10:10:00', deviceTime: '2026-07-03 10:10:00', serverTime: '2026-07-03 10:10:01' }
            ],
            total: 2,
            limit: 10,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/raw-frames/query')) {
      expect(init?.method).toBe('POST');
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByRole('heading', { name: '轨迹回放' })).toBeInTheDocument();
  expect(screen.getByText('历史查询导出任务板')).toBeInTheDocument();
  expect(screen.getByText('2 个有效点')).toBeInTheDocument();
  expect(screen.getByText('围绕当前车辆生成可播放轨迹，用于定位复盘和客户问询。')).toBeInTheDocument();
  expect(screen.getByText('位置导出')).toBeInTheDocument();
  expect(screen.getAllByText('历史明细').length).toBeGreaterThan(0);
  expect(screen.getAllByText('字段裁剪').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('客户轨迹影响')).toBeInTheDocument();
  expect(screen.getByText('轨迹范围')).toBeInTheDocument();
  expect(screen.getAllByText('定位覆盖').length).toBeGreaterThan(0);
  expect(screen.getByText('异常影响')).toBeInTheDocument();
  expect(screen.getAllByText('历史明细').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('地图能力')).toBeInTheDocument();
  expect(screen.getByText('轨迹回放作业台')).toBeInTheDocument();
  expect(screen.getByText('2 个有效轨迹点')).toBeInTheDocument();
  expect(screen.getByText('区间里程')).toBeInTheDocument();
  expect(screen.getAllByText('12.4 km').length).toBeGreaterThan(0);
  expect(screen.getAllByText('最高速度').length).toBeGreaterThan(0);
  expect(screen.getAllByText('42 km/h').length).toBeGreaterThan(0);
  expect(screen.getAllByText('起点').length).toBeGreaterThan(0);
  expect(screen.getAllByText('终点').length).toBeGreaterThan(0);
});

test('shows trajectory evidence quality on history playback workspace', async () => {
  window.history.replaceState(null, '', '/#/history?keyword=VIN-TRACK-QUALITY&protocol=JT808');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input, init) => {
    const path = String(input);
    if (path.includes('/api/history/locations')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [
              { vin: 'VIN-TRACK-QUALITY', plate: '粤A轨迹质量', protocol: 'JT808', longitude: 113.2, latitude: 23.1, speedKmh: 10, socPercent: 0, totalMileageKm: 100, lastSeen: '2026-07-03 10:00:00', deviceTime: '2026-07-03 10:00:00', serverTime: '2026-07-03 10:00:01' },
              { vin: 'VIN-TRACK-QUALITY', plate: '粤A轨迹质量', protocol: 'JT808', longitude: 113.3, latitude: 23.2, speedKmh: 42, socPercent: 0, totalMileageKm: 112.4, lastSeen: '2026-07-03 10:10:00', deviceTime: '2026-07-03 10:10:00', serverTime: '2026-07-03 10:10:01' }
            ],
            total: 2,
            limit: 10,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/raw-frames/query')) {
      expect(init?.method).toBe('POST');
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('轨迹复盘质量')).toBeInTheDocument();
  expect(screen.getByText('定位覆盖率')).toBeInTheDocument();
  expect(screen.getAllByText('100%').length).toBeGreaterThan(0);
  expect(screen.getAllByText('回放跨度').length).toBeGreaterThan(0);
  expect(screen.getAllByText('10 分钟').length).toBeGreaterThan(0);
  expect(screen.getByText('采样间隔')).toBeInTheDocument();
  expect(screen.getByText('10 分钟/点')).toBeInTheDocument();
});

test('flags trajectory playback anomalies for gap mileage and speed review', async () => {
  window.history.replaceState(null, '', '/#/history?keyword=VIN-TRACK-ANOMALY&protocol=JT808');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input, init) => {
    const path = String(input);
    if (path.includes('/api/history/locations')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [
              { vin: 'VIN-TRACK-ANOMALY', plate: '粤A轨迹异', protocol: 'JT808', longitude: 113.2, latitude: 23.1, speedKmh: 18, socPercent: 0, totalMileageKm: 300, lastSeen: '2026-07-03 10:00:00', deviceTime: '2026-07-03 10:00:00', serverTime: '2026-07-03 10:00:01' },
              { vin: 'VIN-TRACK-ANOMALY', plate: '粤A轨迹异', protocol: 'JT808', longitude: 113.3, latitude: 23.2, speedKmh: 132, socPercent: 0, totalMileageKm: 318, lastSeen: '2026-07-03 10:40:00', deviceTime: '2026-07-03 10:40:00', serverTime: '2026-07-03 10:40:01' },
              { vin: 'VIN-TRACK-ANOMALY', plate: '粤A轨迹异', protocol: 'JT808', longitude: 113.4, latitude: 23.3, speedKmh: 22, socPercent: 0, totalMileageKm: 316, lastSeen: '2026-07-03 10:45:00', deviceTime: '2026-07-03 10:45:00', serverTime: '2026-07-03 10:45:01' }
            ],
            total: 3,
            limit: 10,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/raw-frames/query')) {
      expect(init?.method).toBe('POST');
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('轨迹异常判读')).toBeInTheDocument();
  expect(screen.getByText('数据断点')).toBeInTheDocument();
  expect(screen.getByText('1 处')).toBeInTheDocument();
  expect(screen.getByText('里程回退')).toBeInTheDocument();
  expect(screen.getByText('2 km')).toBeInTheDocument();
  expect(screen.getByText('速度异常')).toBeInTheDocument();
  expect(screen.getAllByText('132 km/h').length).toBeGreaterThan(0);
  expect(screen.getByText('建议核对明细证据和当日里程统计')).toBeInTheDocument();
});

test('opens amap route from trajectory playback workspace', async () => {
  window.history.replaceState(null, '', '/#/history?keyword=VIN-TRACK-AMAP&protocol=JT808');
  const openSpy = vi.spyOn(window, 'open').mockImplementation(() => null);
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input, init) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/locations')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [
              { vin: 'VIN-TRACK-AMAP', plate: '粤A高德1', protocol: 'JT808', longitude: 113.2, latitude: 23.1, speedKmh: 10, socPercent: 0, totalMileageKm: 100, lastSeen: '2026-07-03 10:00:00', deviceTime: '2026-07-03 10:00:00', serverTime: '2026-07-03 10:00:01' },
              { vin: 'VIN-TRACK-AMAP', plate: '粤A高德1', protocol: 'JT808', longitude: 113.3, latitude: 23.2, speedKmh: 42, socPercent: 0, totalMileageKm: 112.4, lastSeen: '2026-07-03 10:10:00', deviceTime: '2026-07-03 10:10:00', serverTime: '2026-07-03 10:10:01' }
            ],
            total: 2,
            limit: 10,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/raw-frames/query')) {
      expect(init?.method).toBe('POST');
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByRole('heading', { name: '轨迹回放' })).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '高德线路' }));

  expect(openSpy).toHaveBeenCalledWith(
    expect.stringContaining('https://uri.amap.com/navigation?from=113.2,23.1'),
    '_blank',
    'noopener,noreferrer'
  );
  expect(openSpy).toHaveBeenCalledWith(
    expect.stringContaining('to=113.3,23.2'),
    '_blank',
    'noopener,noreferrer'
  );
});

test('opens amap marker when trajectory has one valid point', async () => {
  window.history.replaceState(null, '', '/#/history?keyword=VIN-TRACK-AMAP-ONE&protocol=JT808');
  const openSpy = vi.spyOn(window, 'open').mockImplementation(() => null);
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input, init) => {
    const path = String(input);
    if (path.includes('/api/history/locations')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [
              { vin: 'VIN-TRACK-AMAP-ONE', plate: '粤A单点1', protocol: 'JT808', longitude: 113.28, latitude: 23.18, speedKmh: 10, socPercent: 0, totalMileageKm: 100, lastSeen: '2026-07-03 10:00:00', deviceTime: '2026-07-03 10:00:00', serverTime: '2026-07-03 10:00:01' }
            ],
            total: 1,
            limit: 10,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/raw-frames/query')) {
      expect(init?.method).toBe('POST');
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByRole('heading', { name: '轨迹回放' })).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '高德线路' }));

  expect(openSpy).toHaveBeenCalledWith(
    expect.stringContaining('https://uri.amap.com/marker?position=113.28,23.18'),
    '_blank',
    'noopener,noreferrer'
  );
});

test('resolves current trajectory point address through server-side amap api', async () => {
  window.history.replaceState(null, '', '/#/history?keyword=VIN-TRACK-ADDRESS&protocol=JT808');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input, init) => {
    const path = String(input);
    if (path.includes('/api/history/locations')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [
              { vin: 'VIN-TRACK-ADDRESS', plate: '粤A地址1', protocol: 'JT808', longitude: 113.2, latitude: 23.1, speedKmh: 10, socPercent: 0, totalMileageKm: 100, lastSeen: '2026-07-03 10:00:00', deviceTime: '2026-07-03 10:00:00', serverTime: '2026-07-03 10:00:01' },
              { vin: 'VIN-TRACK-ADDRESS', plate: '粤A地址1', protocol: 'JT808', longitude: 113.3, latitude: 23.2, speedKmh: 42, socPercent: 0, totalMileageKm: 112.4, lastSeen: '2026-07-03 10:10:00', deviceTime: '2026-07-03 10:10:00', serverTime: '2026-07-03 10:10:01' }
            ],
            total: 2,
            limit: 10,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/map/reverse-geocode')) {
      expect(path).toContain('longitude=113.2');
      expect(path).toContain('latitude=23.1');
      return {
        ok: true,
        json: async () => ({
          data: {
            provider: 'AMap',
            longitude: 113.2,
            latitude: 23.1,
            formattedAddress: '广东省广州市天河区测试路',
            province: '广东省',
            city: '广州市',
            district: '天河区',
            adcode: '440106'
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/raw-frames/query')) {
      expect(init?.method).toBe('POST');
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('当前回放点')).toBeInTheDocument();
  expect(screen.getByText('地址未解析')).toBeInTheDocument();
  fireEvent.click(screen.getAllByRole('button', { name: '解析地址' })[0]);

  await waitFor(() => {
    expect(screen.getAllByText('广东省广州市天河区测试路').length).toBeGreaterThan(0);
  });
  expect(screen.getByText('AMap')).toBeInTheDocument();
  fireEvent.click(screen.getByText('复制轨迹复盘包'));

  await waitFor(() => {
    expect(writeText).toHaveBeenCalledWith(expect.stringContaining('当前点地址：广东省广州市天河区测试路'));
  });
});

test('controls trajectory playback current point from history locations', async () => {
  window.history.replaceState(null, '', '/#/history?keyword=VIN-TRACK-CONTROL&protocol=JT808');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input, init) => {
    const path = String(input);
    if (path.includes('/api/history/locations')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [
              { vin: 'VIN-TRACK-CONTROL', plate: '粤A回放1', protocol: 'JT808', longitude: 113.2, latitude: 23.1, speedKmh: 10, socPercent: 0, totalMileageKm: 100, lastSeen: '2026-07-03 10:00:00', deviceTime: '2026-07-03 10:00:00', serverTime: '2026-07-03 10:00:01' },
              { vin: 'VIN-TRACK-CONTROL', plate: '粤A回放1', protocol: 'JT808', longitude: 113.3, latitude: 23.2, speedKmh: 42, socPercent: 0, totalMileageKm: 112.4, lastSeen: '2026-07-03 10:10:00', deviceTime: '2026-07-03 10:10:00', serverTime: '2026-07-03 10:10:01' },
              { vin: 'VIN-TRACK-CONTROL', plate: '粤A回放1', protocol: 'JT808', longitude: 113.4, latitude: 23.3, speedKmh: 28, socPercent: 0, totalMileageKm: 119.9, lastSeen: '2026-07-03 10:20:00', deviceTime: '2026-07-03 10:20:00', serverTime: '2026-07-03 10:20:01' }
            ],
            total: 3,
            limit: 10,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/raw-frames/query')) {
      expect(init?.method).toBe('POST');
    }
    if (path.includes('/api/vehicle-service/overview')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN-TRACK-CONTROL',
            plate: '粤A回放1',
            sourceCount: 1,
            onlineSourceCount: 1,
            coverageStatus: 'healthy',
            primaryProtocol: 'JT808',
            lastSeen: '2026-07-03 10:20:00',
            historyCount: 3,
            rawCount: 0,
            mileageCount: 1,
            qualityIssueCount: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('当前回放点')).toBeInTheDocument();
  expect(screen.getByText('点 1 / 3')).toBeInTheDocument();
  expect(screen.getAllByText('2026-07-03 10:00:00').length).toBeGreaterThan(0);
  fireEvent.click(screen.getAllByRole('button', { name: '下一点' })[0]);
  expect(screen.getByText('点 2 / 3')).toBeInTheDocument();
  expect(screen.getAllByText('2026-07-03 10:10:00').length).toBeGreaterThan(0);
  fireEvent.click(screen.getByRole('button', { name: '选择轨迹点 3' }));
  expect(screen.getByText('点 3 / 3')).toBeInTheDocument();
  fireEvent.change(screen.getByRole('slider', { name: '轨迹回放进度' }), { target: { value: '0' } });
  expect(screen.getByText('点 1 / 3')).toBeInTheDocument();
  const mapPointButtons = screen.getAllByRole('button', { name: '选择地图车辆 粤A回放1' });
  fireEvent.click(mapPointButtons[0]);
  expect(screen.getByText('点 1 / 3')).toBeInTheDocument();
  fireEvent.click(mapPointButtons[1]);
  expect(screen.getByText('点 2 / 3')).toBeInTheDocument();
  fireEvent.click(screen.getAllByRole('button', { name: '上一点' })[0]);
  expect(screen.getByText('点 1 / 3')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '回放点车辆服务' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/detail?keyword=VIN-TRACK-CONTROL&protocol=JT808');
  });
});

test('auto plays trajectory playback points with selectable speed', async () => {
  window.history.replaceState(null, '', '/#/history?keyword=VIN-TRACK-AUTO&protocol=JT808');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/history/locations')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [
              { vin: 'VIN-TRACK-AUTO', plate: '粤A自动1', protocol: 'JT808', longitude: 113.2, latitude: 23.1, speedKmh: 10, socPercent: 0, totalMileageKm: 100, lastSeen: '2026-07-03 10:00:00', deviceTime: '2026-07-03 10:00:00', serverTime: '2026-07-03 10:00:01' },
              { vin: 'VIN-TRACK-AUTO', plate: '粤A自动1', protocol: 'JT808', longitude: 113.3, latitude: 23.2, speedKmh: 42, socPercent: 0, totalMileageKm: 112.4, lastSeen: '2026-07-03 10:10:00', deviceTime: '2026-07-03 10:10:00', serverTime: '2026-07-03 10:10:01' },
              { vin: 'VIN-TRACK-AUTO', plate: '粤A自动1', protocol: 'JT808', longitude: 113.4, latitude: 23.3, speedKmh: 28, socPercent: 0, totalMileageKm: 119.9, lastSeen: '2026-07-03 10:20:00', deviceTime: '2026-07-03 10:20:00', serverTime: '2026-07-03 10:20:01' }
            ],
            total: 3,
            limit: 10,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('播放速度')).toBeInTheDocument();
  vi.useFakeTimers();
  expect(screen.getByText('点 1 / 3')).toBeInTheDocument();
  const playTrajectoryButtons = screen.getAllByRole('button', { name: '播放轨迹' });
  fireEvent.click(playTrajectoryButtons[playTrajectoryButtons.length - 1]);
  expect(screen.getByRole('button', { name: '暂停轨迹' })).toBeInTheDocument();

  await vi.advanceTimersByTimeAsync(1200);
  expect(screen.getByText('点 2 / 3')).toBeInTheDocument();

  await vi.advanceTimersByTimeAsync(2400);
  expect(screen.getByText('点 3 / 3')).toBeInTheDocument();
  expect(screen.getAllByRole('button', { name: '播放轨迹' }).length).toBeGreaterThan(0);

  vi.useRealTimers();
});

test('exports current history location page as csv', async () => {
  window.history.replaceState(null, '', '/#/history?keyword=VIN-EXPORT-LOC&protocol=JT808');
  const createObjectURL = vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:history-location');
  const revokeObjectURL = vi.spyOn(URL, 'revokeObjectURL').mockImplementation(() => undefined);
  vi.spyOn(HTMLAnchorElement.prototype, 'click').mockImplementation(() => undefined);
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input, init) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/locations')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-EXPORT-LOC',
              plate: '粤A导出1',
              protocol: 'JT808',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 30,
              socPercent: 0,
              totalMileageKm: 100,
              lastSeen: '2026-07-03 20:12:10',
              deviceTime: '2026-07-03 20:12:10',
              serverTime: '2026-07-03 20:12:11'
            }],
            total: 1,
            limit: 10,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/raw-frames/query')) {
      expect(init?.method).toBe('POST');
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect((await screen.findAllByText('VIN-EXPORT-LOC')).length).toBeGreaterThan(0);
  fireEvent.click(screen.getByRole('button', { name: '导出位置当前页 CSV' }));

  expect(createObjectURL).toHaveBeenCalled();
  expect(revokeObjectURL).toHaveBeenCalledWith('blob:history-location');
});

test('exports current raw frame page as csv with parsed fields', async () => {
  window.history.replaceState(null, '', '/#/history-query?keyword=VIN-EXPORT-RAW&protocol=GB32960&includeFields=true&tab=raw');
  const createObjectURL = vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:history-raw');
  const revokeObjectURL = vi.spyOn(URL, 'revokeObjectURL').mockImplementation(() => undefined);
  vi.spyOn(HTMLAnchorElement.prototype, 'click').mockImplementation(() => undefined);
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/locations')) {
      return {
        ok: true,
        json: async () => ({
          data: { items: [], total: 0, limit: 10, offset: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/raw-frames/query')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              id: 'raw-export-001',
              vin: 'VIN-EXPORT-RAW',
              plate: '粤A导出2',
              protocol: 'GB32960',
              frameType: 'REALTIME',
              deviceTime: '2026-07-03 20:12:10',
              serverTime: '2026-07-03 20:12:11',
              rawSizeBytes: 256,
              parsedFields: { 'gb32960.vehicle.speed_kmh': 42 }
            }],
            total: 1,
            limit: 10,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect((await screen.findAllByText('raw-export-001')).length).toBeGreaterThanOrEqual(1);
  fireEvent.click(screen.getByRole('button', { name: '导出历史明细当前页 CSV' }));

  expect(createObjectURL).toHaveBeenCalled();
  expect(revokeObjectURL).toHaveBeenCalledWith('blob:history-raw');
});

test('shows vehicle and source scope on mileage hash', async () => {
  window.history.replaceState(null, '', '/#/mileage?keyword=VIN-MILEAGE-001&protocol=GB32960');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { vehicleCount: 1, recordCount: 2, sourceCount: 1, totalMileageKm: 12.3, averageMileagePerVin: 12.3 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('当前车辆：VIN-MILEAGE-001')).toBeInTheDocument();
  expect(screen.getAllByText('数据通道：GB32960').length).toBeGreaterThan(0);
  expect(screen.getByText('里程对账结论')).toBeInTheDocument();
  expect(screen.getByText('先回答客户这段时间跑了多少、闭合是否通过、异常是否需要复核，再进入轨迹、历史和导出。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程对账结论 区间总里程 12.3 km 复制摘要' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程对账结论 闭合状态 分页抽样 发布审计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程对账结论 异常记录 0 条 复制决策' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程对账结论 下一步 复核后交付 查看证据' })).toBeInTheDocument();
  expect(screen.getByText('客户里程问题')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程问题 这段时间总共跑了多少？ 复制摘要' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程问题 这份里程能给客户或 BI 吗？ 发布审计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程问题 哪天或哪辆车有异常？ 复制决策' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程问题 这段路能回放轨迹吗？ 轨迹复核' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程问题 明细数据能导出吗？ 导出明细' })).toBeInTheDocument();
});

test('applies mileage date range from shareable hash to API requests', async () => {
  window.history.replaceState(null, '', '/#/mileage?keyword=VIN-MILEAGE-002&protocol=JT808&dateFrom=2026-07-01&dateTo=2026-07-03');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { vehicleCount: 1, recordCount: 2, sourceCount: 1, totalMileageKm: 12.3, averageMileagePerVin: 12.3 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/mileage/summary?'), undefined);
  });
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('dateFrom=2026-07-01'), undefined);
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('dateTo=2026-07-03'), undefined);
  expect(screen.getByText('当前车辆：VIN-MILEAGE-002')).toBeInTheDocument();
  expect(screen.getAllByText('数据通道：JT808').length).toBeGreaterThan(0);
});

test('shows and clears current mileage filters while keeping vehicle scope', async () => {
  window.history.replaceState(null, '', '/#/mileage?keyword=VIN-MILEAGE-002&protocol=JT808&dateFrom=2026-07-01&dateTo=2026-07-03');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { vehicleCount: 1, recordCount: 2, sourceCount: 1, totalMileageKm: 12.3, averageMileagePerVin: 12.3 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('当前里程筛选')).toBeInTheDocument();
  expect(screen.getByText('车辆：VIN-MILEAGE-002')).toBeInTheDocument();
  expect(screen.getAllByText('数据通道：JT808').length).toBeGreaterThan(0);
  expect(screen.getByText('开始日期：2026-07-01')).toBeInTheDocument();
  expect(screen.getByText('结束日期：2026-07-03')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '清空筛选' }));

  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/mileage/summary?keyword=VIN-MILEAGE-002'), undefined);
  });
  expect(window.location.hash).toBe('#/mileage?keyword=VIN-MILEAGE-002');
});

test('updates mileage hash when mileage filters are submitted', async () => {
  window.history.replaceState(null, '', '/#/mileage');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { vehicleCount: 1, recordCount: 2, sourceCount: 1, totalMileageKm: 12.3, averageMileagePerVin: 12.3 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  await screen.findByRole('heading', { name: '里程统计' });
  fireEvent.change(screen.getByPlaceholderText('VIN / 车牌 / 手机号 / OEM'), { target: { value: '粤AG18312' } });
  fireEvent.click(screen.getByText('全部数据通道'));
  fireEvent.click(await screen.findByText('JT808'));
  fireEvent.change(screen.getByPlaceholderText('2026-07-01'), { target: { value: '2026-07-01' } });
  fireEvent.change(screen.getByPlaceholderText('2026-07-03'), { target: { value: '2026-07-03' } });
  fireEvent.click(screen.getByRole('button', { name: '查询' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/mileage?keyword=%E7%B2%A4AG18312&protocol=JT808&dateFrom=2026-07-01&dateTo=2026-07-03');
  });
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/mileage/summary?'), undefined);
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('dateFrom=2026-07-01'), undefined);
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('dateTo=2026-07-03'), undefined);
});

test('opens current vehicle service from mileage header with current source evidence', async () => {
  window.history.replaceState(null, '', '/#/mileage?keyword=VIN-MILEAGE-HEADER&protocol=GB32960');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { vehicleCount: 1, recordCount: 0, sourceCount: 1, totalMileageKm: 0, averageMileagePerVin: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/resolve')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            lookupKey: 'VIN-MILEAGE-HEADER',
            resolved: true,
            vin: 'VIN-MILEAGE-HEADER',
            plate: '',
            phone: '',
            oem: '',
            protocols: ['GB32960'],
            online: true,
            lastSeen: '2026-07-03 20:12:10'
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect((await screen.findAllByText('数据通道：GB32960')).length).toBeGreaterThan(0);
  fireEvent.click(screen.getByRole('button', { name: '当前车辆服务' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/detail?keyword=VIN-MILEAGE-HEADER&protocol=GB32960');
  });
});

test('opens vehicle service from mileage row with row source evidence', async () => {
  window.history.replaceState(null, '', '/#/mileage?keyword=VIN-MILEAGE-ROW&protocol=JT808');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { vehicleCount: 1, recordCount: 1, sourceCount: 1, totalMileageKm: 12.3, averageMileagePerVin: 12.3 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/daily')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-MILEAGE-ROW',
              plate: '粤AG18312',
              source: 'JT808',
              date: '2026-07-03',
              startMileageKm: 100,
              endMileageKm: 112.3,
              dailyMileageKm: 12.3,
              anomalySeverity: ''
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/resolve')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            lookupKey: 'VIN-MILEAGE-ROW',
            resolved: true,
            vin: 'VIN-MILEAGE-ROW',
            plate: '粤AG18312',
            phone: '',
            oem: 'G7s',
            protocols: ['JT808'],
            online: true,
            lastSeen: '2026-07-03 20:12:10'
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect((await screen.findAllByText('VIN-MILEAGE-ROW')).length).toBeGreaterThan(0);
  const serviceButtons = screen.getAllByRole('button', { name: '车辆服务' });
  fireEvent.click(serviceButtons[serviceButtons.length - 1]);

  await waitFor(() => {
    expect(window.location.hash).toBe('#/detail?keyword=VIN-MILEAGE-ROW&protocol=JT808');
  });
});

test('opens same-day trajectory playback from mileage row for evidence review', async () => {
  window.history.replaceState(null, '', '/#/mileage?keyword=VIN-MILEAGE-TRACK&protocol=JT808');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { vehicleCount: 1, recordCount: 1, sourceCount: 1, totalMileageKm: 18.8, averageMileagePerVin: 18.8 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/daily')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-MILEAGE-TRACK',
              plate: '粤A轨迹核',
              source: 'JT808',
              date: '2026-07-03',
              startMileageKm: 200,
              endMileageKm: 218.8,
              dailyMileageKm: 18.8,
              anomalySeverity: 'warning'
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect((await screen.findAllByText('VIN-MILEAGE-TRACK')).length).toBeGreaterThan(0);
  fireEvent.click(screen.getByRole('button', { name: '核对轨迹' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/history?keyword=VIN-MILEAGE-TRACK&protocol=JT808&dateFrom=2026-07-03&dateTo=2026-07-04');
  });
});

test('opens same-day raw frame evidence from mileage row', async () => {
  window.history.replaceState(null, '', '/#/mileage?keyword=VIN-MILEAGE-RAW&protocol=JT808');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { vehicleCount: 1, recordCount: 1, sourceCount: 1, totalMileageKm: 18.8, averageMileagePerVin: 18.8 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/daily')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-MILEAGE-RAW',
              plate: '粤ARAW核',
              source: 'JT808',
              date: '2026-07-03',
              startMileageKm: 200,
              endMileageKm: 218.8,
              dailyMileageKm: 18.8,
              anomalySeverity: 'warning'
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/locations') || path.includes('/api/history/raw-frames')) {
      return {
        ok: true,
        json: async () => ({
          data: { items: [], total: 0, limit: 20, offset: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect((await screen.findAllByText('VIN-MILEAGE-RAW')).length).toBeGreaterThan(0);
  fireEvent.click(screen.getByRole('button', { name: '核对原始' }));

  await waitFor(() => {
    expect(window.location.hash.startsWith('#/history-query?')).toBe(true);
  });
  const params = new URLSearchParams(window.location.hash.slice('#/history-query?'.length));
  expect(params.get('keyword')).toBe('VIN-MILEAGE-RAW');
  expect(params.get('protocol')).toBe('JT808');
  expect(params.get('dateFrom')).toBe('2026-07-03');
  expect(params.get('dateTo')).toBe('2026-07-04');
  expect(params.get('tab')).toBe('raw');
  expect(await screen.findByRole('heading', { name: '历史查询导出' })).toBeInTheDocument();
});

test('shows mileage anomaly action guidance', async () => {
  window.history.replaceState(null, '', '/#/mileage?keyword=VIN-MILEAGE-ANOMALY&protocol=GB32960');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { vehicleCount: 1, recordCount: 1, sourceCount: 1, totalMileageKm: -3.2, averageMileagePerVin: -3.2 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/daily')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-MILEAGE-ANOMALY',
              plate: '粤A异常1',
              source: 'GB32960',
              date: '2026-07-03',
              startMileageKm: 100,
              endMileageKm: 96.8,
              dailyMileageKm: -3.2,
              anomalySeverity: 'warning'
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect((await screen.findAllByText('VIN-MILEAGE-ANOMALY')).length).toBeGreaterThan(0);
  expect(screen.getByText('核对里程来源')).toBeInTheDocument();
  expect(screen.getByText('日里程异常，优先核对当天首末总里程、来源断链和补传数据。')).toBeInTheDocument();
  fireEvent.click(screen.getAllByRole('button', { name: '复制证据' })[0]);

  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【里程异常复核包】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆：粤A异常1 / VIN-MILEAGE-ANOMALY'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('数据通道：GB32960'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('统计日期：2026-07-03'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('日里程：-3.2 km'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('异常等级：warning'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务：http://localhost:3000/#/detail?keyword=VIN-MILEAGE-ANOMALY&protocol=GB32960'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹证据：http://localhost:3000/#/history?keyword=VIN-MILEAGE-ANOMALY&protocol=GB32960&dateFrom=2026-07-03&dateTo=2026-07-04'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('历史明细：http://localhost:3000/#/history-query?keyword=VIN-MILEAGE-ANOMALY&protocol=GB32960&tab=raw&dateFrom=2026-07-03&dateTo=2026-07-04&includeFields=true'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('里程统计：http://localhost:3000/#/mileage?keyword=VIN-MILEAGE-ANOMALY&protocol=GB32960&dateFrom=2026-07-03&dateTo=2026-07-04'));
});

test('shows mileage statistics workspace with trend source and definition', async () => {
  window.history.replaceState(null, '', '/#/mileage?keyword=VIN-MILEAGE-STATS');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { vehicleCount: 2, recordCount: 4, sourceCount: 2, totalMileageKm: 140, averageMileagePerVin: 70 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/daily')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [
              { vin: 'VIN-MILEAGE-STATS', plate: '粤A统计1', source: 'JT808', date: '2026-07-01', startMileageKm: 100, endMileageKm: 140, dailyMileageKm: 40, anomalySeverity: '' },
              { vin: 'VIN-MILEAGE-STATS', plate: '粤A统计1', source: 'GB32960', date: '2026-07-01', startMileageKm: 200, endMileageKm: 230, dailyMileageKm: 30, anomalySeverity: '' },
              { vin: 'VIN-MILEAGE-STATS-2', plate: '粤A统计2', source: 'JT808', date: '2026-07-02', startMileageKm: 300, endMileageKm: 360, dailyMileageKm: 60, anomalySeverity: 'warning' },
              { vin: 'VIN-MILEAGE-STATS-2', plate: '粤A统计2', source: 'GB32960', date: '2026-07-02', startMileageKm: 400, endMileageKm: 410, dailyMileageKm: 10, anomalySeverity: '' }
            ],
            total: 4,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('区间趋势')).toBeInTheDocument();
  expect(screen.getByText('客户里程交付台')).toBeInTheDocument();
  expect(screen.getByText('按自定义时间范围交付里程、闭合校验、异常复核和导出证据。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程交付台 交付状态 复核后交付 复制交付' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程交付台 区间里程 140 km 复制摘要' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程交付台 闭合校验 闭合通过 发布审计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程交付台 异常复核 1 条 轨迹复核' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程交付台 数据导出 4 条明细 导出明细' })).toBeInTheDocument();
  expect(screen.getAllByText('来源贡献').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('2026-07-01').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('2026-07-02').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('JT808').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('GB32960').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('当前页里程')).toBeInTheDocument();
  expect(screen.getAllByText('140 km').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('统计闭合工作台')).toBeInTheDocument();
  expect(screen.getByText('汇总总里程')).toBeInTheDocument();
  expect(screen.getAllByText('明细合计').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('闭合差值').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('记录覆盖率')).toBeInTheDocument();
  expect(screen.getAllByText('闭合通过').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('当前筛选范围汇总总里程与明细合计一致，可作为统计证据。').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByRole('button', { name: '复制闭合摘要' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '闭合复核轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '闭合复核原始' })).toBeInTheDocument();
  expect(screen.getByText('统计发布审计')).toBeInTheDocument();
  expect(screen.getAllByText('复核后发布').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('区间闭合').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('记录覆盖').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('在线证据')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '复制统计发布审计' })).toBeInTheDocument();
  expect(screen.getAllByText('异常记录').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('1').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('最大单日')).toBeInTheDocument();
  expect(screen.getAllByText('60 km').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('统计可信度')).toBeInTheDocument();
  expect(screen.getByText('异常率')).toBeInTheDocument();
  expect(screen.getByText('25%')).toBeInTheDocument();
  expect(screen.getByText('最大单日占比')).toBeInTheDocument();
  expect(screen.getByText('42.9%')).toBeInTheDocument();
  expect(screen.getByText('分页覆盖率')).toBeInTheDocument();
  expect(screen.getAllByText('100%').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('闭合校验').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('0 km').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('里程口径').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText((content) => content.includes('区间总值应等于各日里程之和'))).toBeInTheDocument();
});

test('shows vehicle-first statistics domains for one vehicle service', async () => {
  window.history.replaceState(null, '', '/#/mileage?keyword=VIN-STATS-SERVICE&protocol=JT808');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/mileage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { vehicleCount: 3, recordCount: 6, sourceCount: 2, totalMileageKm: 210, averageMileagePerVin: 70 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/daily')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [
              { vin: 'VIN-STATS-SERVICE', plate: '粤A统计服务1', source: 'JT808', date: '2026-07-01', startMileageKm: 100, endMileageKm: 150, dailyMileageKm: 50, anomalySeverity: '' },
              { vin: 'VIN-STATS-SERVICE', plate: '粤A统计服务1', source: 'GB32960', date: '2026-07-01', startMileageKm: 200, endMileageKm: 240, dailyMileageKm: 40, anomalySeverity: '' },
              { vin: 'VIN-STATS-SERVICE-2', plate: '粤A统计服务2', source: 'JT808', date: '2026-07-02', startMileageKm: 300, endMileageKm: 380, dailyMileageKm: 80, anomalySeverity: 'warning' }
            ],
            total: 3,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/statistics/online-summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vehicleCount: 4,
            onlineVehicleCount: 3,
            offlineVehicleCount: 1,
            onlineRatePercent: 75,
            redisOnlineKeys: 92,
            protocolStats: [
              { protocol: 'JT808', online: 2, total: 3 },
              { protocol: 'GB32960', online: 1, total: 2 }
            ],
            evidence: '由车辆服务覆盖汇总、实时来源状态和 Redis 在线 key 计算'
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/statistics/online-vehicles')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [
              {
                vin: 'VIN-STATS-SERVICE',
                plate: '粤A统计服务1',
                phone: '13307795425',
                oem: 'G7s',
                protocols: ['JT808', 'GB32960'],
                online: true,
                lastSeen: '2026-07-03 20:12:10',
                offlineDurationMinutes: 0,
                sourceCount: 2,
                onlineSourceCount: 2,
                bindingStatus: 'bound'
              },
              {
                vin: 'VIN-STATS-SERVICE-OFFLINE',
                plate: '粤A离线1',
                phone: '13307795426',
                oem: '广安车联',
                protocols: ['JT808'],
                online: false,
                lastSeen: '2026-07-03 18:12:10',
                offlineDurationMinutes: 120,
                sourceCount: 1,
                onlineSourceCount: 0,
                bindingStatus: 'bound'
              }
            ],
            total: 2,
            limit: 10,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('客户里程统计')).toBeInTheDocument();
  expect(screen.getByText('客户里程对账总控')).toBeInTheDocument();
  expect(screen.getByText('把区间里程、每日闭合、轨迹证明、异常通知和报表导出收敛成客户可验收的对账链路。')).toBeInTheDocument();
  expect(screen.getByText('里程报表交付旅程')).toBeInTheDocument();
  expect(screen.getByText('按客户报表习惯组织：先确认报表周期和车辆范围，再检查区间闭合、轨迹证明，最后导出或暂缓发布。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程报表交付旅程 1 报表周期 全部日期' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程报表交付旅程 2 区间闭合 分页抽样' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程报表交付旅程 3 轨迹证明 粤A统计服务2' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程报表交付旅程 4 导出发布 复核后交付' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: /复制报表旅程包/ })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程对账总控 区间里程 210 km 复制摘要' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程对账总控 每日闭合 分页抽样 闭合审计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程对账总控 轨迹证明 2026-07-02 轨迹复核' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程对账总控 报表导出 3 条 导出报表' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: /复制对账总控包/ })).toBeInTheDocument();
  expect(screen.getByText('客户里程结论条')).toBeInTheDocument();
  expect(screen.getByText('先给客户一个可交付结论，再进入轨迹复核、明细导出和告警通知。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程结论 区间累计 210 km' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程结论 明细合计 170 km' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程结论 异常记录 1 条' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程结论 在线影响 1 离线' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程动作 复制结论 复制摘要' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程动作 轨迹复核 轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程动作 导出明细 导出CSV' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程动作 告警通知 在线态势' })).toBeInTheDocument();
  expect(screen.getByText('客户里程口径交付条')).toBeInTheDocument();
  expect(screen.getByText('日里程按当天首末总里程差值计算；区间总值必须等于每日里程之和，异常日进入轨迹和历史明细复核。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程口径交付 区间总值 210 km' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程口径交付 明细合计 170 km' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程口径交付 闭合差值 需全量' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程口径交付 异常记录 1 条' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程口径动作 复制口径 决策说明' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程口径动作 复制交付 交付包' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程口径动作 轨迹复核 轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程口径动作 导出明细 CSV' })).toBeInTheDocument();
  expect(screen.getByText('客户里程统计工作台')).toBeInTheDocument();
  expect(screen.getByText('统计页先回答客户这段时间跑了多少、每日是否闭合、异常在哪里、能否导出。')).toBeInTheDocument();
  expect(screen.getByText('来源证据只用于统计可信度：JT808')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程统计工作台 区间里程 210 km 复制摘要' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程统计工作台 每日闭合 分页抽样 发布审计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程统计工作台 轨迹回放 2026-07-02 回放轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程统计工作台 历史导出 3 条 导出明细' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程统计工作台 告警通知 1 离线 在线态势' })).toBeInTheDocument();
  expect(screen.getByText('先定车辆和时间范围，再判断里程能否交付')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程统计 选择范围 调整筛选' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程统计 区间里程 复制摘要' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程统计 发布判断 发布审计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程统计 复核证据 轨迹复核' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程统计 明细导出 导出明细' })).toBeInTheDocument();
  expect(screen.getByText('客户里程对账台')).toBeInTheDocument();
  expect(screen.getByText('用同一辆车和同一时间窗完成区间里程、日报闭合、轨迹证据、告警通知和报表导出。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程对账台 区间里程 210 km 复制摘要' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程对账台 日报闭合 分页抽样 闭合审计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程对账台 轨迹证据 2026-07-02 轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程对账台 告警通知 1 离线 在线态势' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程对账台 报表导出 3 条 导出CSV' })).toBeInTheDocument();
  expect(screen.getByText('客户里程交付导航')).toBeInTheDocument();
  expect(screen.getByText('把里程统计变成客户能看懂的交付路径：先确认口径，再回放轨迹，最后导出明细和结论。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程交付导航 确认里程口径 复制统计摘要' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程交付导航 回放里程轨迹 轨迹复核' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程交付导航 导出明细证据 导出CSV' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程交付导航 发送交付结论 复制交付包' })).toBeInTheDocument();
  expect(screen.getByText('里程客户验收清单')).toBeInTheDocument();
  expect(screen.getByText('客户交付前只看五个门禁：范围、闭合、覆盖、异常、在线影响；任何一项不通过都能直接进入复核动作。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程客户验收 范围确认 VIN-STATS-SERVICE' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程客户验收 区间闭合 分页抽样' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程客户验收 明细覆盖 50%' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程客户验收 异常复核 1 条' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程客户验收 在线影响 1 离线' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程验收动作 复制验收 交付说明' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程验收动作 复核轨迹 轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程验收动作 导出明细 CSV' })).toBeInTheDocument();
  expect(await screen.findByText('车辆统计服务总览')).toBeInTheDocument();
  expect(screen.getAllByText('里程统计').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('在线率与离线时长')).toBeInTheDocument();
  expect(screen.getByText('数据完整性')).toBeInTheDocument();
  expect(screen.getAllByText('通道一致性').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('三类数据源最终汇总为一个车辆服务统计视图')).toBeInTheDocument();
  expect(screen.getByText('当前统计证据')).toBeInTheDocument();
  expect(screen.getByText('3 车 / 2 数据通道 / 3 条明细')).toBeInTheDocument();
  expect(screen.getByText('客户里程决策台')).toBeInTheDocument();
  expect(screen.getByText('先判断能不能交付，再进入轨迹、历史明细和导出复核')).toBeInTheDocument();
  expect(screen.getAllByRole('button', { name: '复制决策说明' }).length).toBeGreaterThanOrEqual(1);
  expect(screen.getByRole('button', { name: '客户里程决策 交付结论 复制决策' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程决策 区间闭合 统计摘要' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程决策 覆盖率 导出明细' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程决策 异常复核 异常明细' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户里程决策 在线影响 在线态势' })).toBeInTheDocument();
  expect(screen.getByText('客户里程复核交付包')).toBeInTheDocument();
  expect(screen.getByText('面向客户交付里程时，先确认区间里程、闭合状态、异常记录和在线影响，再导出明细或进入轨迹与历史明细复核。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程交付 区间里程 复制交付' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程交付 闭合校验 闭合摘要' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程交付 异常记录 异常明细' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程交付 在线影响 在线态势' })).toBeInTheDocument();
  expect(screen.getByText('里程报告建议')).toBeInTheDocument();
  expect(screen.getByText('从车辆服务视角安排下一步')).toBeInTheDocument();
  expect(screen.getByText('里程页不再让客户先理解不同接入协议的差异，而是直接给出交付、轨迹回放、历史查询、导出和在线影响的下一步动作。')).toBeInTheDocument();
  expect(screen.queryByText(/GB32960、JT808|Yutong MQTT/)).not.toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程报告建议 先完成发布复核 发布审计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程报告建议 优先复核异常日 轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程报告建议 拉齐全量明细 导出明细' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程报告建议 关注离线影响 在线态势' })).toBeInTheDocument();
  expect(screen.getByText('统计影响范围')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '复制影响范围' })).toBeInTheDocument();
  expect(screen.getByText('影响待复核')).toBeInTheDocument();
  expect(screen.getByText('3 辆车')).toBeInTheDocument();
  expect(screen.getByText('当前筛选会影响 6 条里程记录、210 km 里程口径和 1 辆离线车辆判断。')).toBeInTheDocument();
  expect(screen.getByText('里程服务任务板')).toBeInTheDocument();
  expect(screen.getAllByText('发布判断').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('区间复核')).toBeInTheDocument();
  expect(screen.getByText('异常处理')).toBeInTheDocument();
  expect(screen.getAllByText('明细导出').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByRole('button', { name: '里程服务任务 发布判断 BI口径' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程服务任务 区间复核 轨迹证据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程服务任务 异常处理 复制证据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '里程服务任务 明细导出 导出明细' })).toBeInTheDocument();
  expect(screen.getByText('影响车辆')).toBeInTheDocument();
  expect(screen.getByText('统计规模')).toBeInTheDocument();
  expect(screen.getByText('闭合影响')).toBeInTheDocument();
  expect(screen.getByText('异常影响')).toBeInTheDocument();
  expect(screen.getAllByText('发布判断').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('在线影响评估')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '复制在线影响' })).toBeInTheDocument();
  expect(screen.getByText('离线车辆')).toBeInTheDocument();
  expect(screen.getAllByText('来源覆盖').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('当前页离线')).toBeInTheDocument();
  expect(screen.getByText('最长离线')).toBeInTheDocument();
  expect(screen.getByText('统计证据')).toBeInTheDocument();
  expect((await screen.findAllByText('75%')).length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText((content) => content.includes('离线 1 车')).length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText((content) => content.includes('Redis 在线 92 key')).length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText((content) => content.includes('由车辆服务覆盖汇总、实时来源状态和 Redis 在线 key 计算')).length).toBeGreaterThanOrEqual(1);
  fireEvent.click(screen.getByRole('button', { name: '复制影响范围' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【统计影响范围】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆范围：VIN-STATS-SERVICE'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('数据通道：JT808'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('影响车辆：3 辆'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('统计记录：6 条'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('累计里程：210 km'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('在线状态：3 在线 / 1 离线 / 在线率 75%'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('发布判断：复核后发布'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('业务影响：当前统计范围需要先复核轨迹、历史明细和离线车辆影响'));
  fireEvent.click(screen.getByRole('button', { name: /复制对账总控包/ }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【客户里程对账总控包】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('服务链路：区间里程 -> 每日闭合 -> 轨迹证明 -> 报表导出'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('对账范围：VIN-STATS-SERVICE / JT808 / 全部日期'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('区间里程：210 km'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('闭合状态：分页抽样'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('报表导出：http://localhost:3000/#/mileage?keyword=VIN-STATS-SERVICE&protocol=JT808'));
  fireEvent.click(screen.getByRole('button', { name: /复制报表旅程包/ }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【里程报表交付旅程包】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('报表周期：全部日期'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆范围：VIN-STATS-SERVICE'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('区间闭合：分页抽样'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹证明：粤A统计服务2 / 2026-07-02 / 80 km'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('导出发布：复核后交付 / 复核后发布'));
  fireEvent.click(screen.getAllByRole('button', { name: '复制决策说明' })[0]);
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【客户里程决策说明】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('决策结论：复核后交付'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('客户判断路径：'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. 先看区间闭合：区间总值必须等于每日里程之和。'));
  fireEvent.click(screen.getByRole('button', { name: '复制在线影响' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【在线影响评估】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆范围：VIN-STATS-SERVICE'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('数据通道：JT808'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('在线概览：3 在线 / 1 离线 / 在线率 75%'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('Redis 在线 Key：92'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('当前页车辆：2 / 当前筛选 2'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('最长离线：粤A离线1 / 2 小时 / 2026-07-03 18:12:10'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. JT808：2/3，在线率 66.7%'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('2. 离线车辆优先打开车辆服务，核对实时、轨迹、历史明细和告警记录'));
  fireEvent.click(screen.getByRole('button', { name: '里程交付 区间里程 复制交付' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【客户里程复核交付包】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆范围：VIN-STATS-SERVICE'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('累计里程：210 km'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('发布判断：复核后发布'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('里程口径：日里程按首末总里程差值计算，区间总值应等于每日里程之和'));
  expect(await screen.findByText('在线状态明细')).toBeInTheDocument();
  expect(await screen.findByText('VIN-STATS-SERVICE-OFFLINE')).toBeInTheDocument();
  expect(screen.getByText('粤A离线1')).toBeInTheDocument();
  expect(screen.getAllByText('2 小时').length).toBeGreaterThan(0);
  fireEvent.click(screen.getByRole('button', { name: '复制在线状态清单' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【车辆在线状态说明】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆范围：VIN-STATS-SERVICE'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('在线概览：3 在线 / 1 离线 / 在线率 75%'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('来源在线：JT808 2/3；GB32960 1/2'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. 粤A离线1 / VIN-STATS-SERVICE-OFFLINE / 广安车联'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('离线时长：2 小时；最后上报：2026-07-03 18:12:10'));
});

test('copies mileage statistics summary for operations reporting', async () => {
  window.history.replaceState(null, '', '/#/mileage?keyword=VIN-MILEAGE-COPY&protocol=JT808&dateFrom=2026-07-01&dateTo=2026-07-03');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { vehicleCount: 2, recordCount: 4, sourceCount: 2, totalMileageKm: 140, averageMileagePerVin: 70 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/daily')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [
              { vin: 'VIN-MILEAGE-COPY', plate: '粤A统计1', source: 'JT808', date: '2026-07-01', startMileageKm: 100, endMileageKm: 140, dailyMileageKm: 40, anomalySeverity: '' },
              { vin: 'VIN-MILEAGE-COPY-2', plate: '粤A统计2', source: 'JT808', date: '2026-07-02', startMileageKm: 300, endMileageKm: 400, dailyMileageKm: 100, anomalySeverity: 'warning' }
            ],
            total: 2,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('区间趋势')).toBeInTheDocument();
  expect(screen.getByText('BI发布口径')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '复制BI发布口径' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '复制统计发布审计' })).toBeInTheDocument();
  fireEvent.click(screen.getAllByRole('button', { name: '复制统计摘要' })[0]);

  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【里程统计摘要】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆范围：VIN-MILEAGE-COPY'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('数据通道：JT808'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('日期范围：2026-07-01 至 2026-07-03'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('累计里程：140 km'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('当前页里程：140 km'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('异常记录：1'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('最大单日：粤A统计2 / 2026-07-02 / 100 km'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('里程口径：区间总值等于各日里程之和'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('里程统计：http://localhost:3000/#/mileage?keyword=VIN-MILEAGE-COPY&protocol=JT808&dateFrom=2026-07-01&dateTo=2026-07-03'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务：http://localhost:3000/#/detail?keyword=VIN-MILEAGE-COPY&protocol=JT808'));
  fireEvent.click(screen.getByRole('button', { name: '复制BI发布口径' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【BI里程发布口径】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('发布结论：需复核后发布'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('里程口径：日里程按首末总里程差值计算，区间总值等于每日里程之和'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('闭合状态：分页抽样'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('记录覆盖率：50%'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('异常记录：1'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('通道一致性：可做跨通道核对'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('发布风险：存在分页未全量、异常记录或闭合差值，需要先复核轨迹和历史明细'));
  fireEvent.click(screen.getByRole('button', { name: '复制统计发布审计' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【统计发布审计】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('发布结论：复核后发布'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('审计结果：通过 1 / 复核 4 / 阻断 0'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('[复核] 记录覆盖：50%'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('[复核] 异常记录：1'));
});

test('exports current mileage page as CSV', async () => {
  window.history.replaceState(null, '', '/#/mileage?keyword=VIN-MILEAGE-EXPORT&protocol=JT808');
  const createObjectURL = vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:mileage-export');
  const revokeObjectURL = vi.spyOn(URL, 'revokeObjectURL').mockImplementation(() => undefined);
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { vehicleCount: 1, recordCount: 1, sourceCount: 1, totalMileageKm: 42.5, averageMileagePerVin: 42.5 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/daily')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [
              { vin: 'VIN-MILEAGE-EXPORT', plate: '粤A导出1', source: 'JT808', date: '2026-07-03', startMileageKm: 100, endMileageKm: 142.5, dailyMileageKm: 42.5, anomalySeverity: '' }
            ],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect((await screen.findAllByText('VIN-MILEAGE-EXPORT')).length).toBeGreaterThan(0);
  fireEvent.click(screen.getByRole('button', { name: '导出里程当前页 CSV' }));

  expect(createObjectURL).toHaveBeenCalled();
  expect(revokeObjectURL).toHaveBeenCalledWith('blob:mileage-export');
});

test('opens vehicle service from history results with row source evidence', async () => {
  window.history.replaceState(null, '', '/#/history?keyword=%E7%B2%A4AG18312&protocol=JT808');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input, init) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/locations')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-JT808-001',
              plate: '粤AG18312',
              protocol: 'JT808',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 30,
              socPercent: 0,
              totalMileageKm: 100,
              lastSeen: '2026-07-03 20:12:10',
              deviceTime: '2026-07-03 20:12:10',
              serverTime: '2026-07-03 20:12:11'
            }],
            total: 1,
            limit: 10,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/resolve')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            lookupKey: 'VIN-JT808-001',
            resolved: true,
            vin: 'VIN-JT808-001',
            plate: '粤AG18312',
            phone: '13307795425',
            oem: 'G7s',
            protocols: ['JT808'],
            online: true,
            lastSeen: '2026-07-03 20:12:10'
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('VIN-JT808-001')).toBeInTheDocument();
  const locationRow = screen.getByText('VIN-JT808-001').closest('tr');
  expect(locationRow).not.toBeNull();
  fireEvent.click(within(locationRow!).getByRole('button', { name: '车辆服务' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/detail?keyword=VIN-JT808-001&protocol=JT808');
  });
});

test('opens same-day mileage statistics from history location row', async () => {
  window.history.replaceState(null, '', '/#/history?keyword=VIN-HISTORY-MILEAGE&protocol=JT808');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/locations')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-HISTORY-MILEAGE',
              plate: '粤A历史账',
              protocol: 'JT808',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 30,
              socPercent: 0,
              totalMileageKm: 218.8,
              lastSeen: '2026-07-03 20:12:10',
              deviceTime: '2026-07-03 20:12:10',
              serverTime: '2026-07-03 20:12:11'
            }],
            total: 1,
            limit: 10,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { vehicleCount: 1, recordCount: 1, sourceCount: 1, totalMileageKm: 18.8, averageMileagePerVin: 18.8 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/daily')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-HISTORY-MILEAGE',
              plate: '粤A历史账',
              source: 'JT808',
              date: '2026-07-03',
              startMileageKm: 200,
              endMileageKm: 218.8,
              dailyMileageKm: 18.8,
              anomalySeverity: 'normal'
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect((await screen.findAllByText('VIN-HISTORY-MILEAGE')).length).toBeGreaterThan(0);
  const locationRow = screen.getByRole('row', { name: /VIN-HISTORY-MILEAGE/ });
  fireEvent.click(within(locationRow).getByRole('button', { name: '里程统计' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/mileage?keyword=VIN-HISTORY-MILEAGE&protocol=JT808&dateFrom=2026-07-03&dateTo=2026-07-04');
  });
});

test('opens same-day raw evidence from history location row', async () => {
  window.history.replaceState(null, '', '/#/history?keyword=VIN-HISTORY-RAW&protocol=JT808');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/locations')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-HISTORY-RAW',
              plate: '粤A原帧核',
              protocol: 'JT808',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 30,
              socPercent: 0,
              totalMileageKm: 218.8,
              lastSeen: '2026-07-03 20:12:10',
              deviceTime: '2026-07-03 20:12:10',
              serverTime: '2026-07-03 20:12:11'
            }],
            total: 1,
            limit: 10,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/raw-frames/query')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              id: 'raw-from-location-001',
              vin: 'VIN-HISTORY-RAW',
              plate: '粤A原帧核',
              protocol: 'JT808',
              frameType: '0x0200',
              deviceTime: '2026-07-03 20:12:10',
              serverTime: '2026-07-03 20:12:11',
              rawSizeBytes: 128,
              parsedFields: { 'jt808.location.longitude': 113.2 }
            }],
            total: 1,
            limit: 10,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect((await screen.findAllByText('VIN-HISTORY-RAW')).length).toBeGreaterThan(0);
  fireEvent.click(screen.getByRole('button', { name: '核对历史明细' }));

  await waitFor(() => {
    expect(window.location.hash.startsWith('#/history-query?')).toBe(true);
  });
  const params = new URLSearchParams(window.location.hash.slice('#/history-query?'.length));
  expect(params.get('keyword')).toBe('VIN-HISTORY-RAW');
  expect(params.get('protocol')).toBe('JT808');
  expect(params.get('dateFrom')).toBe('2026-07-03');
  expect(params.get('dateTo')).toBe('2026-07-04');
  expect(params.get('includeFields')).toBe('true');
  expect(params.get('tab')).toBe('raw');
  expect(await screen.findByRole('tab', { name: '历史明细' })).toHaveAttribute('aria-selected', 'true');
  expect((await screen.findAllByText('raw-from-location-001')).length).toBeGreaterThanOrEqual(1);
});

test('opens vehicle service from raw history rows with row source evidence', async () => {
  window.history.replaceState(null, '', '/#/history?keyword=%E7%B2%A4AG18312&protocol=GB32960');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/locations')) {
      return {
        ok: true,
        json: async () => ({
          data: { items: [], total: 0, limit: 10, offset: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/raw-frames/query')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              id: 'raw-gb32960-001',
              vin: 'VIN-GB32960-001',
              plate: '粤AG18312',
              protocol: 'GB32960',
              frameType: 'REALTIME',
              deviceTime: '2026-07-03 20:12:10',
              serverTime: '2026-07-03 20:12:11',
              rawSizeBytes: 256,
              parsedFields: {}
            }],
            total: 1,
            limit: 10,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/resolve')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            lookupKey: 'VIN-GB32960-001',
            resolved: true,
            vin: 'VIN-GB32960-001',
            plate: '粤AG18312',
            phone: '',
            oem: 'G7s',
            protocols: ['GB32960'],
            online: true,
            lastSeen: '2026-07-03 20:12:10'
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  fireEvent.click(await screen.findByRole('tab', { name: '历史明细' }));
  expect(await screen.findByText('raw-gb32960-001')).toBeInTheDocument();
  const rawRow = screen.getByText('raw-gb32960-001').closest('tr');
  expect(rawRow).not.toBeNull();
  fireEvent.click(within(rawRow!).getByRole('button', { name: '车辆服务' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/detail?keyword=VIN-GB32960-001&protocol=GB32960');
  });
});

test('opens same-day mileage statistics from raw history row', async () => {
  window.history.replaceState(null, '', '/#/history-query?keyword=VIN-RAW-MILEAGE&protocol=JT808&tab=raw');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/locations')) {
      return {
        ok: true,
        json: async () => ({
          data: { items: [], total: 0, limit: 10, offset: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/history/raw-frames/query')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              id: 'raw-jt808-mileage-001',
              vin: 'VIN-RAW-MILEAGE',
              plate: '粤ARAW里',
              protocol: 'JT808',
              frameType: '0x0200',
              deviceTime: '2026-07-03 20:12:10',
              serverTime: '2026-07-03 20:12:11',
              rawSizeBytes: 128,
              parsedFields: { 'jt808.location.total_mileage_km': 218.8 }
            }],
            total: 1,
            limit: 10,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { vehicleCount: 1, recordCount: 1, sourceCount: 1, totalMileageKm: 18.8, averageMileagePerVin: 18.8 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/mileage/daily')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-RAW-MILEAGE',
              plate: '粤ARAW里',
              source: 'JT808',
              date: '2026-07-03',
              startMileageKm: 200,
              endMileageKm: 218.8,
              dailyMileageKm: 18.8,
              anomalySeverity: ''
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect((await screen.findAllByText('raw-jt808-mileage-001')).length).toBeGreaterThanOrEqual(1);
  const rawRow = screen.getByRole('row', { name: /raw-jt808-mileage-001/ });
  fireEvent.click(within(rawRow).getByRole('button', { name: '里程统计' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/mileage?keyword=VIN-RAW-MILEAGE&protocol=JT808&dateFrom=2026-07-03&dateTo=2026-07-04');
  });
  expect(await screen.findByRole('heading', { name: '里程统计' })).toBeInTheDocument();
});

test('opens current vehicle service from history header with current source evidence', async () => {
  window.history.replaceState(null, '', '/#/history?keyword=VIN-JT808-HEADER&protocol=JT808');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/resolve')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            lookupKey: 'VIN-JT808-HEADER',
            resolved: true,
            vin: 'VIN-JT808-HEADER',
            plate: '粤AG18312',
            phone: '13307795425',
            oem: 'G7s',
            protocols: ['JT808'],
            online: true,
            lastSeen: '2026-07-03 20:12:10'
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect((await screen.findAllByText('来源证据：JT808')).length).toBeGreaterThan(0);
  fireEvent.click(screen.getByRole('button', { name: '当前车辆服务' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/detail?keyword=VIN-JT808-HEADER&protocol=JT808');
  });
});

test('opens vehicle service from realtime vehicles with primary source evidence', async () => {
  window.history.replaceState(null, '', '/#/realtime');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/realtime/vehicles')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-MQTT-001',
              plate: '川AHTWO1',
              phone: '',
              oem: '宇通',
              protocols: ['GB32960', 'YUTONG_MQTT'],
              sourceCount: 2,
              onlineSourceCount: 1,
              online: true,
              primaryProtocol: 'YUTONG_MQTT',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 30,
              socPercent: 78,
              totalMileageKm: 119925,
              lastSeen: '2026-07-03 20:12:10'
            }],
            total: 1,
            limit: 50,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/overview')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN-MQTT-001',
            plate: '川AHTWO1',
            sourceCount: 2,
            onlineSourceCount: 1,
            coverageStatus: 'online',
            primaryProtocol: 'YUTONG_MQTT',
            lastSeen: '2026-07-03 20:12:10',
            historyCount: 0,
            rawCount: 0,
            mileageCount: 0,
            qualityIssueCount: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect((await screen.findAllByText('VIN-MQTT-001')).length).toBeGreaterThan(0);
  fireEvent.click(screen.getByRole('button', { name: '车辆服务' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/detail?keyword=VIN-MQTT-001&protocol=YUTONG_MQTT');
  });
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/vehicle-service/overview?keyword=VIN-MQTT-001&protocol=YUTONG_MQTT'), undefined);
});

test('opens vehicle service from realtime vehicles with current source filter', async () => {
  window.history.replaceState(null, '', '/#/realtime?keyword=VIN-RT-FILTER&protocol=JT808');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/realtime/vehicles')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-RT-FILTER',
              plate: '粤ART808',
              phone: '',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              sourceStatus: [
                { protocol: 'GB32960', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: false, hasRaw: false, hasMileage: false },
                { protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:11:10', hasRealtime: true, hasHistory: false, hasRaw: false, hasMileage: false },
                { protocol: 'YUTONG_MQTT', online: false, lastSeen: '', hasRealtime: false, hasHistory: false, hasRaw: false, hasMileage: false }
              ],
              sourceCount: 2,
              onlineSourceCount: 2,
              online: true,
              primaryProtocol: 'GB32960',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 30,
              socPercent: 78,
              totalMileageKm: 119925,
              lastSeen: '2026-07-03 20:12:10'
            }],
            total: 1,
            limit: 50,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/overview')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN-RT-FILTER',
            plate: '粤ART808',
            sourceCount: 2,
            onlineSourceCount: 2,
            coverageStatus: 'online',
            primaryProtocol: 'GB32960',
            lastSeen: '2026-07-03 20:12:10',
            historyCount: 0,
            rawCount: 0,
            mileageCount: 0,
            qualityIssueCount: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect((await screen.findAllByText('VIN-RT-FILTER')).length).toBeGreaterThan(0);
  fireEvent.click(screen.getByRole('button', { name: '车辆服务' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/detail?keyword=VIN-RT-FILTER&protocol=JT808');
  });
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/vehicle-service/overview?keyword=VIN-RT-FILTER&protocol=JT808'), undefined);
});

test('shows canonical service status in realtime vehicles', async () => {
  window.history.replaceState(null, '', '/#/realtime');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/realtime/vehicles')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-RT-DEGRADED',
              plate: '粤ART001',
              phone: '',
              oem: 'G7s',
              protocols: ['GB32960'],
              sourceCount: 1,
              onlineSourceCount: 1,
              online: true,
              primaryProtocol: 'GB32960',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 30,
              socPercent: 78,
              totalMileageKm: 119925,
              lastSeen: '2026-07-03 20:12:10',
              bindingStatus: 'bound',
              serviceStatus: {
                status: 'degraded',
                severity: 'warning',
                title: '来源不完整',
                detail: '由实时车辆 API 统一判定',
                sourceCount: 2,
                onlineSourceCount: 1
              }
            }],
            total: 1,
            limit: 50,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect((await screen.findAllByText('VIN-RT-DEGRADED')).length).toBeGreaterThan(0);
  expect(screen.getAllByText('数据通道不完整').length).toBeGreaterThan(0);
});

test('realtime page prioritizes customer vehicle monitoring command center', async () => {
  window.history.replaceState(null, '', '/#/realtime');
  vi.setSystemTime(new Date('2026-07-03T20:15:00+08:00'));
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            linkHealth: [],
            kafkaLag: 0,
            activeConnections: 0,
            capacityFindings: [],
            redisOnlineKeys: 0,
            tdengineWritable: true,
            mysqlWritable: true,
            runtime: {
              requestTimeoutMs: 5000,
              platformRelease: 'platform-realtime-command-test',
              amapWebJsConfigured: true,
              amapApiConfigured: true,
              amapSecurityProxyEnabled: true,
              amapSecurityCodeExposed: false,
              amapSecurityServiceHost: '/_AMapService'
            }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/realtime/vehicles')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [
              {
                vin: 'VIN-RT-CUSTOMER-001',
                plate: '粤A实时1',
                phone: '13300000001',
                oem: 'G7s',
                protocols: ['JT808', 'GB32960'],
                sourceStatus: [
                  { protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:14:30', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
                  { protocol: 'GB32960', online: true, lastSeen: '2026-07-03 20:14:25', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }
                ],
                sourceCount: 2,
                onlineSourceCount: 2,
                online: true,
                primaryProtocol: 'JT808',
                longitude: 113.2,
                latitude: 23.1,
                speedKmh: 36,
                socPercent: 78,
                totalMileageKm: 119925,
                lastSeen: '2026-07-03 20:14:30',
                bindingStatus: 'bound',
                serviceStatus: {
                  status: 'healthy',
                  severity: 'ok',
                  title: '服务正常',
                  detail: '车辆实时服务正常',
                  sourceCount: 2,
                  onlineSourceCount: 2
                }
              },
              {
                vin: 'VIN-RT-CUSTOMER-002',
                plate: '粤A实时2',
                phone: '13300000002',
                oem: 'G7s',
                protocols: ['JT808'],
                sourceStatus: [
                  { protocol: 'JT808', online: true, lastSeen: '2026-07-03 19:40:00', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
                  { protocol: 'GB32960', online: false, lastSeen: '', hasRealtime: false, hasHistory: false, hasRaw: false, hasMileage: false }
                ],
                sourceCount: 2,
                onlineSourceCount: 1,
                online: true,
                primaryProtocol: 'JT808',
                longitude: 0,
                latitude: 0,
                speedKmh: 0,
                socPercent: 0,
                totalMileageKm: 8899,
                lastSeen: '2026-07-03 19:40:00',
                bindingStatus: 'bound',
                serviceStatus: {
                  status: 'degraded',
                  severity: 'warning',
                  title: '来源不完整',
                  detail: '缺少 GB32960',
                  sourceCount: 2,
                  onlineSourceCount: 1
                }
              }
            ],
            total: 2,
            limit: 50,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('实时车辆服务总览')).toBeInTheDocument();
  expect(screen.getByText('客户先看车辆是否在线、地图是否可见、哪些车辆需要处置，再进入轨迹、统计、导出和告警。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时车辆服务总览 在线车辆 2 在线' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时车辆服务总览 有效定位 1 辆' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时车辆服务总览 需关注 1 辆' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时车辆服务总览 地图能力 高德可用' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时服务动作 打开地图 地图态势' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时服务动作 轨迹复盘 轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时服务动作 导出清单 导出CSV' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时服务动作 告警通知 关注车辆' })).toBeInTheDocument();
  expect(await screen.findByText('客户实时监控总控台')).toBeInTheDocument();
  expect(screen.getByText('实时页先回答客户能不能看到车、车辆在哪里、哪些车辆异常，再进入轨迹、统计、导出和告警。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户实时监控总控台 实时地图 1 有定位 打开地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户实时监控总控台 在线车辆 2 在线 只看在线' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户实时监控总控台 异常车辆 1 关注 关注异常' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户实时监控总控台 轨迹复盘 粤A实时1 轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户实时监控总控台 导出交付 2 当前页 导出CSV' })).toBeInTheDocument();
  expect(screen.getByText('来源证据仅用于可信度判断：JT808 / GB32960')).toBeInTheDocument();
  expect(screen.getByText('实时处置优先级')).toBeInTheDocument();
  expect(screen.getByText('按客户影响排序：先处理不能看车、定位无效、数据通道不完整和更新超时的车辆。')).toBeInTheDocument();
  expect(screen.getAllByText('粤A实时2').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText((content) => content.includes('坐标无效')).length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText((content) => content.includes('缺少 GB32960')).length).toBeGreaterThanOrEqual(1);
  expect(screen.getByRole('button', { name: '实时处置优先级 1 粤A实时2 查看告警' })).toBeInTheDocument();
});

test('realtime page treats protocol filters as source evidence for vehicle service', async () => {
  window.history.replaceState(null, '', '/#/realtime');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/realtime/vehicles')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-RT-EVIDENCE',
              plate: '粤A证据1',
              protocols: ['GB32960', 'JT808'],
              sourceStatus: [
                { protocol: 'GB32960', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
                { protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:12:08', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }
              ],
              sourceCount: 2,
              onlineSourceCount: 2,
              online: true,
              primaryProtocol: 'GB32960',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 30,
              socPercent: 78,
              totalMileageKm: 119925,
              lastSeen: '2026-07-03 20:12:10',
              bindingStatus: 'bound',
              serviceStatus: {
                status: 'healthy',
                severity: 'ok',
                title: '服务正常',
                detail: '2/2 来源在线',
                sourceCount: 2,
                onlineSourceCount: 2
              }
            }],
            total: 1,
            limit: 50,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByRole('heading', { name: '实时监控' })).toBeInTheDocument();
  expect(screen.getByText('来源证据筛选')).toBeInTheDocument();
  expect(screen.getByText('全部来源证据')).toBeInTheDocument();
  expect(screen.getByText('车辆来源证据覆盖')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '筛选来源证据 GB32960' })).toBeInTheDocument();
});

test('frames realtime page as one vehicle service with source evidence', async () => {
  window.history.replaceState(null, '', '/#/realtime');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/realtime/vehicles')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-RT-SERVICE',
              plate: '粤ART002',
              phone: '',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              sourceStatus: [
                { protocol: 'GB32960', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: false, hasRaw: false, hasMileage: false },
                { protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:11:10', hasRealtime: true, hasHistory: false, hasRaw: false, hasMileage: false },
                { protocol: 'YUTONG_MQTT', online: false, lastSeen: '', hasRealtime: false, hasHistory: false, hasRaw: false, hasMileage: false }
              ],
              sourceCount: 2,
              onlineSourceCount: 2,
              online: true,
              primaryProtocol: 'GB32960',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 30,
              socPercent: 78,
              totalMileageKm: 119925,
              lastSeen: '2026-07-03 20:12:10',
              bindingStatus: 'bound',
              serviceStatus: {
                status: 'healthy',
                severity: 'ok',
                title: '服务正常',
                detail: '2/2 来源在线',
                sourceCount: 2,
                onlineSourceCount: 2
              }
            }],
            total: 1,
            limit: 50,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByRole('heading', { name: '实时监控' })).toBeInTheDocument();
  expect(screen.getAllByText('高德地图待配置').length).toBeGreaterThan(0);
  expect(screen.getByText('客户实时监控')).toBeInTheDocument();
  expect(screen.getByText('从实时列表直接完成车辆监控闭环')).toBeInTheDocument();
  expect(screen.getByText('实时地图调度台')).toBeInTheDocument();
  expect(screen.getByText('像现代车队 Live Map 一样，把地图作为调度指挥台：先看在线位置，再处理轨迹、告警和导出。')).toBeInTheDocument();
  expect(screen.getByText('协议来源只作为车辆位置可信度证据，客户主路径是找车、调度、复盘和通知。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时地图调度台 查看在线车辆 实时地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时地图调度台 复盘选中车辆 轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时地图调度台 处理异常车辆 告警事件' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时地图调度台 导出当前态势 导出CSV' })).toBeInTheDocument();
  expect(screen.getByText('实时客户常问')).toBeInTheDocument();
  expect(screen.getByText('把实时数据翻译成客户能直接理解的车辆状态')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时客户常问 这辆车现在在哪里？ 地图定位' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时客户常问 这辆车还在线吗？ 只看在线' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时客户常问 为什么状态异常？ 异常清单' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时客户常问 当前状态能交接吗？ 复制交接包' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆监控建议 先处理异常车辆 关注异常' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆监控建议 复盘选中车辆 轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆监控建议 导出当前实时清单 导出 CSV' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时监控路径 只看在线 在线车辆' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时监控路径 地图态势 查看地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时监控路径 异常优先 关注车辆' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时监控路径 单车复盘 轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时监控路径 查询导出 导出 CSV' })).toBeInTheDocument();
  expect(screen.getByText('实时单车服务快照')).toBeInTheDocument();
  expect(screen.getByText('客户打开实时监控时，直接围绕选中车辆查看在线、位置、里程、新鲜度，并进入轨迹、统计、历史导出和告警通知。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时单车服务快照 车辆服务 粤ART002 车辆档案' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时单车服务快照 轨迹回放 更新超时 打开轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时单车服务快照 里程统计 119925 km 查里程' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时单车服务快照 历史导出 GB32960 导出证据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时单车服务快照 告警通知 服务正常 查看告警' })).toBeInTheDocument();
  expect(screen.getAllByText('定位有效').length).toBeGreaterThan(0);
  expect(screen.getByText('车辆核心数据')).toBeInTheDocument();
  expect(screen.getByText('实时来源证据')).toBeInTheDocument();
  expect(screen.getByText('来源覆盖')).toBeInTheDocument();
  expect(screen.getAllByText('2/2 来源在线').length).toBeGreaterThan(0);
  expect(screen.getByText('GB32960 在线')).toBeInTheDocument();
  expect(screen.getByText('JT808 在线')).toBeInTheDocument();
  expect(screen.getByText('YUTONG_MQTT 未接入')).toBeInTheDocument();
  expect(screen.getByText('车辆监控影响')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: /复制影响报告/ })).toBeInTheDocument();
  expect(screen.getAllByText('需要处置').length).toBeGreaterThan(0);
  expect(screen.getByText('车辆范围')).toBeInTheDocument();
  expect(screen.getByText('在线影响')).toBeInTheDocument();
  expect(screen.getByText('定位影响')).toBeInTheDocument();
  expect(screen.getByText('服务影响')).toBeInTheDocument();
  expect(screen.getByText('地图能力')).toBeInTheDocument();
  expect(screen.getByText('当前页 1 辆，在线 1 辆，定位有效 1 辆，降级 0 辆，超时 1 辆。')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: /复制影响报告/ }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【车辆监控影响】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('影响等级：需要处置'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆范围：当前页 1 / 总计 1，覆盖率 100%'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('在线状态：1 在线 / 0 离线'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('定位影响：1 辆有有效坐标'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('服务影响：0 辆降级 / 1 辆超时'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('地图能力：高德地图未配置，使用坐标预览'));
  expect(screen.getByText('自定义时间窗复盘')).toBeInTheDocument();
  expect(screen.getByText('按车辆和时间窗复盘发生了什么')).toBeInTheDocument();
  expect(screen.getByText('时间窗监控任务台')).toBeInTheDocument();
  expect(screen.getByText('把同一辆车、同一时间窗贯穿到轨迹、里程、历史导出和告警说明，避免客户跨页面重复筛选。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗监控任务台 1 锁定车辆 粤ART002 已锁定' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗监控任务台 2 轨迹复盘 1 天窗口 轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗监控任务台 3 里程核对 119925 km 里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗监控任务台 4 历史导出 GB32960 导出证据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗监控任务台 5 告警闭环 服务正常 告警复盘' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗复盘 轨迹回放 打开轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '时间窗复盘 历史数据 查询历史' })).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: /复制时间窗包/ }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【客户时间窗复盘包】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('历史数据：http://localhost:3000/#/history-query?keyword=VIN-RT-SERVICE&protocol=GB32960'));
  fireEvent.click(screen.getByRole('button', { name: '时间窗复盘 历史数据 查询历史' }));
  await waitFor(() => {
    const params = new URLSearchParams(window.location.hash.split('?')[1] ?? '');
    expect(window.location.hash).toContain('#/history-query?');
    expect(params.get('keyword')).toBe('VIN-RT-SERVICE');
    expect(params.get('protocol')).toBe('GB32960');
    expect(params.get('dateFrom')).toBeTruthy();
    expect(params.get('dateTo')).toBeTruthy();
  });
});

test('refreshes realtime vehicle data from the monitoring workspace', async () => {
  window.history.replaceState(null, '', '/#/realtime');
  let realtimeCalls = 0;
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/realtime/vehicles')) {
      realtimeCalls += 1;
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: `VIN-REFRESH-${realtimeCalls}`,
              plate: `粤A刷新${realtimeCalls}`,
              protocols: ['JT808'],
              sourceStatus: [{ protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }],
              sourceCount: 1,
              onlineSourceCount: 1,
              online: true,
              primaryProtocol: 'JT808',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 30,
              socPercent: 78,
              totalMileageKm: 119925,
              lastSeen: '2026-07-03 20:12:10',
              bindingStatus: 'bound'
            }],
            total: 1,
            limit: 50,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect((await screen.findAllByText('粤A刷新1')).length).toBeGreaterThan(0);
  expect(screen.getByText('自动刷新 15秒')).toBeInTheDocument();
  expect(screen.getByText((content) => content.includes('最后刷新'))).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '刷新实时数据' }));
  expect((await screen.findAllByText('粤A刷新2')).length).toBeGreaterThan(0);
  expect(realtimeCalls).toBeGreaterThanOrEqual(2);
});

test('shows realtime freshness status for recently updated and stale vehicles', async () => {
  window.history.replaceState(null, '', '/#/map');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/realtime/vehicles')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [
              {
                vin: 'VIN-FRESH-001',
                plate: '粤A新鲜1',
                protocols: ['GB32960'],
                sourceStatus: [{ protocol: 'GB32960', online: true, lastSeen: '2999-07-04 17:00:00', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }],
                sourceCount: 1,
                onlineSourceCount: 1,
                online: true,
                primaryProtocol: 'GB32960',
                longitude: 113.2,
                latitude: 23.1,
                speedKmh: 30,
                socPercent: 78,
                totalMileageKm: 119925,
                lastSeen: '2999-07-04 17:00:00',
                bindingStatus: 'bound'
              },
              {
                vin: 'VIN-STALE-001',
                plate: '粤A超时1',
                protocols: ['JT808'],
                sourceStatus: [{ protocol: 'JT808', online: false, lastSeen: '2020-01-01 00:00:00', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }],
                sourceCount: 1,
                onlineSourceCount: 0,
                online: false,
                primaryProtocol: 'JT808',
                longitude: 113.3,
                latitude: 23.2,
                speedKmh: 0,
                socPercent: 50,
                totalMileageKm: 1000,
                lastSeen: '2020-01-01 00:00:00',
                bindingStatus: 'bound'
              }
            ],
            total: 2,
            limit: 50,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { issueVehicleCount: 0, issueRecordCount: 0, errorCount: 0, warningCount: 0, protocols: [], issueTypes: [] },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/alert-events/notification-plan')) {
      return {
        ok: true,
        json: async () => ({
          data: { rules: [], policies: [], priorityIssues: [], activeRuleCount: 0, p0RuleCount: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/alert-events')) {
      return {
        ok: true,
        json: async () => ({
          data: { items: [], total: 0, limit: 50, offset: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByRole('heading', { name: '车辆实时地图' })).toBeInTheDocument();
  expect(screen.getByText('车辆服务地图中枢')).toBeInTheDocument();
  expect(screen.getByText('把 32960、808、MQTT 收敛成同一张车辆地图：看位置、判在线、选车辆、回放轨迹、处理告警和导出证据。')).toBeInTheDocument();
  expect(screen.getByText('高德地图底座')).toBeInTheDocument();
  expect(screen.getByText('来源证据仅用于筛选和追溯，不作为客户主路径。')).toBeInTheDocument();
  expect(screen.getByText('客户车辆地图总控')).toBeInTheDocument();
  expect(screen.getByText('把实时找车、轨迹回放、围栏告警和报表导出收敛成一条客户能直接使用的地图服务链路。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆地图总控 实时找车 1 在线 打开地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆地图总控 轨迹回放 粤A超时1 回放轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆地图总控 围栏告警 1 关注 告警通知' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户车辆地图总控 报表导出 2 当前页 导出报表' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: /复制地图总控包/ })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务地图任务 实时监控 1 在线' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务地图任务 轨迹回放 粤A超时1' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务地图任务 历史查询 JT808' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务地图任务 告警通知 1 关注' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆服务地图任务 统计导出 2 辆' })).toBeInTheDocument();
  expect(screen.getByText('地图状态筛选条')).toBeInTheDocument();
  expect(screen.getByText('先按客户最关心的在线、离线、无坐标和需关注分组看车，再进入轨迹、统计、导出或告警。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '地图状态筛选条 在线车辆 1 在线' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '地图状态筛选条 离线车辆 1 离线' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '地图状态筛选条 无坐标车辆 0 无坐标' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '地图状态筛选条 需关注车辆 1 关注' })).toBeInTheDocument();
  expect(screen.getByText('地图图层控制台')).toBeInTheDocument();
  expect(screen.getByText('把实时地图拆成客户能理解的图层：在线、关注、定位、选车服务和交付导出。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '地图图层控制台 在线车辆 1 在线' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '地图图层控制台 关注车辆 1 关注' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '地图图层控制台 定位车辆 2 定位' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '地图图层控制台 选中车辆 粤A超时1' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '地图图层动作 只看在线 在线层' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '地图图层动作 优先关注 关注层' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '地图图层动作 选车服务 服务层' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '地图图层动作 导出态势 交付层' })).toBeInTheDocument();
  expect(screen.getAllByText('数据新鲜').length).toBeGreaterThan(0);
  expect(screen.getAllByText('更新超时').length).toBeGreaterThan(0);
  expect(screen.getByText('车辆服务闭环')).toBeInTheDocument();
  expect(screen.getByText('客户从这里完成看车、选车、查轨迹、查历史、查里程、收告警和导出。')).toBeInTheDocument();
  expect(screen.getByText('车队服务旅程条')).toBeInTheDocument();
  expect(screen.getByText('把客户常用的四件事固定成一条路径：实时找车、轨迹复盘、里程核对、导出通知。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车队服务旅程条 1 实时找车 1 在线' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车队服务旅程条 2 轨迹复盘 粤A超时1' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车队服务旅程条 3 里程核对 1000 km' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车队服务旅程条 4 导出通知 1 关注' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: /复制旅程交付包/ })).toBeInTheDocument();
  expect(screen.getByText('Live Map 决策条')).toBeInTheDocument();
  expect(screen.getByText('客户进来先判断四件事：哪些车在线、哪些车能定位、哪辆车要先处理、选中车辆下一步做什么。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: 'Live Map 决策条 在线覆盖 1 / 2' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: 'Live Map 决策条 定位覆盖 2 / 2' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: 'Live Map 决策条 优先处理 1 关注' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: 'Live Map 决策条 当前选车 粤A超时1' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: 'Live Map 决策动作 只看在线 在线' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: 'Live Map 决策动作 定位关注 关注车' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: 'Live Map 决策动作 轨迹复盘 轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: 'Live Map 决策动作 导出地图 CSV' })).toBeInTheDocument();
  expect(screen.getByText('客户常用地图视图')).toBeInTheDocument();
  expect(screen.getByText('把在线运营、异常处置、单车复盘和报表交付保存成客户常用地图入口。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户常用地图视图 在线运营 1 在线 打开地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户常用地图视图 异常处置 1 关注 关注车辆' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户常用地图视图 单车复盘 粤A超时1 选中车辆' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户常用地图视图 报表交付 2 当前页 导出证据' })).toBeInTheDocument();
  expect(screen.getByText('客户地图监控包')).toBeInTheDocument();
  expect(screen.getByText('车辆范围')).toBeInTheDocument();
  expect(screen.getByText('在线态势')).toBeInTheDocument();
  expect(screen.getByText('定位态势')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: /复制地图监控包/ })).toBeInTheDocument();
  expect(screen.getByText('单车服务台')).toBeInTheDocument();
  expect(screen.getAllByText('粤A新鲜1').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByRole('button', { name: '单车服务台 车辆档案 查看车辆' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '单车服务台 轨迹回放 回放轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '单车服务台 历史数据 查询历史' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '单车服务台 告警通知 查看告警' })).toBeInTheDocument();
  expect(screen.getAllByText('关注车辆').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('选中车辆').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('车辆列表')).toBeInTheDocument();
  expect(screen.getAllByText('1 辆更新超时').length).toBeGreaterThanOrEqual(1);
  fireEvent.click(screen.getByRole('button', { name: '单车服务台 交接卡 复制交接' }));
  await waitFor(() => {
    expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【地图选中车辆交接卡】'));
  });
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆：粤A超时1 / VIN-STALE-001 / JT808'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('问题：'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('天未更新；JT808 离线'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务：http://localhost:3000/#/detail?keyword=VIN-STALE-001&protocol=JT808'));
  fireEvent.click(screen.getByRole('button', { name: /复制地图总控包/ }));
  await waitFor(() => {
    expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【客户车辆地图总控包】'));
  });
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('地图总控：当前页 2 / 总计 2'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('服务链路：实时找车 -> 轨迹回放 -> 围栏告警 -> 报表导出'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时地图：http://localhost:3000/#/map?'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('选中车辆：粤A超时1 / VIN-STALE-001 / JT808'));
  fireEvent.click(screen.getByRole('button', { name: /复制旅程交付包/ }));
  await waitFor(() => {
    expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【车队服务旅程交付包】'));
  });
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时找车：1 在线 / 2 当前页 / 2 总计'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹复盘：粤A超时1 / VIN-STALE-001 / JT808'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('里程核对：1000 km'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('导出通知：1 关注 / 1 更新超时'));
  fireEvent.click(screen.getByRole('button', { name: /复制地图监控包/ }));
  await waitFor(() => {
    expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【客户地图监控包】'));
  });
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('在线态势：1 在线 / 1 离线'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务：http://localhost:3000/#/detail?keyword=VIN-STALE-001&protocol=JT808'));
  expect(screen.getByText('自定义时间窗复盘')).toBeInTheDocument();
  expect(screen.getByText('按车辆和时间窗复盘发生了什么')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: /复制时间窗包/ }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【客户时间窗复盘包】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('告警通知：http://localhost:3000/#/alert-events?keyword=VIN-STALE-001&protocol=JT808'));
  fireEvent.click(screen.getByRole('button', { name: '时间窗复盘 告警通知 查看告警' }));
  await waitFor(() => {
    const params = new URLSearchParams(window.location.hash.split('?')[1] ?? '');
    expect(window.location.hash).toContain('#/alert-events?');
    expect(params.get('keyword')).toBe('VIN-STALE-001');
    expect(params.get('protocol')).toBe('JT808');
    expect(params.get('dateFrom')).toBeTruthy();
    expect(params.get('dateTo')).toBeTruthy();
  });
});

test('exports current realtime vehicles as CSV', async () => {
  window.history.replaceState(null, '', '/#/realtime?protocol=JT808');
  const createObjectURL = vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:realtime-export');
  const revokeObjectURL = vi.spyOn(URL, 'revokeObjectURL').mockImplementation(() => undefined);
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/realtime/vehicles')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-RT-EXPORT',
              plate: '粤A实时导',
              phone: '13300000001',
              oem: 'G7s',
              protocols: ['JT808'],
              sourceStatus: [{ protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }],
              sourceCount: 1,
              onlineSourceCount: 1,
              online: true,
              bindingStatus: 'bound',
              primaryProtocol: 'JT808',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 32,
              socPercent: 76,
              totalMileageKm: 1200.5,
              lastSeen: '2026-07-03 20:12:10',
              serviceStatus: {
                status: 'healthy',
                severity: 'ok',
                title: '服务正常',
                detail: '1/1 来源在线',
                sourceCount: 1,
                onlineSourceCount: 1
              }
            }],
            total: 1,
            limit: 50,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 50, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByRole('button', { name: '导出实时当前页 CSV' })).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '导出实时当前页 CSV' }));

  expect(createObjectURL).toHaveBeenCalled();
  expect(revokeObjectURL).toHaveBeenCalledWith('blob:realtime-export');
});

test('copies realtime operations summary from realtime page', async () => {
  window.history.replaceState(null, '', '/#/realtime?protocol=JT808&online=online');
  Object.defineProperty(window, '__LINGNIU_APP_CONFIG__', {
    configurable: true,
    value: { amapWebJsKey: 'amap-key' }
  });
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/realtime/vehicles')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [
              {
                vin: 'VIN-RT-SUMMARY-001',
                plate: '粤A摘要1',
                phone: '13300000001',
                oem: 'G7s',
                protocols: ['JT808'],
                sourceStatus: [{ protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }],
                sourceCount: 1,
                onlineSourceCount: 1,
                online: true,
                bindingStatus: 'bound',
                primaryProtocol: 'JT808',
                longitude: 113.2,
                latitude: 23.1,
                speedKmh: 32,
                socPercent: 76,
                totalMileageKm: 1200.5,
                lastSeen: '2026-07-03 20:12:10',
                serviceStatus: {
                  status: 'healthy',
                  severity: 'ok',
                  title: '服务正常',
                  detail: '1/1 来源在线',
                  sourceCount: 1,
                  onlineSourceCount: 1
                }
              },
              {
                vin: 'VIN-RT-SUMMARY-002',
                plate: '粤A摘要2',
                phone: '13300000002',
                oem: '现代',
                protocols: ['GB32960', 'JT808'],
                sourceStatus: [
                  { protocol: 'GB32960', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
                  { protocol: 'JT808', online: false, lastSeen: '2026-07-03 19:58:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }
                ],
                sourceCount: 2,
                onlineSourceCount: 1,
                online: true,
                bindingStatus: 'bound',
                primaryProtocol: 'GB32960',
                longitude: 113.4,
                latitude: 23.2,
                speedKmh: 18,
                socPercent: 69,
                totalMileageKm: 2200.5,
                lastSeen: '2026-07-03 20:12:08',
                serviceStatus: {
                  status: 'degraded',
                  severity: 'warning',
                  title: '来源不完整',
                  detail: 'JT808 离线，GB32960 仍可支撑车辆服务',
                  sourceCount: 2,
                  onlineSourceCount: 1
                }
              },
              {
                vin: 'VIN-RT-SUMMARY-003',
                plate: '粤A摘要3',
                phone: '13300000003',
                oem: '宇通',
                protocols: ['YUTONG_MQTT'],
                sourceStatus: [{ protocol: 'YUTONG_MQTT', online: false, lastSeen: '2026-07-03 18:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }],
                sourceCount: 1,
                onlineSourceCount: 0,
                online: false,
                bindingStatus: 'bound',
                primaryProtocol: 'YUTONG_MQTT',
                longitude: 0,
                latitude: 0,
                speedKmh: 0,
                socPercent: 60,
                totalMileageKm: 3200.5,
                lastSeen: '2026-07-03 18:12:10',
                serviceStatus: {
                  status: 'offline',
                  severity: 'error',
                  title: '车辆离线',
                  detail: 'YUTONG_MQTT 超过在线窗口',
                  sourceCount: 1,
                  onlineSourceCount: 0
                }
              }
            ],
            total: 3,
            limit: 50,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 50, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect((await screen.findAllByText('VIN-RT-SUMMARY-001')).length).toBeGreaterThan(0);
  expect(screen.getByText('实时作业入口')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时筛选 在线车辆 2' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时筛选 离线车辆 1' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '实时筛选 需要关注 2' })).toBeInTheDocument();
  expect(screen.getByText('建议处置')).toBeInTheDocument();
  expect(screen.getByText('查看需关注车辆 2')).toBeInTheDocument();
  expect(screen.getByText('车辆来源证据覆盖')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '筛选来源证据 GB32960' })).toHaveTextContent('GB32960');
  expect(screen.getByRole('button', { name: '筛选来源证据 JT808' })).toHaveTextContent('2 辆');
  expect(screen.getByRole('button', { name: '筛选来源证据 YUTONG_MQTT' })).toHaveTextContent('超时 1');
  expect(screen.getByText('实时覆盖判读')).toBeInTheDocument();
  expect(screen.getByText('在线率')).toBeInTheDocument();
  expect(screen.getAllByText('66.7%').length).toBeGreaterThanOrEqual(3);
  expect(screen.getByText('定位有效率')).toBeInTheDocument();
  expect(screen.getByText('降级率')).toBeInTheDocument();
  expect(screen.getByText('分页覆盖率')).toBeInTheDocument();
  expect(screen.getByText('100%')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '复制实时摘要' }));

  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【实时监控摘要】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('当前筛选：数据通道：JT808；在线：在线'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆总数：3，当前页：3'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('在线车辆：2，定位有效：2'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('需要关注：2，数据通道：3'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('地图配置：已配置'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('重点车辆：'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('粤A摘要3 / YUTONG_MQTT / 车辆离线'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时页面：http://localhost:3000/#/realtime?protocol=JT808&online=online'));
  fireEvent.click(screen.getByRole('button', { name: '复制实时异常处置清单' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【实时异常处置清单】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('当前筛选：数据通道：JT808；在线：在线'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('异常车辆：3 / 当前页 3 / 总计 3'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('粤A摘要3 / VIN-RT-SUMMARY-003 / JT808'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('状态：车辆离线；在线：离线；问题：'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('坐标无效'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('建议动作：核对YUTONG_MQTT定位字段解析和平台转发'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务：http://localhost:3000/#/detail?keyword=VIN-RT-SUMMARY-003&protocol=JT808'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时监控：http://localhost:3000/#/realtime?keyword=VIN-RT-SUMMARY-003&protocol=JT808'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('轨迹回放：http://localhost:3000/#/history?keyword=VIN-RT-SUMMARY-003&protocol=JT808'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('告警事件：http://localhost:3000/#/alert-events?keyword=VIN-RT-SUMMARY-003&protocol=JT808'));
  fireEvent.click(screen.getByRole('button', { name: '复制车辆监控交付包' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【车辆监控交付包】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('页面车辆：3 / 总计 3；版本：'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('在线：2；离线：1；定位有效：2；需要关注：2；超时：'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆覆盖：GB32960：在线 1/1，实时 1/1；JT808：在线 1/2，实时 2/2；YUTONG_MQTT：在线 0/1，实时 1/1'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('实时入口：http://localhost:3000/#/realtime?protocol=JT808&online=online'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('车辆服务：http://localhost:3000/#/detail?keyword=VIN-RT-SUMMARY-003&protocol=JT808'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('里程统计：http://localhost:3000/#/mileage?keyword=VIN-RT-SUMMARY-003&protocol=JT808'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('历史查询：http://localhost:3000/api/history/raw-frames?protocol=JT808&vin=VIN-RT-SUMMARY-003&limit=20&includeFields=true'));
  fireEvent.click(screen.getByRole('button', { name: '实时筛选 需要关注 2' }));
  expect(window.location.hash).toBe('#/realtime?protocol=JT808&serviceStatus=degraded&online=online');
  fireEvent.click(screen.getByRole('button', { name: '筛选来源证据 GB32960' }));
  expect(window.location.hash).toBe('#/realtime?protocol=GB32960&serviceStatus=degraded&online=online');
});

test('shows production AMap integration status on realtime page', async () => {
  window.history.replaceState(null, '', '/#/realtime');
  Object.defineProperty(window, '__LINGNIU_APP_CONFIG__', {
    configurable: true,
    value: {}
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            linkHealth: [],
            kafkaLag: 0,
            redisOnlineKeys: 0,
            tdengineWritable: true,
            mysqlWritable: true,
            runtime: {
              requestTimeoutMs: 5000,
              amapWebJsConfigured: true,
              amapApiConfigured: true,
              amapSecurityProxyEnabled: true,
              amapSecurityCodeExposed: false,
              amapSecurityServiceHost: '/_AMapService',
              platformRelease: 'platform-20260704153005'
            }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/realtime/vehicles')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [
              {
                vin: 'VIN-AMAP-READY-001',
                plate: '粤A地图就绪',
                phone: '13300000001',
                oem: 'G7s',
                protocols: ['JT808'],
                sourceStatus: [{ protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }],
                sourceCount: 1,
                onlineSourceCount: 1,
                online: true,
                bindingStatus: 'bound',
                primaryProtocol: 'JT808',
                longitude: 113.2,
                latitude: 23.1,
                speedKmh: 32,
                socPercent: 76,
                totalMileageKm: 1200.5,
                lastSeen: '2026-07-03 20:12:10',
                serviceStatus: { status: 'healthy', severity: 'ok', title: '服务正常', detail: '1/1 来源在线', sourceCount: 1, onlineSourceCount: 1 }
              },
              {
                vin: 'VIN-AMAP-READY-002',
                plate: '粤A地图无坐标',
                phone: '13300000002',
                oem: 'G7s',
                protocols: ['GB32960'],
                sourceStatus: [{ protocol: 'GB32960', online: true, lastSeen: '2026-07-03 20:12:08', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }],
                sourceCount: 1,
                onlineSourceCount: 1,
                online: true,
                bindingStatus: 'bound',
                primaryProtocol: 'GB32960',
                longitude: 0,
                latitude: 0,
                speedKmh: 12,
                socPercent: 68,
                totalMileageKm: 2200.5,
                lastSeen: '2026-07-03 20:12:08',
                serviceStatus: { status: 'healthy', severity: 'ok', title: '服务正常', detail: '1/1 来源在线', sourceCount: 1, onlineSourceCount: 1 }
              }
            ],
            total: 2,
            limit: 50,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 50, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('地图展示状态')).toBeInTheDocument();
  expect(screen.getByText('Web JS Key')).toBeInTheDocument();
  expect(screen.getByText('服务端 API Key')).toBeInTheDocument();
  expect(screen.getAllByText('已配置').length).toBeGreaterThan(0);
  expect(screen.getByText('安全代理')).toBeInTheDocument();
  expect(screen.getByText('/_AMapService')).toBeInTheDocument();
  expect(screen.getByText('当前版本')).toBeInTheDocument();
  expect(screen.getByText('platform-20260704153005')).toBeInTheDocument();
  expect(screen.getByText('定位覆盖')).toBeInTheDocument();
  expect(screen.getByText('1 / 2')).toBeInTheDocument();
});

test('selects realtime map vehicle from fallback map point', async () => {
  window.history.replaceState(null, '', '/#/realtime');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/realtime/vehicles')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [
              {
                vin: 'VIN-MAP-SELECT-1',
                plate: '粤A选中1',
                phone: '',
                oem: 'G7s',
                protocols: ['GB32960'],
                sourceStatus: [{ protocol: 'GB32960', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }],
                sourceCount: 1,
                onlineSourceCount: 1,
                online: true,
                primaryProtocol: 'GB32960',
                longitude: 113.2,
                latitude: 23.1,
                speedKmh: 30,
                socPercent: 78,
                totalMileageKm: 119925,
                lastSeen: '2026-07-03 20:12:10',
                bindingStatus: 'bound',
                serviceStatus: { status: 'healthy', severity: 'ok', title: '服务正常', detail: '1/1 来源在线', sourceCount: 1, onlineSourceCount: 1 }
              },
              {
                vin: 'VIN-MAP-SELECT-2',
                plate: '粤A选中2',
                phone: '',
                oem: 'G7s',
                protocols: ['JT808'],
                sourceStatus: [{ protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:11:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }],
                sourceCount: 1,
                onlineSourceCount: 1,
                online: true,
                primaryProtocol: 'JT808',
                longitude: 113.4,
                latitude: 23.2,
                speedKmh: 42,
                socPercent: 66,
                totalMileageKm: 2200,
                lastSeen: '2026-07-03 20:11:10',
                bindingStatus: 'bound',
                serviceStatus: { status: 'healthy', severity: 'ok', title: '服务正常', detail: '1/1 来源在线', sourceCount: 1, onlineSourceCount: 1 }
              }
            ],
            total: 2,
            limit: 50,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  const selectedPanelTitle = await screen.findByText('当前选中车辆');
  const selectedPanel = selectedPanelTitle.closest('.vp-selected-vehicle-panel');
  expect(selectedPanel).not.toBeNull();
  expect(within(selectedPanel as HTMLElement).getByText('粤A选中1')).toBeInTheDocument();

  fireEvent.click(screen.getByRole('button', { name: '选择地图车辆 粤A选中2' }));

  await waitFor(() => {
    expect(within(selectedPanel as HTMLElement).getByText('粤A选中2')).toBeInTheDocument();
  });
  expect(within(selectedPanel as HTMLElement).getByText('42 km/h')).toBeInTheDocument();

  const locateButtons = screen.getAllByRole('button', { name: '地图定位' });
  fireEvent.click(locateButtons[0]);
  expect(within(selectedPanel as HTMLElement).getByText('粤A选中1')).toBeInTheDocument();
  fireEvent.click(locateButtons[1]);
  expect(within(selectedPanel as HTMLElement).getByText('粤A选中2')).toBeInTheDocument();
});

test('opens vehicle service from realtime map service queue', async () => {
  window.history.replaceState(null, '', '/#/realtime');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/realtime/vehicles')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-MAP-001',
              plate: '粤AMAP01',
              phone: '',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              sourceStatus: [
                { protocol: 'GB32960', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: false, hasRaw: false, hasMileage: false },
                { protocol: 'JT808', online: false, lastSeen: '2026-07-03 19:12:10', hasRealtime: true, hasHistory: false, hasRaw: false, hasMileage: false }
              ],
              sourceCount: 2,
              onlineSourceCount: 1,
              online: true,
              primaryProtocol: 'GB32960',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 30,
              socPercent: 78,
              totalMileageKm: 119925,
              lastSeen: '2026-07-03 20:12:10',
              bindingStatus: 'bound',
              serviceStatus: {
                status: 'degraded',
                severity: 'warning',
                title: '来源不完整',
                detail: 'JT808 离线，GB32960 仍可支撑车辆服务',
                sourceCount: 2,
                onlineSourceCount: 1
              }
            }],
            total: 1,
            limit: 50,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/overview')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN-MAP-001',
            plate: '粤AMAP01',
            sourceCount: 2,
            onlineSourceCount: 1,
            coverageStatus: 'degraded',
            primaryProtocol: 'GB32960',
            lastSeen: '2026-07-03 20:12:10',
            historyCount: 0,
            rawCount: 0,
            mileageCount: 0,
            qualityIssueCount: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('地图车辆队列')).toBeInTheDocument();
  expect(screen.getAllByText('粤AMAP01').length).toBeGreaterThan(0);
  expect(screen.getByText('JT808 离线，GB32960 仍可支撑车辆服务')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '车辆详情' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/detail?keyword=VIN-MAP-001&protocol=GB32960');
  });
});

test('opens amap coordinate and trajectory playback from realtime map service queue', async () => {
  window.history.replaceState(null, '', '/#/realtime');
  const openSpy = vi.spyOn(window, 'open').mockImplementation(() => null);
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/realtime/vehicles')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-MAP-LINK',
              plate: '粤AMAP02',
              phone: '',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              sourceStatus: [
                { protocol: 'GB32960', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
                { protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:11:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }
              ],
              sourceCount: 2,
              onlineSourceCount: 2,
              online: true,
              primaryProtocol: 'GB32960',
              longitude: 113.24567,
              latitude: 23.12345,
              speedKmh: 30,
              socPercent: 78,
              totalMileageKm: 119925,
              lastSeen: '2026-07-03 20:12:10',
              bindingStatus: 'bound',
              serviceStatus: {
                status: 'healthy',
                severity: 'ok',
                title: '服务正常',
                detail: '2/2 来源在线',
                sourceCount: 2,
                onlineSourceCount: 2
              }
            }],
            total: 1,
            limit: 50,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('地图车辆队列')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '高德坐标' }));
  expect(openSpy).toHaveBeenCalledWith(
    expect.stringContaining('https://uri.amap.com/marker?position=113.24567,23.12345'),
    '_blank',
    'noopener,noreferrer'
  );

  fireEvent.click(screen.getAllByRole('button', { name: '轨迹回放' })[0]);
  expect(window.location.hash.startsWith('#/history?')).toBe(true);
  const historyParams = new URLSearchParams(window.location.hash.slice('#/history?'.length));
  expect(historyParams.get('keyword')).toBe('VIN-MAP-LINK');
  expect(historyParams.get('protocol')).toBe('GB32960');
  expect(historyParams.get('dateFrom')).toBeTruthy();
  expect(historyParams.get('dateTo')).toBeTruthy();
});

test('surfaces multi-source realtime consistency issues with quality drilldown', async () => {
  window.history.replaceState(null, '', '/#/realtime');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/realtime/vehicles')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-RT-CONSISTENCY',
              plate: '粤A一致监控',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808', 'YUTONG_MQTT'],
              sourceStatus: [
                { protocol: 'GB32960', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
                { protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:12:05', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
                { protocol: 'YUTONG_MQTT', online: false, lastSeen: '2026-07-03 20:01:40', hasRealtime: true, hasHistory: false, hasRaw: true, hasMileage: false }
              ],
              sourceCount: 3,
              onlineSourceCount: 2,
              online: true,
              primaryProtocol: 'GB32960',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 30,
              socPercent: 78,
              totalMileageKm: 119925,
              lastSeen: '2026-07-03 20:12:10',
              bindingStatus: 'bound',
              serviceStatus: {
                status: 'degraded',
                severity: 'warning',
                title: '来源不完整',
                detail: 'YUTONG_MQTT 离线，GB32960/JT808 仍可支撑车辆服务',
                sourceCount: 3,
                onlineSourceCount: 2
              }
            }],
            total: 1,
            limit: 50,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: { issueVehicleCount: 0, issueRecordCount: 0, errorCount: 0, warningCount: 0, protocols: [], issueTypes: [] },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: { items: [], total: 0, limit: 50, offset: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('需关注车辆')).toBeInTheDocument();
  expect(screen.getAllByText('粤A一致监控').length).toBeGreaterThan(0);
  expect(screen.getByText('处置说明：YUTONG_MQTT 离线，GB32960/JT808 仍可支撑车辆服务')).toBeInTheDocument();
  expect(screen.getByText('问题：YUTONG_MQTT 离线')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '查看告警' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/alert-events?keyword=VIN-RT-CONSISTENCY&protocol=GB32960');
  });
});

test('opens quality issues from realtime vehicle row with source evidence', async () => {
  window.history.replaceState(null, '', '/#/realtime?protocol=JT808');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/realtime/vehicles')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-RT-QUALITY',
              plate: '粤A质量1',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['JT808'],
              sourceStatus: [
                { protocol: 'JT808', online: false, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: false }
              ],
              sourceCount: 1,
              onlineSourceCount: 0,
              online: false,
              primaryProtocol: 'JT808',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 0,
              socPercent: 70,
              totalMileageKm: 100,
              lastSeen: '2026-07-03 20:12:10',
              bindingStatus: 'bound',
              serviceStatus: {
                status: 'offline',
                severity: 'error',
                title: '车辆离线',
                detail: 'JT808 离线',
                sourceCount: 1,
                onlineSourceCount: 0
              }
            }],
            total: 1,
            limit: 50,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            issueVehicleCount: 1,
            issueRecordCount: 1,
            errorCount: 1,
            warningCount: 0,
            protocols: [{ name: 'JT808', count: 1 }],
            issueTypes: [{ name: 'LINK_GAP', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-RT-QUALITY',
              plate: '粤A质量1',
              phone: '13307795425',
              sourceEndpoint: '115.29.187.205:808',
              protocol: 'JT808',
              issueType: 'LINK_GAP',
              severity: 'error',
              title: 'JT808 离线',
              detail: '实时来源不可用',
              firstSeen: '2026-07-03 20:12:10',
              lastSeen: '2026-07-03 20:12:10'
            }],
            total: 1,
            limit: 50,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            linkHealth: [],
            kafkaLag: 0,
            activeConnections: 1,
            capacityFindings: [],
            redisOnlineKeys: 1,
            tdengineWritable: true,
            mysqlWritable: true,
            runtime: { requestTimeoutMs: 0 }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect((await screen.findAllByText('VIN-RT-QUALITY')).length).toBeGreaterThan(0);
  fireEvent.click(screen.getAllByRole('button', { name: '告警事件' })[0]);

  await waitFor(() => {
    expect(window.location.hash).toBe('#/alert-events?keyword=VIN-RT-QUALITY&protocol=JT808');
  });
  expect(await screen.findByRole('heading', { name: '告警事件' })).toBeInTheDocument();
});

test('loads realtime vehicles from shareable source filter hash', async () => {
  window.history.replaceState(null, '', '/#/realtime?keyword=%E7%B2%A4ART002&protocol=JT808&online=online&serviceStatus=degraded');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/realtime/vehicles')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-RT-FILTERED',
              plate: '粤ART002',
              phone: '',
              oem: 'G7s',
              protocols: ['JT808'],
              sourceCount: 1,
              onlineSourceCount: 1,
              online: true,
              primaryProtocol: 'JT808',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 30,
              socPercent: 78,
              totalMileageKm: 119925,
              lastSeen: '2026-07-03 20:12:10',
              bindingStatus: 'bound'
            }],
            total: 1,
            limit: 50,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect((await screen.findAllByText('VIN-RT-FILTERED')).length).toBeGreaterThan(0);
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/realtime/vehicles?limit=50&offset=0&keyword=%E7%B2%A4ART002&protocol=JT808&online=online&serviceStatus=degraded'), undefined);
});

test('opens realtime status from quality issue row with source evidence', async () => {
  window.history.replaceState(null, '', '/#/alert-events');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return {
        ok: true,
        json: async () => ({
          data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            issueVehicleCount: 1,
            issueRecordCount: 1,
            errorCount: 1,
            warningCount: 0,
            protocols: [{ name: 'JT808', count: 1 }],
            issueTypes: [{ name: 'VIN_MISSING', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: '',
              plate: '粤AG18312',
              phone: '13307795425',
              sourceEndpoint: '115.231.168.135:43625',
              protocol: 'JT808',
              issueType: 'VIN_MISSING',
              severity: 'error',
              lastSeen: '2026-07-03 20:12:10',
              detail: 'JT808 数据缺少 VIN 映射'
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/realtime/vehicles')) {
      return {
        ok: true,
        json: async () => ({
          data: { items: [], total: 0, limit: 50, offset: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('13307795425')).toBeInTheDocument();
  expect(screen.getByText('客户告警通知总控')).toBeInTheDocument();
  expect(screen.getByText('把影响车辆、通知对象、SLA升级、证据闭环和恢复验收收敛成客户可执行的告警服务链路。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户告警通知总控 影响车辆 1 辆 复制影响' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户告警通知总控 通知对象 接入运维 + 业务责任人 复制通知' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户告警通知总控 SLA升级 30 分钟升级 通知闭环' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户告警通知总控 恢复验收 有标准 复制回执' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: /复制告警总控包/ })).toBeInTheDocument();
  expect(screen.getByText('告警处置结论栏')).toBeInTheDocument();
  expect(screen.getByText('先判断客户受影响车辆，再确认通知、SLA、证据和恢复回执，避免告警停留在技术表格里。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '告警处置结论栏 影响范围 1 辆 复制影响' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '告警处置结论栏 通知策略 P0 1 / P1 0 复制通知' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '告警处置结论栏 SLA策略 立即处置 通知闭环' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '告警处置结论栏 证据闭环 可复核 复制交接' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '告警处置结论栏 恢复回执 有标准 复制回执' })).toBeInTheDocument();
  expect(screen.getByText('客户告警服务台')).toBeInTheDocument();
  expect(screen.getByText('客户先看到哪些车受影响、是否要通知、证据是否齐全、恢复后如何验收。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户告警服务台 影响车辆 1 辆 复制影响' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户告警服务台 待通知 P0 1 / P1 0 复制通知' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户告警服务台 焦点车辆 粤AG18312 / 13307795425 车辆服务' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户告警服务台 证据闭环 可复核 复制交接' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户告警服务台 恢复验收 有标准 复制回执' })).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: /复制告警总控包/ }));
  await waitFor(() => {
    expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【客户告警通知总控包】'));
  });
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('服务链路：影响车辆 -> 通知对象 -> SLA升级 -> 证据闭环 -> 恢复验收'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('影响范围：1 辆 / 1 条'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('优先级：P0 1 / P1 0'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('主通知策略：P0 实时中断 / 接入运维 + 业务责任人 / 站内告警 / 邮件 / 企业微信 / 30 分钟升级'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('焦点车辆：粤AG18312 / 13307795425'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('通知闭环：http://localhost:3000/#/notification-rules'));
  fireEvent.click(screen.getAllByRole('button', { name: '实时状态' })[0]);

  await waitFor(() => {
    expect(window.location.hash).toBe('#/realtime?keyword=%E7%B2%A4AG18312&protocol=JT808');
  });
  expect(await screen.findByRole('heading', { name: '实时监控' })).toBeInTheDocument();
});

test('shows and clears current realtime service filters', async () => {
  window.history.replaceState(null, '', '/#/realtime?keyword=%E7%B2%A4ART002&protocol=JT808&online=online&serviceStatus=degraded');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/realtime/vehicles')) {
      return {
        ok: true,
        json: async () => ({
          data: { items: [], total: 1, limit: 50, offset: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('当前实时筛选')).toBeInTheDocument();
  expect(screen.getByText('关键词：粤ART002')).toBeInTheDocument();
  expect(screen.getAllByText('数据通道：JT808').length).toBeGreaterThan(0);
  expect(screen.getByText('在线：在线')).toBeInTheDocument();
  expect(screen.getByText('服务状态：数据通道不完整')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '清空筛选' }));

  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/realtime/vehicles?limit=50&offset=0'), undefined);
  });
  expect(window.location.hash).toBe('#/realtime');
});

test('updates realtime hash when source filters are submitted', async () => {
  window.history.replaceState(null, '', '/#/realtime?protocol=JT808');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/realtime/vehicles')) {
      return {
        ok: true,
        json: async () => ({
          data: { items: [], total: 0, limit: 50, offset: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  await screen.findByRole('heading', { name: '实时监控' });
  fireEvent.change(screen.getByPlaceholderText('VIN / 车牌 / 手机号 / OEM'), { target: { value: '粤ART002' } });
  fireEvent.click(screen.getByRole('button', { name: '查询' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/realtime?keyword=%E7%B2%A4ART002&protocol=JT808');
  });
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/realtime/vehicles?limit=50&offset=0&keyword=%E7%B2%A4ART002&protocol=JT808'), undefined);
});

test('shows vehicle service status in vehicle list', async () => {
  window.history.replaceState(null, '', '/#/vehicles');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicles/coverage')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-DEGRADED-001',
              plate: '粤AG18312',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              sourceCount: 1,
              onlineSourceCount: 1,
              online: true,
              lastSeen: '2026-07-03 20:12:10',
              bindingStatus: 'bound',
              serviceStatus: {
                status: 'degraded',
                severity: 'warning',
                title: '来源不完整',
                detail: '由车辆服务 API 统一判定',
                sourceCount: 2,
                onlineSourceCount: 1
              }
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }
          : { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('VIN-DEGRADED-001')).toBeInTheDocument();
  expect(screen.getByText('车辆服务状态')).toBeInTheDocument();
  expect(screen.getAllByText('来源不完整').length).toBeGreaterThan(0);
});

test('shows source consistency diagnosis in vehicle service list', async () => {
  window.history.replaceState(null, '', '/#/vehicles');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicles/coverage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1,
            onlineVehicles: 1,
            singleSourceVehicles: 0,
            multiSourceVehicles: 1,
            noDataVehicles: 0,
            unboundVehicles: 0,
            missingSources: []
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/coverage')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-CONSISTENCY-LIST',
              plate: '粤A一致',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              missingProtocols: ['YUTONG_MQTT'],
              sourceCount: 2,
              onlineSourceCount: 2,
              online: true,
              lastSeen: '2026-07-03 20:12:10',
              bindingStatus: 'bound',
              sourceConsistency: {
                sourceCount: 2,
                onlineSourceCount: 2,
                locatedSourceCount: 0,
                missingProtocols: ['YUTONG_MQTT'],
                mileageDeltaKm: 0,
                sourceTimeDeltaSeconds: 0,
                scope: 'coverage',
                status: 'degraded',
                severity: 'warning',
                title: '来源不完整',
                detail: '2/2 个来源在线，车辆服务可用但缺少 YUTONG_MQTT 来源。'
              }
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }
          : { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('VIN-CONSISTENCY-LIST')).toBeInTheDocument();
  expect(screen.getByText('来源一致性')).toBeInTheDocument();
  expect(screen.getByText('来源不完整')).toBeInTheDocument();
});

test('filters vehicle list from source consistency diagnosis', async () => {
  window.history.replaceState(null, '', '/#/vehicles');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicles/coverage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1,
            onlineVehicles: 1,
            singleSourceVehicles: 0,
            multiSourceVehicles: 1,
            noDataVehicles: 0,
            unboundVehicles: 0,
            missingSources: [{ protocol: 'YUTONG_MQTT', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/coverage')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-CONSISTENCY-ACTION',
              plate: '粤A动作',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              missingProtocols: ['YUTONG_MQTT'],
              sourceCount: 2,
              onlineSourceCount: 2,
              online: true,
              lastSeen: '2026-07-03 20:12:10',
              bindingStatus: 'bound',
              sourceConsistency: {
                sourceCount: 2,
                onlineSourceCount: 2,
                locatedSourceCount: 0,
                missingProtocols: ['YUTONG_MQTT'],
                mileageDeltaKm: 0,
                sourceTimeDeltaSeconds: 0,
                scope: 'coverage',
                status: 'degraded',
                severity: 'warning',
                title: '来源不完整',
                detail: '2/2 个来源在线，车辆服务可用但缺少 YUTONG_MQTT 来源。'
              }
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }
          : { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('VIN-CONSISTENCY-ACTION')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '来源不完整' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/vehicles?serviceStatus=degraded&missingProtocol=YUTONG_MQTT');
  });
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('serviceStatus=degraded'), undefined);
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('missingProtocol=YUTONG_MQTT'), undefined);
});

test('filters vehicle list from single-source consistency diagnosis', async () => {
  window.history.replaceState(null, '', '/#/vehicles');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicles/coverage/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            totalVehicles: 1,
            onlineVehicles: 1,
            singleSourceVehicles: 1,
            multiSourceVehicles: 0,
            noDataVehicles: 0,
            unboundVehicles: 0,
            missingSources: [{ protocol: 'JT808', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicles/coverage')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-SINGLE-SOURCE',
              plate: '粤A单源',
              phone: '',
              oem: '',
              protocols: ['GB32960'],
              missingProtocols: ['JT808', 'YUTONG_MQTT'],
              sourceCount: 1,
              onlineSourceCount: 1,
              online: true,
              lastSeen: '2026-07-04 07:15:28',
              bindingStatus: 'bound',
              sourceConsistency: {
                sourceCount: 1,
                onlineSourceCount: 1,
                locatedSourceCount: 0,
                missingProtocols: ['JT808', 'YUTONG_MQTT'],
                mileageDeltaKm: 0,
                sourceTimeDeltaSeconds: 0,
                scope: 'coverage',
                status: 'single_source',
                severity: 'warning',
                title: '单一来源',
                detail: '当前车辆只有一个数据来源，无法做跨来源一致性校验。'
              }
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }
          : { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('VIN-SINGLE-SOURCE')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '单一来源' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/vehicles?serviceStatus=degraded&missingProtocol=JT808');
  });
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('missingProtocol=JT808'), undefined);
});

test('opens vehicle service from source-filtered vehicle list with source evidence', async () => {
  window.history.replaceState(null, '', '/#/vehicles?protocol=JT808');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicles/coverage')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-LIST-JT808',
              plate: '粤A列表1',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['JT808'],
              sourceCount: 1,
              onlineSourceCount: 1,
              online: true,
              lastSeen: '2026-07-03 20:12:10',
              bindingStatus: 'bound'
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/vehicle-service/overview')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN-LIST-JT808',
            plate: '粤A列表1',
            sourceCount: 1,
            onlineSourceCount: 1,
            coverageStatus: 'online',
            primaryProtocol: 'JT808',
            lastSeen: '2026-07-03 20:12:10',
            historyCount: 0,
            rawCount: 0,
            mileageCount: 0,
            qualityIssueCount: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }
          : { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('VIN-LIST-JT808')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '服务' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/detail?keyword=VIN-LIST-JT808&protocol=JT808');
  });
});

test('opens realtime and trajectory from vehicle center row', async () => {
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicles/coverage')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-LIST-WORKFLOW',
              plate: '粤A列表流',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              missingProtocols: [],
              sourceStatus: [
                { protocol: 'GB32960', online: false, lastSeen: '2026-07-03 18:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
                { protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }
              ],
              sourceCount: 2,
              onlineSourceCount: 1,
              online: true,
              lastSeen: '2026-07-03 20:12:10',
              bindingStatus: 'bound'
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }
          : { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  window.history.replaceState(null, '', '/#/vehicles');
  render(<App />);

  expect(await screen.findByText('VIN-LIST-WORKFLOW')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '实时' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/realtime?keyword=VIN-LIST-WORKFLOW&protocol=JT808');
  });

  cleanup();
  window.history.replaceState(null, '', '/#/vehicles');
  render(<App />);

  expect(await screen.findByText('VIN-LIST-WORKFLOW')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '轨迹' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/history?keyword=VIN-LIST-WORKFLOW&protocol=JT808');
  });
});

test('opens quality issues from source-filtered vehicle list with source evidence', async () => {
  window.history.replaceState(null, '', '/#/vehicles?protocol=JT808');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicles/coverage')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-LIST-QUALITY',
              plate: '粤A列表质',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['JT808'],
              sourceCount: 1,
              onlineSourceCount: 0,
              online: false,
              lastSeen: '2026-07-03 20:12:10',
              bindingStatus: 'bound',
              serviceStatus: {
                status: 'offline',
                severity: 'error',
                title: '车辆离线',
                detail: 'JT808 来源离线',
                sourceCount: 1,
                onlineSourceCount: 0
              }
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            issueVehicleCount: 1,
            issueRecordCount: 1,
            errorCount: 1,
            warningCount: 0,
            protocols: [{ name: 'JT808', count: 1 }],
            issueTypes: [{ name: 'LINK_GAP', count: 1 }]
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    if (path.includes('/api/quality/issues') || path.includes('/api/alert-events?')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            items: [{
              vin: 'VIN-LIST-QUALITY',
              plate: '粤A列表质',
              phone: '13307795425',
              sourceEndpoint: '115.231.168.135:43625',
              protocol: 'JT808',
              issueType: 'LINK_GAP',
              severity: 'error',
              lastSeen: '2026-07-03 20:12:10',
              detail: 'JT808 来源离线'
            }],
            total: 1,
            limit: 20,
            offset: 0
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }
          : { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('VIN-LIST-QUALITY')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '告警' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/alert-events?keyword=VIN-LIST-QUALITY&protocol=JT808');
  });
  expect(await screen.findByRole('heading', { name: '告警事件' })).toBeInTheDocument();
});

test('filters vehicle list by service status', async () => {
  window.history.replaceState(null, '', '/#/vehicles');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicles/coverage')) {
      return {
        ok: true,
        json: async () => ({
          data: { items: [], total: 0, limit: 20, offset: 0 },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }
          : { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  await screen.findByRole('heading', { name: '车辆服务' });
  fireEvent.click(screen.getByTestId('service-status-filter'));
  fireEvent.click(await screen.findByText('来源不完整'));
  fireEvent.click(screen.getByRole('button', { name: '查询' }));

  await waitFor(() => {
    expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('serviceStatus=degraded'), undefined);
  });
});

test('switches vehicle detail to a source from the source matrix', async () => {
  window.history.replaceState(null, '', '/#/detail?keyword=VIN001');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN001',
            lookupKey: 'VIN001',
            lookupResolved: true,
            resolution: {
              lookupKey: 'VIN001',
              resolved: true,
              vin: 'VIN001',
              plate: '粤AG18312',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808', 'YUTONG_MQTT'],
              online: true,
              lastSeen: '2026-07-03 20:12:10'
            },
            realtimeSummary: {
              vin: 'VIN001',
              plate: '粤AG18312',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808', 'YUTONG_MQTT'],
              sourceCount: 3,
              onlineSourceCount: 2,
              online: true,
              primaryProtocol: 'GB32960',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 30,
              socPercent: 78,
              totalMileageKm: 100,
              lastSeen: '2026-07-03 20:12:10'
            },
            sources: ['GB32960', 'JT808', 'YUTONG_MQTT'],
            sourceStatus: [
              { protocol: 'GB32960', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
              { protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:12:08', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
              { protocol: 'YUTONG_MQTT', online: false, lastSeen: '', hasRealtime: false, hasHistory: false, hasRaw: false, hasMileage: false }
            ],
            realtime: [
              {
                vin: 'VIN001',
                plate: '粤AG18312',
                protocol: 'GB32960',
                longitude: 113.2,
                latitude: 23.1,
                speedKmh: 30,
                socPercent: 78,
                totalMileageKm: 100,
                lastSeen: '2026-07-03 20:12:10'
              },
              {
                vin: 'VIN001',
                plate: '粤AG18312',
                protocol: 'JT808',
                longitude: 113.21,
                latitude: 23.11,
                speedKmh: 28,
                socPercent: 0,
                totalMileageKm: 99.5,
                lastSeen: '2026-07-03 20:12:08'
              }
            ],
            history: { items: [], total: 0, limit: 20, offset: 0 },
            raw: { items: [], total: 0, limit: 10, offset: 0 },
            mileage: { items: [], total: 0, limit: 20, offset: 0 },
            quality: { items: [], total: 0, limit: 20, offset: 0 }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect((await screen.findAllByText('VIN001')).length).toBeGreaterThan(0);
  expect(screen.getAllByText('GB32960 在线').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('JT808 在线').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('YUTONG_MQTT 未接入').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('数据通道归并矩阵')).toBeInTheDocument();
  expect(screen.getByText('主实时通道')).toBeInTheDocument();
  expect(screen.getAllByText('定位数据').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('里程口径').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('无实时字段')).toBeInTheDocument();
  expect(screen.getByText('无历史数据')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '复制归并矩阵' }));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('【单车数据通道归并矩阵】'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('1. GB32960 / 主数据通道'));
  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('贡献：主实时通道、在线心跳、实时字段、定位数据、轨迹回放、历史数据、里程口径'));
  expect(screen.getByText('车辆服务角色')).toBeInTheDocument();
  expect(screen.getAllByText('主数据通道').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('在线数据通道').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('暂无来源').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('最新位置')).toBeInTheDocument();
  expect(screen.getAllByText('113.2, 23.1').length).toBeGreaterThan(0);
  expect(screen.getAllByText(/100\s*km/).length).toBeGreaterThan(0);
  expect(screen.getAllByText(/30\s*km\/h/).length).toBeGreaterThan(0);
  expect(screen.getAllByText('YUTONG_MQTT').length).toBeGreaterThan(0);
  expect(screen.getAllByText('无上报').length).toBeGreaterThan(0);
  fireEvent.click(screen.getByRole('button', { name: '仅看 JT808' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/detail?keyword=VIN001&protocol=JT808');
  });
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/vehicle-service?keyword=VIN001&protocol=JT808'), undefined);
});

test('shows unified service overview on vehicle detail', async () => {
  window.history.replaceState(null, '', '/#/detail?keyword=VIN001');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN001',
            lookupKey: 'VIN001',
            lookupResolved: true,
            serviceOverview: {
              vin: 'VIN001',
              plate: '粤AG18312',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808', 'YUTONG_MQTT'],
              primaryProtocol: 'GB32960',
              coverageStatus: 'partial',
              sourceCount: 3,
              onlineSourceCount: 2,
              lastSeen: '2026-07-03 20:12:10',
              realtimeCount: 3,
              historyCount: 12,
              rawCount: 34,
              mileageCount: 7,
              qualityIssueCount: 1
            },
            serviceStatus: {
              status: 'degraded',
              severity: 'warning',
              title: '来源不完整',
              detail: '2/3 个来源在线',
              sourceCount: 3,
              onlineSourceCount: 2
            },
            sources: ['GB32960', 'JT808', 'YUTONG_MQTT'],
            sourceStatus: [],
            realtime: [],
            history: { items: [], total: 12, limit: 20, offset: 0 },
            raw: { items: [], total: 34, limit: 10, offset: 0 },
            mileage: { items: [], total: 7, limit: 20, offset: 0 },
            quality: { items: [], total: 1, limit: 20, offset: 0 }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }
          : { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('车辆服务概览')).toBeInTheDocument();
  expect(screen.getByText('车辆服务档案总览')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: /复制档案/ })).toBeInTheDocument();
  expect(screen.getByText('今日单车服务任务板')).toBeInTheDocument();
  expect(screen.getByText('看实时位置')).toBeInTheDocument();
  expect(screen.getByText('回放轨迹证据')).toBeInTheDocument();
  expect(screen.getAllByText('查询历史数据').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('复核里程统计')).toBeInTheDocument();
  expect(screen.getByText('闭环告警事件')).toBeInTheDocument();
  expect(screen.getByText('单车服务任务台')).toBeInTheDocument();
  expect(screen.getByText('单车服务处理清单')).toBeInTheDocument();
  expect(screen.getAllByText('档案完整度').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('实时')).toBeInTheDocument();
  expect(screen.getAllByText('轨迹').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('历史数据').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('里程').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('告警').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('实时定位')).toBeInTheDocument();
  expect(screen.getAllByText('轨迹回放').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('历史数据').length).toBeGreaterThan(0);
  expect(screen.getByText('告警处置')).toBeInTheDocument();
  expect(screen.getAllByText('里程复核').length).toBeGreaterThan(0);
  expect(screen.getByText('确认实时状态')).toBeInTheDocument();
  expect(screen.getByText('回放轨迹断点')).toBeInTheDocument();
  expect(screen.getAllByText('查询历史数据').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('处理告警闭环')).toBeInTheDocument();
  expect(screen.getByText('复核里程口径')).toBeInTheDocument();
  expect(screen.getByText('车辆在线状态、最新时间、有效坐标均可解释。')).toBeInTheDocument();
  expect(screen.getByText('2026-07-03 20:12:10')).toBeInTheDocument();
  expect(screen.getAllByText('12 条历史').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('34 帧').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('1 项').length).toBeGreaterThan(0);
  expect(screen.getAllByText('7 条统计').length).toBeGreaterThanOrEqual(1);
  expect(screen.getByText('2/3')).toBeInTheDocument();
  expect(screen.getByText('轨迹 12 / 历史数据 34 / 里程 7')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '单车任务 看实时位置 实时监控' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '单车任务 回放轨迹证据 轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '单车任务 查询历史数据 历史数据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '单车任务 复核里程统计 里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '单车任务 闭环告警事件 告警事件' })).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '单车服务任务台 历史数据 查看历史数据' }));
  expect(window.location.hash).toBe('#/history-query?keyword=VIN001&tab=raw');
});

test('opens quality governance from vehicle detail overview', async () => {
  window.history.replaceState(null, '', '/#/detail?keyword=VIN001');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN001',
            lookupKey: 'VIN001',
            lookupResolved: true,
            serviceOverview: {
              vin: 'VIN001',
              plate: '粤AG18312',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              primaryProtocol: 'GB32960',
              coverageStatus: 'partial',
              sourceCount: 2,
              onlineSourceCount: 1,
              lastSeen: '2026-07-03 20:12:10',
              realtimeCount: 2,
              historyCount: 12,
              rawCount: 34,
              mileageCount: 7,
              qualityIssueCount: 3
            },
            sources: ['GB32960', 'JT808'],
            sourceStatus: [],
            realtime: [],
            history: { items: [], total: 12, limit: 20, offset: 0 },
            raw: { items: [], total: 34, limit: 10, offset: 0 },
            mileage: { items: [], total: 7, limit: 20, offset: 0 },
            quality: { items: [], total: 3, limit: 20, offset: 0 }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }
          : path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')
            ? { issueVehicleCount: 1, issueRecordCount: 3, errorCount: 0, warningCount: 3, protocols: [], issueTypes: [] }
            : { items: [], total: 3, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('车辆服务概览')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '查看质量问题 3 项' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/alert-events?keyword=VIN001');
  });
  expect(await screen.findByRole('heading', { name: '告警事件' })).toBeInTheDocument();
  expect(screen.getByText('告警事件闭环')).toBeInTheDocument();
  expect(screen.getByText('事件触发')).toBeInTheDocument();
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/alert-events?keyword=VIN001&limit=20&offset=0'), undefined);
});

test('quality health storage card stays pending before ops health loads', async () => {
  window.history.replaceState(null, '', '/#/alert-events');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/ops/health')) {
      return new Promise<Response>(() => undefined);
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')
          ? { issueVehicleCount: 0, issueRecordCount: 0, errorCount: 0, warningCount: 0, protocols: [], issueTypes: [] }
          : { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByRole('heading', { name: '告警事件' })).toBeInTheDocument();
  expect(screen.getByText('存储读取')).toBeInTheDocument();
  expect(screen.getByText('检测中')).toBeInTheDocument();
  expect(screen.queryByText('异常')).not.toBeInTheDocument();
});

test('quality health shows active connection capacity metric', async () => {
  window.history.replaceState(null, '', '/#/alert-events');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, activeConnections: 120000, redisOnlineKeys: 368, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000, platformRelease: 'platform-20260704153710' } }
          : path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')
            ? { issueVehicleCount: 0, issueRecordCount: 0, errorCount: 0, warningCount: 0, protocols: [], issueTypes: [] }
            : { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByRole('heading', { name: '告警事件' })).toBeInTheDocument();
  expect(await screen.findByText('活跃连接')).toBeInTheDocument();
  expect(screen.getByText('120,000')).toBeInTheDocument();
  expect(screen.getByText('运行版本')).toBeInTheDocument();
  expect(screen.getByText('platform-20260704153710')).toBeInTheDocument();
});

test('quality health shows structured capacity findings', async () => {
  window.history.replaceState(null, '', '/#/alert-events');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 42, activeConnections: 120000, capacityFindings: ['kafka lag 42'], redisOnlineKeys: 368, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }
          : path.includes('/api/quality/summary') || path.includes('/api/alert-events/summary')
            ? { issueVehicleCount: 0, issueRecordCount: 0, errorCount: 0, warningCount: 0, protocols: [], issueTypes: [] }
            : { items: [], total: 0, limit: 20, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByRole('heading', { name: '告警事件' })).toBeInTheDocument();
  expect(await screen.findByText('容量检查发现')).toBeInTheDocument();
  expect(screen.getByText('kafka lag 42')).toBeInTheDocument();
});

test('shows vehicle service data chain before source details', async () => {
  window.history.replaceState(null, '', '/#/detail?keyword=VIN001');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN001',
            lookupKey: 'VIN001',
            lookupResolved: true,
            serviceOverview: {
              vin: 'VIN001',
              plate: '粤AG18312',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808', 'YUTONG_MQTT'],
              primaryProtocol: 'GB32960',
              coverageStatus: 'partial',
              sourceCount: 3,
              onlineSourceCount: 2,
              lastSeen: '2026-07-03 20:12:10',
              realtimeCount: 3,
              historyCount: 12,
              rawCount: 34,
              mileageCount: 7,
              qualityIssueCount: 1
            },
            serviceStatus: {
              status: 'degraded',
              severity: 'warning',
              title: '来源不完整',
              detail: '2/3 个来源在线',
              sourceCount: 3,
              onlineSourceCount: 2
            },
            sourceConsistency: {
              sourceCount: 3,
              onlineSourceCount: 2,
              locatedSourceCount: 2,
              mileageDeltaKm: 1.4,
              sourceTimeDeltaSeconds: 125,
              scope: 'vehicle',
              status: 'degraded',
              severity: 'warning',
              title: '来源不完整',
              detail: 'MQTT 离线，32960 与 808 仍可支撑车辆服务'
            },
            sources: ['GB32960', 'JT808', 'YUTONG_MQTT'],
            sourceStatus: [
              { protocol: 'GB32960', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
              { protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:12:08', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
              { protocol: 'YUTONG_MQTT', online: false, lastSeen: '2026-07-03 20:10:05', hasRealtime: true, hasHistory: false, hasRaw: true, hasMileage: false }
            ],
            realtime: [],
            history: { items: [], total: 12, limit: 20, offset: 0 },
            raw: { items: [], total: 34, limit: 10, offset: 0 },
            mileage: { items: [], total: 7, limit: 20, offset: 0 },
            quality: { items: [], total: 1, limit: 20, offset: 0 }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }
          : { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('车辆服务数据链')).toBeInTheDocument();
  expect(screen.getAllByText('2/3 在线').length).toBeGreaterThan(0);
  expect(screen.getAllByText('2 个通道有位置').length).toBeGreaterThan(0);
  expect(screen.getAllByText('1.4 km').length).toBeGreaterThan(0);
  expect(screen.getAllByText('125 秒').length).toBeGreaterThan(0);
  expect(screen.getAllByText('MQTT 离线，32960 与 808 仍可支撑车辆服务').length).toBeGreaterThan(0);
});

test('shows unified latest location posture on vehicle detail', async () => {
  window.history.replaceState(null, '', '/#/detail?keyword=VIN-MAP-001');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN-MAP-001',
            lookupKey: 'VIN-MAP-001',
            lookupResolved: true,
            serviceOverview: {
              vin: 'VIN-MAP-001',
              plate: '粤A地图1',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808', 'YUTONG_MQTT'],
              primaryProtocol: 'GB32960',
              coverageStatus: 'partial',
              sourceCount: 3,
              onlineSourceCount: 2,
              lastSeen: '2026-07-03 20:12:10',
              realtimeCount: 3,
              historyCount: 12,
              rawCount: 34,
              mileageCount: 7,
              qualityIssueCount: 0
            },
            serviceStatus: {
              status: 'degraded',
              severity: 'warning',
              title: '来源不完整',
              detail: '2/3 个来源在线',
              sourceCount: 3,
              onlineSourceCount: 2
            },
            sources: ['GB32960', 'JT808', 'YUTONG_MQTT'],
            sourceStatus: [
              { protocol: 'GB32960', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
              { protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:12:08', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
              { protocol: 'YUTONG_MQTT', online: false, lastSeen: '2026-07-03 20:10:05', hasRealtime: false, hasHistory: false, hasRaw: true, hasMileage: false }
            ],
            realtime: [
              { vin: 'VIN-MAP-001', plate: '粤A地图1', protocol: 'GB32960', longitude: 113.2, latitude: 23.1, speedKmh: 36, socPercent: 76, totalMileageKm: 1200.5, lastSeen: '2026-07-03 20:12:10' },
              { vin: 'VIN-MAP-001', plate: '粤A地图1', protocol: 'JT808', longitude: 113.21, latitude: 23.11, speedKmh: 35, socPercent: 0, totalMileageKm: 1200.2, lastSeen: '2026-07-03 20:12:08' }
            ],
            history: { items: [], total: 12, limit: 20, offset: 0 },
            raw: { items: [], total: 34, limit: 10, offset: 0 },
            mileage: { items: [], total: 7, limit: 20, offset: 0 },
            quality: { items: [], total: 0, limit: 20, offset: 0 }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }
          : { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('车辆位置态势')).toBeInTheDocument();
  expect(screen.getByText('2 个来源有最新位置')).toBeInTheDocument();
  expect(screen.getByText('GB32960 / 36 km/h')).toBeInTheDocument();
  expect(screen.getByText('JT808 / 35 km/h')).toBeInTheDocument();
  expect(screen.getByText('高德地图未配置，显示车辆服务坐标预览')).toBeInTheDocument();
});

test('copies shareable vehicle service detail link', async () => {
  window.history.replaceState(null, '', '/#/detail?keyword=VIN001&protocol=JT808');
  const writeText = vi.fn(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN001',
            lookupKey: 'VIN001',
            lookupResolved: true,
            sources: ['GB32960', 'JT808'],
            sourceStatus: [],
            realtime: [],
            history: { items: [], total: 0, limit: 20, offset: 0 },
            raw: { items: [], total: 0, limit: 10, offset: 0 },
            mileage: { items: [], total: 0, limit: 20, offset: 0 },
            quality: { items: [], total: 0, limit: 20, offset: 0 }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }
          : { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  fireEvent.click(await screen.findByRole('button', { name: /复制服务链接/ }));

  expect(writeText).toHaveBeenCalledWith(expect.stringContaining('/#/detail?keyword=VIN001&protocol=JT808'));
});

test('copies vehicle service diagnostic summary', async () => {
  window.history.replaceState(null, '', '/#/detail?keyword=VIN-SUMMARY-001&protocol=JT808');
  const writeText = vi.fn<(value: string) => Promise<void>>(() => Promise.resolve());
  Object.defineProperty(navigator, 'clipboard', {
    configurable: true,
    value: { writeText }
  });
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service')) {
      const serviceStatus = {
        status: 'degraded',
        severity: 'warning',
        title: '来源不完整',
        detail: 'JT808 在线，GB32960 离线',
        sourceCount: 2,
        onlineSourceCount: 1
      };
      const sourceConsistency = {
        sourceCount: 2,
        onlineSourceCount: 1,
        locatedSourceCount: 2,
        missingProtocols: ['YUTONG_MQTT'],
        mileageDeltaKm: 12.6,
        sourceTimeDeltaSeconds: 90,
        scope: 'all_sources',
        status: 'degraded',
        severity: 'warning',
        title: '来源不完整',
        detail: 'JT808 在线，GB32960 离线，需要补齐 MQTT'
      };
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN-SUMMARY-001',
            lookupKey: 'VIN-SUMMARY-001',
            lookupResolved: true,
            identity: {
              vin: 'VIN-SUMMARY-001',
              plate: '粤A诊断1',
              phone: '13307795425',
              oem: 'G7s',
              online: true,
              lastSeen: '2026-07-03 20:12:10'
            },
            realtimeSummary: {
              vin: 'VIN-SUMMARY-001',
              plate: '粤A诊断1',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['JT808'],
              sourceCount: 2,
              onlineSourceCount: 1,
              online: true,
              primaryProtocol: 'JT808',
              lastSeen: '2026-07-03 20:12:10'
            },
            serviceOverview: {
              vin: 'VIN-SUMMARY-001',
              plate: '粤A诊断1',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              primaryProtocol: 'JT808',
              coverageStatus: 'partial',
              sourceCount: 2,
              onlineSourceCount: 1,
              lastSeen: '2026-07-03 20:12:10',
              realtimeCount: 2,
              historyCount: 12,
              rawCount: 34,
              mileageCount: 3,
              qualityIssueCount: 1
            },
            sources: ['GB32960', 'JT808'],
            sourceStatus: [
              { protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
              { protocol: 'GB32960', online: false, lastSeen: '2026-07-03 19:55:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }
            ],
            realtime: [
              { vin: 'VIN-SUMMARY-001', plate: '粤A诊断1', protocol: 'JT808', longitude: 113.2, latitude: 23.1, speedKmh: 32, totalMileageKm: 1200.5, lastSeen: '2026-07-03 20:12:10' }
            ],
            history: { items: [], total: 12, limit: 20, offset: 0 },
            raw: { items: [], total: 34, limit: 10, offset: 0 },
            mileage: { items: [], total: 3, limit: 20, offset: 0 },
            quality: {
              items: [
                { vin: 'VIN-SUMMARY-001', plate: '粤A诊断1', protocol: 'JT808', issueType: 'LINK_GAP', severity: 'warning', lastSeen: '2026-07-03 20:12:10', detail: 'JT808 间断' }
              ],
              total: 1,
              limit: 20,
              offset: 0
            },
            serviceStatus,
            sourceConsistency
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000, platformRelease: 'platform-20260704155844' } }
          : { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('车辆服务总览')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: /复制归一摘要/ })).toBeInTheDocument();
  expect(screen.getByText('车辆身份')).toBeInTheDocument();
  expect(screen.getByText('实时主数据通道')).toBeInTheDocument();
  expect(screen.getByText('业务可用性')).toBeInTheDocument();
  expect(await screen.findByText('车辆服务结论')).toBeInTheDocument();
  expect(screen.getByText('单车 Live View')).toBeInTheDocument();
  expect(screen.getByText('客户打开一辆车时，先看当前位置、最后上报、速度、SOC、总里程和告警，再进入轨迹回放、里程统计、数据导出和告警闭环。')).toBeInTheDocument();
  expect(screen.getByText('客户单车操作台')).toBeInTheDocument();
  expect(screen.getByText('围绕这辆车直接完成看车、回放、统计、导出和告警通知。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户单车操作台 看当前位置 113.2, 23.1 实时地图' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户单车操作台 回放轨迹 12 条 轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户单车操作台 核对里程 3 条 里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户单车操作台 导出证据 34 条 历史导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户单车操作台 告警通知 1 项 告警闭环' })).toBeInTheDocument();
  expect(screen.getByText('当前位置')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '单车实时视图 实时位置 查看实时' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '单车实时视图 数据导出 历史导出' })).toBeInTheDocument();
  expect(screen.getByText('客户车辆档案摘要')).toBeInTheDocument();
  expect(screen.getByText('把 VIN、车牌、在线状态、实时位置、轨迹、历史数据、告警和统计放在同一个单车入口，协议来源只作为可追溯证据。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆档案摘要 服务状态 查看实时' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆档案摘要 轨迹证据 轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆档案摘要 历史证据 历史查询' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆档案摘要 告警通知 告警闭环' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '车辆档案摘要 里程口径 里程统计' })).toBeInTheDocument();
  expect(screen.getByText('客户常问')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '单车客户常问 这辆车现在在哪里？ 实时监控' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '单车客户常问 最近是否还在线？ 查看实时' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '单车客户常问 能不能回放轨迹？ 轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '单车客户常问 这辆车跑了多少？ 里程统计' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '单车客户常问 数据能导出吗？ 历史导出' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '单车客户常问 是否需要通知处理？ 告警通知' })).toBeInTheDocument();
  expect(screen.getByText('车辆服务档案总览')).toBeInTheDocument();
  expect(screen.getByText('单车客户服务交付包')).toBeInTheDocument();
  expect(screen.getByText('面向客户交付时，以车辆为主对象提供实时监控、轨迹回放、历史数据查询、告警通知和里程统计，协议来源只作为可追溯证据。')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户服务交付 实时监控 打开监控' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户服务交付 轨迹回放 回放轨迹' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户服务交付 历史数据查询 查询历史' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户服务交付 告警通知 告警闭环' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '客户服务交付 里程统计 里程复核' })).toBeInTheDocument();
  expect(screen.getAllByText('单车时间窗交付').length).toBeGreaterThan(0);
  expect(screen.getByText('把同一辆车和同一段时间带入轨迹回放、里程统计、历史导出和告警通知，客户复盘时不需要跨页面重复输入。')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: /复制归一摘要/ }));
  await waitFor(() => {
    expect(writeText.mock.calls.some((call) => String(call[0]).includes('【统一车辆服务摘要】'))).toBe(true);
  });
  const unifiedCopied = String(writeText.mock.lastCall?.[0] ?? '');
  expect(unifiedCopied).toContain('【统一车辆服务摘要】');
  expect(unifiedCopied).toContain('车辆：粤A诊断1 / VIN-SUMMARY-001');
  expect(unifiedCopied).toContain('当前范围：单一数据通道：JT808');
  expect(unifiedCopied).toContain('车辆身份：VIN-SUMMARY-001');
  expect(unifiedCopied).toContain('服务状态：数据通道不完整');
  expect(unifiedCopied).toContain('实时主数据通道：JT808');
  expect(unifiedCopied).toContain('通道在线：1/2');
  expect(unifiedCopied).toContain('业务可用性：在线可用');
  expect(unifiedCopied).toContain('服务数据：实时 2 / 轨迹 12 / 历史数据 34 / 里程 3 / 告警 1');
  expect(unifiedCopied).toContain('车辆服务：http://localhost:3000/#/detail?keyword=VIN-SUMMARY-001&protocol=JT808');
  fireEvent.click(screen.getByRole('button', { name: /复制档案/ }));
  await waitFor(() => {
    expect(writeText.mock.calls.some((call) => String(call[0]).includes('【车辆服务档案】'))).toBe(true);
  });
  const dossierCopied = String(writeText.mock.lastCall?.[0] ?? '');
  expect(dossierCopied).toContain('【车辆服务档案】');
  expect(dossierCopied).toContain('车辆：粤A诊断1 / VIN-SUMMARY-001');
  expect(dossierCopied).toContain('当前范围：单一数据通道：JT808');
  expect(dossierCopied).toContain('主数据通道：JT808');
  expect(dossierCopied).toContain('通道在线：1/2');
  expect(dossierCopied).toContain('资产概览：实时 2 / 轨迹 12 / 历史数据 34 / 里程 3 / 告警 1');
  expect(dossierCopied).toContain('车辆服务：http://localhost:3000/#/detail?keyword=VIN-SUMMARY-001&protocol=JT808');
  const customerPackageButtons = screen.getAllByRole('button', { name: /复制交付包/ });
  fireEvent.click(customerPackageButtons[customerPackageButtons.length - 1]);
  await waitFor(() => {
    expect(writeText.mock.calls.some((call) => String(call[0]).includes('【单车客户服务交付包】'))).toBe(true);
  });
  const customerPackageCopied = String(writeText.mock.lastCall?.[0] ?? '');
  expect(customerPackageCopied).toContain('【单车客户服务交付包】');
  expect(customerPackageCopied).toContain('车辆：粤A诊断1 / VIN-SUMMARY-001');
  expect(customerPackageCopied).toContain('平台能力：实时监控 / 轨迹回放 / 历史数据查询 / 告警通知 / 里程统计');
  expect(customerPackageCopied).toContain('实时监控：http://localhost:3000/#/realtime?keyword=VIN-SUMMARY-001&protocol=JT808');
  expect(customerPackageCopied).toContain('轨迹回放：http://localhost:3000/#/history?keyword=VIN-SUMMARY-001&protocol=JT808');
  expect(customerPackageCopied).toContain('历史数据查询：http://localhost:3000/#/history-query?keyword=VIN-SUMMARY-001&protocol=JT808&tab=raw&includeFields=true');
  expect(customerPackageCopied).toContain('告警通知：http://localhost:3000/#/alert-events?keyword=VIN-SUMMARY-001&protocol=JT808');
  expect(customerPackageCopied).toContain('里程统计：http://localhost:3000/#/mileage?keyword=VIN-SUMMARY-001&protocol=JT808');
  fireEvent.click(screen.getByRole('button', { name: /复制诊断摘要/ }));

  await waitFor(() => {
    expect(writeText).toHaveBeenCalledWith(expect.any(String));
  });
  const copied = String(writeText.mock.lastCall?.[0] ?? '');
  expect(copied).toContain('【车辆服务诊断摘要】');
  expect(copied).toContain('运行版本：platform-20260704155844');
  expect(copied).toContain('车辆：粤A诊断1 / VIN-SUMMARY-001');
  expect(copied).toContain('当前范围：单一数据通道：JT808');
  expect(copied).toContain('服务状态：数据通道不完整');
  expect(copied).toContain('数据通道：1/2 在线');
  expect(copied).toContain('定位数据：2 个通道有位置');
  expect(copied).toContain('里程差异：12.6 km');
  expect(copied).toContain('时间差异：90 秒');
  expect(copied).toContain('服务数据：实时 2 / 轨迹 12 / 历史数据 34 / 里程 3 / 告警 1');
  expect(copied).toContain('一致性：数据通道不完整');
  expect(copied).toContain('下一步：');
  expect(copied).toContain('车辆服务：http://localhost:3000/#/detail?keyword=VIN-SUMMARY-001&protocol=JT808');
  expect(copied).toContain('实时：http://localhost:3000/#/realtime?keyword=VIN-SUMMARY-001&protocol=JT808');
  expect(copied).toContain('轨迹：http://localhost:3000/#/history?keyword=VIN-SUMMARY-001&protocol=JT808');
  expect(copied).toContain('历史数据：http://localhost:3000/#/history-query?keyword=VIN-SUMMARY-001&protocol=JT808&tab=raw&includeFields=true');
  expect(copied).toContain('里程统计：http://localhost:3000/#/mileage?keyword=VIN-SUMMARY-001&protocol=JT808');

  fireEvent.click(screen.getByRole('button', { name: /复制服务摘要/ }));
  await waitFor(() => {
    expect(writeText.mock.calls.some((call) => String(call[0]).includes('【车辆服务摘要】'))).toBe(true);
  });
  const operationsCopied = String(writeText.mock.lastCall?.[0] ?? '');
  expect(operationsCopied).toContain('【车辆服务摘要】');
  expect(operationsCopied).toContain('车辆：粤A诊断1 / VIN-SUMMARY-001');
  expect(operationsCopied).toContain('当前范围：单一数据通道：JT808');
  expect(operationsCopied).toContain('服务状态：数据通道不完整');
  expect(operationsCopied).toContain('在线数据通道：1/2');
  expect(operationsCopied).toContain('定位数据：2');
  expect(operationsCopied).toContain('质量问题：1 项');
  expect(operationsCopied).toContain('下一步清单：');
  expect(operationsCopied).toContain('补齐 YUTONG_MQTT 证据');
  expect(operationsCopied).toContain('排查 GB32960 离线');
  expect(operationsCopied).toContain('处理质量问题 1 项');
  expect(operationsCopied).toContain('车辆服务：http://localhost:3000/#/detail?keyword=VIN-SUMMARY-001&protocol=JT808');
  expect(operationsCopied).toContain('实时监控：http://localhost:3000/#/realtime?keyword=VIN-SUMMARY-001&protocol=JT808');
  expect(operationsCopied).toContain('轨迹回放：http://localhost:3000/#/history?keyword=VIN-SUMMARY-001&protocol=JT808');
  expect(operationsCopied).toContain('历史数据：http://localhost:3000/#/history-query?keyword=VIN-SUMMARY-001&protocol=JT808&tab=raw&includeFields=true');
  expect(operationsCopied).toContain('里程统计：http://localhost:3000/#/mileage?keyword=VIN-SUMMARY-001&protocol=JT808');

  fireEvent.click(screen.getByRole('button', { name: '复制处理清单' }));
  await waitFor(() => {
    expect(writeText.mock.calls.some((call) => String(call[0]).includes('【单车服务处理清单】'))).toBe(true);
  });
  const runbookCopied = String(writeText.mock.lastCall?.[0] ?? '');
  expect(runbookCopied).toContain('【单车服务处理清单】');
  expect(runbookCopied).toContain('车辆：粤A诊断1 / VIN-SUMMARY-001');
  expect(runbookCopied).toContain('当前范围：单一数据通道：JT808');
  expect(runbookCopied).toContain('1. 确认实时状态');
  expect(runbookCopied).toContain('2. 回放轨迹断点');
  expect(runbookCopied).toContain('3. 查询历史数据');
  expect(runbookCopied).toContain('4. 处理告警闭环');
  expect(runbookCopied).toContain('5. 复核里程口径');
  expect(runbookCopied).toContain('验收：车辆在线状态、最新时间、有效坐标均可解释。');
  expect(runbookCopied).toContain('告警事件：http://localhost:3000/#/alert-events?keyword=VIN-SUMMARY-001&protocol=JT808');
  expect(runbookCopied).toContain('里程统计：http://localhost:3000/#/mileage?keyword=VIN-SUMMARY-001&protocol=JT808');

  fireEvent.change(screen.getByPlaceholderText('单车开始日期'), { target: { value: '2026-07-01' } });
  fireEvent.change(screen.getByPlaceholderText('单车结束日期'), { target: { value: '2026-07-03' } });
  fireEvent.click(screen.getByRole('button', { name: '单车时间窗交付 历史数据导出 2026-07-01 至 2026-07-03' }));
  expect(window.location.hash.startsWith('#/history-query?')).toBe(true);
  const windowParams = new URLSearchParams(window.location.hash.split('?')[1] ?? '');
  expect(windowParams.get('keyword')).toBe('VIN-SUMMARY-001');
  expect(windowParams.get('protocol')).toBe('JT808');
  expect(windowParams.get('dateFrom')).toBe('2026-07-01');
  expect(windowParams.get('dateTo')).toBe('2026-07-03');
  expect(windowParams.get('tab')).toBe('raw');
  expect(windowParams.get('includeFields')).toBe('true');
});

test('exports vehicle detail service dossier as csv', async () => {
  window.history.replaceState(null, '', '/#/detail?keyword=VIN-REPORT-001&protocol=GB32960');
  const createObjectURL = vi.spyOn(URL, 'createObjectURL').mockReturnValue('blob:vehicle-report');
  const revokeObjectURL = vi.spyOn(URL, 'revokeObjectURL').mockImplementation(() => undefined);
  const click = vi.spyOn(HTMLAnchorElement.prototype, 'click').mockImplementation(() => undefined);
  const appendChild = vi.spyOn(document.body, 'appendChild');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN-REPORT-001',
            lookupKey: 'VIN-REPORT-001',
            lookupResolved: true,
            serviceOverview: {
              vin: 'VIN-REPORT-001',
              plate: '粤A档案导出',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808', 'YUTONG_MQTT'],
              primaryProtocol: 'GB32960',
              coverageStatus: 'partial',
              sourceCount: 3,
              onlineSourceCount: 2,
              lastSeen: '2026-07-03 20:12:10',
              realtimeCount: 3,
              historyCount: 12,
              rawCount: 34,
              mileageCount: 7,
              qualityIssueCount: 1
            },
            serviceStatus: {
              status: 'degraded',
              severity: 'warning',
              title: '来源不完整',
              detail: '2/3 个来源在线',
              sourceCount: 3,
              onlineSourceCount: 2
            },
            sourceConsistency: {
              sourceCount: 3,
              onlineSourceCount: 2,
              locatedSourceCount: 2,
              missingProtocols: ['YUTONG_MQTT'],
              mileageDeltaKm: 1.4,
              sourceTimeDeltaSeconds: 125,
              scope: 'vehicle',
              status: 'degraded',
              severity: 'warning',
              title: '来源不完整',
              detail: 'MQTT 离线，32960 与 808 仍可支撑车辆服务'
            },
            sources: ['GB32960', 'JT808', 'YUTONG_MQTT'],
            sourceStatus: [
              { protocol: 'GB32960', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
              { protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:12:08', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
              { protocol: 'YUTONG_MQTT', online: false, lastSeen: '2026-07-03 20:10:05', hasRealtime: true, hasHistory: false, hasRaw: true, hasMileage: false }
            ],
            realtime: [],
            history: { items: [], total: 12, limit: 20, offset: 0 },
            raw: { items: [], total: 34, limit: 10, offset: 0 },
            mileage: { items: [], total: 7, limit: 20, offset: 0 },
            quality: { items: [], total: 1, limit: 20, offset: 0 }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }
          : { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('车辆服务结论')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: /导出档案 CSV/ }));

  expect(createObjectURL).toHaveBeenCalled();
  expect(click).toHaveBeenCalled();
  expect(revokeObjectURL).toHaveBeenCalledWith('blob:vehicle-report');
  const link = appendChild.mock.calls.find(([node]) => node instanceof HTMLAnchorElement)?.[0] as HTMLAnchorElement | undefined;
  expect(link?.download).toBe('vehicle-service-VIN-REPORT-001-GB32960.csv');
});

test('opens history with the currently selected vehicle detail source', async () => {
  window.history.replaceState(null, '', '/#/detail?keyword=VIN001');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN001',
            lookupKey: 'VIN001',
            lookupResolved: true,
            resolution: {
              lookupKey: 'VIN001',
              resolved: true,
              vin: 'VIN001',
              plate: '粤AG18312',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              online: true,
              lastSeen: '2026-07-03 20:12:10'
            },
            realtimeSummary: {
              vin: 'VIN001',
              plate: '粤AG18312',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              sourceCount: 2,
              onlineSourceCount: 2,
              online: true,
              primaryProtocol: 'GB32960',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 30,
              socPercent: 78,
              totalMileageKm: 100,
              lastSeen: '2026-07-03 20:12:10'
            },
            sources: ['GB32960', 'JT808'],
            sourceStatus: [
              { protocol: 'GB32960', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
              { protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:12:08', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }
            ],
            realtime: [],
            history: { items: [], total: 0, limit: 20, offset: 0 },
            raw: { items: [], total: 0, limit: 10, offset: 0 },
            mileage: { items: [], total: 0, limit: 20, offset: 0 },
            quality: { items: [], total: 0, limit: 20, offset: 0 }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  fireEvent.click(await screen.findByRole('button', { name: '仅看 JT808' }));
  fireEvent.click(screen.getAllByRole('button', { name: '轨迹回放' })[0]);

  await waitFor(() => {
    expect(window.location.hash).toBe('#/history?keyword=VIN001&protocol=JT808');
  });
});

test('opens raw history with the currently selected vehicle detail source', async () => {
  window.history.replaceState(null, '', '/#/detail?keyword=VIN001');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN001',
            lookupKey: 'VIN001',
            lookupResolved: true,
            resolution: {
              lookupKey: 'VIN001',
              resolved: true,
              vin: 'VIN001',
              plate: '粤AG18312',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              online: true,
              lastSeen: '2026-07-03 20:12:10'
            },
            realtimeSummary: {
              vin: 'VIN001',
              plate: '粤AG18312',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              sourceCount: 2,
              onlineSourceCount: 2,
              online: true,
              primaryProtocol: 'GB32960',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 30,
              socPercent: 78,
              totalMileageKm: 100,
              lastSeen: '2026-07-03 20:12:10'
            },
            sources: ['GB32960', 'JT808'],
            sourceStatus: [
              { protocol: 'GB32960', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
              { protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:12:08', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }
            ],
            realtime: [],
            history: { items: [], total: 0, limit: 20, offset: 0 },
            raw: { items: [], total: 0, limit: 10, offset: 0 },
            mileage: { items: [], total: 0, limit: 20, offset: 0 },
            quality: { items: [], total: 0, limit: 20, offset: 0 }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  fireEvent.click(await screen.findByRole('button', { name: '仅看 JT808' }));
  fireEvent.click(screen.getAllByRole('button', { name: '历史数据' })[0]);

  await waitFor(() => {
    expect(window.location.hash).toBe('#/history-query?keyword=VIN001&protocol=JT808&tab=raw');
  });
});

test('opens realtime with the currently selected vehicle detail source', async () => {
  window.history.replaceState(null, '', '/#/detail?keyword=VIN001');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN001',
            lookupKey: 'VIN001',
            lookupResolved: true,
            resolution: {
              lookupKey: 'VIN001',
              resolved: true,
              vin: 'VIN001',
              plate: '粤AG18312',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              online: true,
              lastSeen: '2026-07-03 20:12:10'
            },
            realtimeSummary: {
              vin: 'VIN001',
              plate: '粤AG18312',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              sourceCount: 2,
              onlineSourceCount: 2,
              online: true,
              primaryProtocol: 'GB32960',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 30,
              socPercent: 78,
              totalMileageKm: 100,
              lastSeen: '2026-07-03 20:12:10'
            },
            sources: ['GB32960', 'JT808'],
            sourceStatus: [
              { protocol: 'GB32960', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
              { protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:12:08', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }
            ],
            realtime: [],
            history: { items: [], total: 0, limit: 20, offset: 0 },
            raw: { items: [], total: 0, limit: 10, offset: 0 },
            mileage: { items: [], total: 0, limit: 20, offset: 0 },
            quality: { items: [], total: 0, limit: 20, offset: 0 }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  fireEvent.click(await screen.findByRole('button', { name: '仅看 JT808' }));
  fireEvent.click(screen.getByRole('button', { name: '查看实时' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/realtime?keyword=VIN001&protocol=JT808');
  });
});

test('opens source-specific history directly from vehicle detail source card', async () => {
  window.history.replaceState(null, '', '/#/detail?keyword=VIN001');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN001',
            lookupKey: 'VIN001',
            lookupResolved: true,
            resolution: {
              lookupKey: 'VIN001',
              resolved: true,
              vin: 'VIN001',
              plate: '粤AG18312',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              online: true,
              lastSeen: '2026-07-03 20:12:10'
            },
            realtimeSummary: {
              vin: 'VIN001',
              plate: '粤AG18312',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              sourceCount: 2,
              onlineSourceCount: 2,
              online: true,
              primaryProtocol: 'GB32960',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 30,
              socPercent: 78,
              totalMileageKm: 100,
              lastSeen: '2026-07-03 20:12:10'
            },
            sources: ['GB32960', 'JT808'],
            sourceStatus: [
              { protocol: 'GB32960', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
              { protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:12:08', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }
            ],
            realtime: [],
            history: { items: [], total: 0, limit: 20, offset: 0 },
            raw: { items: [], total: 0, limit: 10, offset: 0 },
            mileage: { items: [], total: 0, limit: 20, offset: 0 },
            quality: { items: [], total: 0, limit: 20, offset: 0 }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  fireEvent.click(await screen.findByRole('button', { name: '查看 JT808 轨迹' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/history?keyword=VIN001&protocol=JT808');
  });
});

test('opens source-specific raw history directly from vehicle detail source data channel', async () => {
  window.history.replaceState(null, '', '/#/detail?keyword=VIN001');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN001',
            lookupKey: 'VIN001',
            lookupResolved: true,
            resolution: {
              lookupKey: 'VIN001',
              resolved: true,
              vin: 'VIN001',
              plate: '粤AG18312',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              online: true,
              lastSeen: '2026-07-03 20:12:10'
            },
            realtimeSummary: {
              vin: 'VIN001',
              plate: '粤AG18312',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              sourceCount: 2,
              onlineSourceCount: 2,
              online: true,
              primaryProtocol: 'GB32960',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 30,
              socPercent: 78,
              totalMileageKm: 100,
              lastSeen: '2026-07-03 20:12:10'
            },
            sources: ['GB32960', 'JT808'],
            sourceStatus: [
              { protocol: 'GB32960', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
              { protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:12:08', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }
            ],
            realtime: [],
            history: { items: [], total: 0, limit: 20, offset: 0 },
            raw: { items: [], total: 0, limit: 10, offset: 0 },
            mileage: { items: [], total: 0, limit: 20, offset: 0 },
            quality: { items: [], total: 0, limit: 20, offset: 0 }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  fireEvent.click(await screen.findByRole('button', { name: '查看 JT808 历史数据' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/history-query?keyword=VIN001&protocol=JT808&tab=raw');
  });
});

test('opens vehicle service from an empty history query with current source evidence', async () => {
  window.history.replaceState(null, '', '/#/history?keyword=VIN404&protocol=JT808');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicles/resolve')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            lookupKey: 'VIN404',
            resolved: true,
            vin: 'VIN404',
            plate: '',
            phone: '',
            oem: '',
            protocols: ['JT808'],
            online: false,
            lastSeen: ''
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }
          : { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  fireEvent.click(await screen.findByRole('button', { name: '当前车辆服务' }));

  await waitFor(() => {
    expect(window.location.hash).toBe('#/detail?keyword=VIN404&protocol=JT808');
  });
});

test('shows selected source scope on vehicle detail', async () => {
  window.history.replaceState(null, '', '/#/detail?keyword=VIN001&protocol=JT808');
  const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN001',
            lookupKey: 'VIN001',
            lookupResolved: true,
            resolution: {
              lookupKey: 'VIN001',
              resolved: true,
              vin: 'VIN001',
              plate: '粤AG18312',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              online: true,
              lastSeen: '2026-07-03 20:12:10'
            },
            realtimeSummary: {
              vin: 'VIN001',
              plate: '粤AG18312',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808'],
              sourceCount: 2,
              onlineSourceCount: 1,
              online: true,
              primaryProtocol: 'JT808',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 30,
              socPercent: 78,
              totalMileageKm: 100,
              lastSeen: '2026-07-03 20:12:10'
            },
            sources: ['GB32960', 'JT808'],
            sourceStatus: [
              { protocol: 'GB32960', online: false, lastSeen: '2026-07-03 20:11:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
              { protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }
            ],
            serviceStatus: {
              status: 'degraded',
              severity: 'warning',
              title: '来源不完整',
              detail: '1/2 个来源在线，车辆服务可用但需要关注离线来源。',
              sourceCount: 2,
              onlineSourceCount: 1
            },
            realtime: [],
            history: { items: [], total: 0, limit: 20, offset: 0 },
            raw: { items: [], total: 0, limit: 10, offset: 0 },
            mileage: { items: [], total: 0, limit: 20, offset: 0 },
            quality: { items: [], total: 0, limit: 20, offset: 0 }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }
          : { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('当前查看范围')).toBeInTheDocument();
  expect(screen.getAllByText('单一数据通道：JT808').length).toBeGreaterThan(0);
  expect(screen.getByText('车辆服务状态')).toBeInTheDocument();
  expect(screen.getAllByText('数据通道不完整').length).toBeGreaterThan(0);
  expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/vehicle-service?keyword=VIN001&protocol=JT808'), undefined);
});

test('shows cross-source consistency for one vehicle service', async () => {
  window.history.replaceState(null, '', '/#/detail?keyword=VIN-CONSISTENCY-001');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN-CONSISTENCY-001',
            lookupKey: 'VIN-CONSISTENCY-001',
            lookupResolved: true,
            resolution: {
              lookupKey: 'VIN-CONSISTENCY-001',
              resolved: true,
              vin: 'VIN-CONSISTENCY-001',
              plate: '粤A一致1',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808', 'YUTONG_MQTT'],
              online: true,
              lastSeen: '2026-07-03 20:12:10'
            },
            realtimeSummary: {
              vin: 'VIN-CONSISTENCY-001',
              plate: '粤A一致1',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808', 'YUTONG_MQTT'],
              sourceCount: 3,
              onlineSourceCount: 2,
              online: true,
              bindingStatus: 'bound',
              primaryProtocol: 'GB32960',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 32,
              socPercent: 78,
              totalMileageKm: 1000.5,
              lastSeen: '2026-07-03T20:12:10+08:00'
            },
            sourceConsistency: {
              sourceCount: 3,
              onlineSourceCount: 2,
              locatedSourceCount: 3,
              missingProtocols: ['YUTONG_MQTT'],
              mileageDeltaKm: 0.4,
              sourceTimeDeltaSeconds: 35,
              scope: 'all_sources',
              status: 'degraded',
              severity: 'warning',
              title: '来源不完整',
              detail: '2/3 个来源在线，车辆服务可用但缺少 YUTONG_MQTT 来源。'
            },
            sources: ['GB32960', 'JT808', 'YUTONG_MQTT'],
            sourceStatus: [
              { protocol: 'GB32960', online: true, lastSeen: '2026-07-03T20:12:10+08:00', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
              { protocol: 'JT808', online: true, lastSeen: '2026-07-03T20:12:05+08:00', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
              { protocol: 'YUTONG_MQTT', online: false, lastSeen: '2026-07-03T20:11:40+08:00', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }
            ],
            realtime: [
              { vin: 'VIN-CONSISTENCY-001', plate: '粤A一致1', protocol: 'GB32960', longitude: 113.2, latitude: 23.1, speedKmh: 32, socPercent: 78, totalMileageKm: 1000.5, lastSeen: '2026-07-03T20:12:10+08:00' },
              { vin: 'VIN-CONSISTENCY-001', plate: '粤A一致1', protocol: 'JT808', longitude: 113.201, latitude: 23.101, speedKmh: 30, socPercent: 0, totalMileageKm: 1000.8, lastSeen: '2026-07-03T20:12:05+08:00' },
              { vin: 'VIN-CONSISTENCY-001', plate: '粤A一致1', protocol: 'YUTONG_MQTT', longitude: 113.2, latitude: 23.1, speedKmh: 31, socPercent: 77, totalMileageKm: 1000.7, lastSeen: '2026-07-03T20:11:40+08:00' }
            ],
            history: { items: [], total: 0, limit: 20, offset: 0 },
            raw: { items: [], total: 0, limit: 10, offset: 0 },
            mileage: { items: [], total: 0, limit: 20, offset: 0 },
            quality: { items: [], total: 0, limit: 20, offset: 0 }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }
          : { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('跨证据一致性')).toBeInTheDocument();
  expect(screen.getAllByText('数据通道不完整').length).toBeGreaterThanOrEqual(1);
  expect(screen.getAllByText('2/3 个来源在线，车辆服务可用但缺少 YUTONG_MQTT 来源。').length).toBeGreaterThan(0);
  expect(screen.getByText('3 个通道')).toBeInTheDocument();
  expect(screen.getAllByText('2/3 在线').length).toBeGreaterThan(0);
  expect(screen.getByText('缺失通道')).toBeInTheDocument();
  expect(screen.getAllByText('3 个通道有位置').length).toBeGreaterThan(0);
  expect(screen.getAllByText('0.4 km').length).toBeGreaterThan(0);
  expect(screen.getAllByText('35 秒').length).toBeGreaterThan(0);
  fireEvent.click(screen.getByRole('button', { name: '查看缺 YUTONG_MQTT 车辆' }));
  expect(window.location.hash).toBe('#/vehicles?serviceStatus=degraded&missingProtocol=YUTONG_MQTT');
});

test('shows actionable vehicle service recommendations on detail', async () => {
  window.history.replaceState(null, '', '/#/detail?keyword=VIN-ACTION-001');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN-ACTION-001',
            lookupKey: 'VIN-ACTION-001',
            lookupResolved: true,
            resolution: {
              lookupKey: 'VIN-ACTION-001',
              resolved: true,
              vin: 'VIN-ACTION-001',
              plate: '粤A处置1',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808', 'YUTONG_MQTT'],
              online: true,
              lastSeen: '2026-07-03T20:12:10+08:00'
            },
            realtimeSummary: {
              vin: 'VIN-ACTION-001',
              plate: '粤A处置1',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['GB32960', 'JT808', 'YUTONG_MQTT'],
              sourceCount: 3,
              onlineSourceCount: 2,
              online: true,
              bindingStatus: 'bound',
              primaryProtocol: 'GB32960',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 32,
              socPercent: 78,
              totalMileageKm: 1000.5,
              lastSeen: '2026-07-03T20:12:10+08:00'
            },
            serviceStatus: {
              status: 'degraded',
              severity: 'warning',
              title: '来源不完整',
              detail: '2/3 个来源在线，车辆服务可用但缺少 YUTONG_MQTT 来源。',
              sourceCount: 3,
              onlineSourceCount: 2
            },
            sourceConsistency: {
              sourceCount: 3,
              onlineSourceCount: 2,
              locatedSourceCount: 2,
              missingProtocols: ['YUTONG_MQTT'],
              mileageDeltaKm: 0.4,
              sourceTimeDeltaSeconds: 35,
              scope: 'all_sources',
              status: 'degraded',
              severity: 'warning',
              title: '来源不完整',
              detail: '2/3 个来源在线，车辆服务可用但缺少 YUTONG_MQTT 来源。'
            },
            sources: ['GB32960', 'JT808', 'YUTONG_MQTT'],
            sourceStatus: [
              { protocol: 'GB32960', online: true, lastSeen: '2026-07-03T20:12:10+08:00', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
              { protocol: 'JT808', online: true, lastSeen: '2026-07-03T20:12:05+08:00', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
              { protocol: 'YUTONG_MQTT', online: false, lastSeen: '2026-07-03T20:11:40+08:00', hasRealtime: true, hasHistory: false, hasRaw: true, hasMileage: false }
            ],
            realtime: [],
            history: { items: [], total: 0, limit: 20, offset: 0 },
            raw: { items: [], total: 2, limit: 10, offset: 0 },
            mileage: { items: [], total: 0, limit: 20, offset: 0 },
            quality: { items: [], total: 3, limit: 20, offset: 0 }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }
          : { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('车辆服务结论')).toBeInTheDocument();
  expect(screen.getByText('降级可服务')).toBeInTheDocument();
  expect(screen.getByText('3 项质量问题影响可信度')).toBeInTheDocument();
  expect(screen.getByText('先处理质量问题，再将该车用于 BI、告警或对外查询。')).toBeInTheDocument();
  expect(await screen.findByText('服务处置建议')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '补齐 YUTONG_MQTT 证据' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '处理质量问题 3 项' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '排查 YUTONG_MQTT 离线' })).toBeInTheDocument();

  fireEvent.click(screen.getByRole('button', { name: '补齐 YUTONG_MQTT 证据' }));
  expect(window.location.hash).toBe('#/vehicles?serviceStatus=degraded&missingProtocol=YUTONG_MQTT');
});

test('opens single vehicle quality evidence from vehicle detail', async () => {
  window.history.replaceState(null, '', '/#/detail?keyword=VIN-DETAIL-QUALITY&protocol=JT808');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN-DETAIL-QUALITY',
            lookupKey: 'VIN-DETAIL-QUALITY',
            lookupResolved: true,
            resolution: {
              lookupKey: 'VIN-DETAIL-QUALITY',
              resolved: true,
              vin: 'VIN-DETAIL-QUALITY',
              plate: '粤A详情告警',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['JT808'],
              online: true,
              lastSeen: '2026-07-03T20:12:10+08:00'
            },
            realtimeSummary: {
              vin: 'VIN-DETAIL-QUALITY',
              plate: '粤A详情告警',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['JT808'],
              sourceCount: 1,
              onlineSourceCount: 1,
              online: true,
              primaryProtocol: 'JT808',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 32,
              socPercent: 0,
              totalMileageKm: 1000.5,
              lastSeen: '2026-07-03T20:12:10+08:00'
            },
            serviceOverview: {
              vin: 'VIN-DETAIL-QUALITY',
              plate: '粤A详情告警',
              phone: '13307795425',
              oem: 'G7s',
              protocols: ['JT808'],
              primaryProtocol: 'JT808',
              coverageStatus: 'online',
              sourceCount: 1,
              onlineSourceCount: 1,
              lastSeen: '2026-07-03T20:12:10+08:00',
              realtimeCount: 1,
              historyCount: 8,
              rawCount: 16,
              mileageCount: 2,
              qualityIssueCount: 1
            },
            sources: ['JT808'],
            sourceStatus: [
              { protocol: 'JT808', online: true, lastSeen: '2026-07-03T20:12:10+08:00', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }
            ],
            realtime: [],
            history: { items: [], total: 8, limit: 20, offset: 0 },
            raw: { items: [], total: 16, limit: 10, offset: 0 },
            mileage: { items: [], total: 2, limit: 20, offset: 0 },
            quality: {
              items: [
                {
                  vin: 'VIN-DETAIL-QUALITY',
                  plate: '粤A详情告警',
                  phone: '13307795425',
                  protocol: 'JT808',
                  issueType: 'VIN_MISSING',
                  severity: 'error',
                  lastSeen: '2026-07-03 20:12:10',
                  detail: '详情页质量问题需要处理',
                  sourceEndpoint: '115.231.168.135:43625'
                }
              ],
              total: 1,
              limit: 20,
              offset: 0
            }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }
          : { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect(await screen.findByText('单车处置建议')).toBeInTheDocument();
  expect(screen.getAllByText('VIN 缺失').length).toBeGreaterThan(0);
  expect(screen.getAllByText('详情页质量问题需要处理').length).toBeGreaterThan(0);
  expect(screen.getAllByText('粤A详情告警 / VIN-DETAIL-QUALITY').length).toBeGreaterThan(0);

  fireEvent.click(screen.getByRole('button', { name: '单车告警轨迹证据' }));
  await waitFor(() => {
    expect(window.location.hash).toBe('#/history?keyword=VIN-DETAIL-QUALITY&protocol=JT808&dateFrom=2026-07-03&dateTo=2026-07-04');
  });
});

test('shows canonical vehicle archive on detail', async () => {
  window.history.replaceState(null, '', '/#/detail?keyword=VIN-ARCHIVE-001');
  vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
    const path = String(input);
    if (path.includes('/api/vehicle-service')) {
      return {
        ok: true,
        json: async () => ({
          data: {
            vin: 'VIN-ARCHIVE-001',
            lookupKey: 'VIN-ARCHIVE-001',
            lookupResolved: true,
            resolution: {
              lookupKey: 'VIN-ARCHIVE-001',
              resolved: true,
              vin: 'VIN-ARCHIVE-001',
              plate: '粤A档案1',
              phone: '',
              oem: '',
              protocols: ['GB32960', 'JT808', 'YUTONG_MQTT'],
              online: true,
              lastSeen: '2026-07-03T20:12:10+08:00'
            },
            realtimeSummary: {
              vin: 'VIN-ARCHIVE-001',
              plate: '粤A档案1',
              phone: '',
              oem: '',
              protocols: ['GB32960', 'JT808', 'YUTONG_MQTT'],
              sourceCount: 3,
              onlineSourceCount: 2,
              online: true,
              bindingStatus: 'bound',
              primaryProtocol: 'GB32960',
              longitude: 113.2,
              latitude: 23.1,
              speedKmh: 32,
              socPercent: 78,
              totalMileageKm: 1000.5,
              lastSeen: '2026-07-03T20:12:10+08:00'
            },
            sources: ['GB32960', 'JT808', 'YUTONG_MQTT'],
            sourceStatus: [],
            realtime: [],
            history: { items: [], total: 0, limit: 20, offset: 0 },
            raw: { items: [], total: 0, limit: 10, offset: 0 },
            mileage: { items: [], total: 0, limit: 20, offset: 0 },
            quality: { items: [], total: 0, limit: 20, offset: 0 }
          },
          traceId: 'trace-test',
          timestamp: 1783094400000
        })
      } as Response;
    }
    return {
      ok: true,
      json: async () => ({
        data: path.includes('/api/ops/health')
          ? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }
          : { items: [], total: 0, limit: 10, offset: 0 },
        traceId: 'trace-test',
        timestamp: 1783094400000
      })
    } as Response;
  });

  render(<App />);

  expect((await screen.findAllByText('车辆档案')).length).toBeGreaterThan(0);
  expect(screen.getByText('车辆主键')).toBeInTheDocument();
  expect(screen.getAllByText('VIN-ARCHIVE-001').length).toBeGreaterThan(0);
  expect(screen.getAllByText('档案完整度').length).toBeGreaterThan(0);
  expect(screen.getAllByText('2/4').length).toBeGreaterThan(0);
  expect(screen.getAllByText('缺手机号').length).toBeGreaterThan(0);
  expect(screen.getAllByText('缺OEM').length).toBeGreaterThan(0);
  expect(screen.getByText('归并来源数')).toBeInTheDocument();
  expect(screen.getAllByText('3').length).toBeGreaterThan(0);
});
