import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
import { afterEach, expect, test, vi } from 'vitest';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import { api } from '../../api/client';
import type { VehicleDetail, VehicleRealtimeRow } from '../../api/types';
import { buildMonitorPath, withMonitorReturn } from '../routing/monitorContext';
import { ROUTER_FUTURE } from '../routing/routerConfig';
import VehiclePage from './VehiclePage';

const fleetMapVehicles = vi.hoisted(() => vi.fn());
const layout = vi.hoisted(() => ({ mobile: false }));

vi.mock('../map/FleetMap', () => ({
  FleetMap: ({ vehicles }: { vehicles: VehicleRealtimeRow[] }) => {
    fleetMapVehicles(vehicles);
    return <div data-testid="fleet-map" />;
  }
}));

vi.mock('../auth/AuthGate', () => ({
  usePlatformSession: () => ({ session: { name: 'test-viewer', role: 'viewer', authMode: 'enforce' } })
}));
vi.mock('../hooks/useMobileLayout', () => ({ useMobileLayout: () => layout.mobile }));

const initialRealtime = {
  vin: 'LTEST000000000001', plate: '粤A12345', phone: '', oem: '', protocols: ['JT808'], sourceStatus: [],
  sourceCount: 1, onlineSourceCount: 1, online: true, bindingStatus: 'bound', primaryProtocol: 'JT808',
  longitude: 113.26, latitude: 23.13, speedKmh: 20, socPercent: 80, totalMileageKm: 1000,
  lastSeen: '2026-07-16 10:00:00', reportIntervalMs: 30_000
} satisfies VehicleRealtimeRow;

const detail = {
  vin: initialRealtime.vin,
  lookupKey: initialRealtime.vin,
  lookupResolved: true,
  realtimeSummary: initialRealtime,
  sources: ['JT808'],
  sourceStatus: [],
  realtime: [],
  history: { items: [], total: 0, limit: 20, offset: 0 },
  raw: { items: [], total: 0, limit: 20, offset: 0 },
  mileage: { items: [], total: 0, limit: 20, offset: 0 },
  quality: { items: [], total: 0, limit: 20, offset: 0 }
} satisfies VehicleDetail;

afterEach(() => {
  cleanup();
  layout.mobile = false;
  vi.restoreAllMocks();
  fleetMapVehicles.mockReset();
});

test('polls lightweight single-vehicle realtime data and passes its report interval to the map', async () => {
  const movedRealtime = { ...initialRealtime, longitude: 113.28, latitude: 23.15, lastSeen: '2026-07-16 10:00:30' };
  vi.spyOn(api, 'vehicleDetail').mockResolvedValue(detail);
  const realtimeSpy = vi.spyOn(api, 'vehicleRealtime').mockResolvedValue({ items: [movedRealtime], total: 1, limit: 1, offset: 0 });
  vi.spyOn(api, 'latestTelemetry').mockResolvedValue({ values: [], categories: [], scannedFrames: 0, asOf: '2026-07-16T02:00:00Z' } as never);
  const sourceEvidence = vi.spyOn(api, 'vehicleSourceEvidence').mockResolvedValue({
    vin: initialRealtime.vin, plate: initialRealtime.plate, mileageDate: '2026-07-16',
    recommendedLocationProtocol: 'JT808', recommendedLocationLabel: 'JT808', locationConflict: false,
    locationSources: [], mileageSources: [],
    comparison: { locationMaxDistanceM: 0, totalMileageDeltaKm: 0, dailyMileageDeltaKm: 0, reportTimeDeltaSeconds: 0 },
    asOf: '2026-07-16T10:00:00+08:00'
  });
  const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });

  const view = render(<QueryClientProvider client={client}><MemoryRouter initialEntries={[`/vehicles/${initialRealtime.vin}`]} future={ROUTER_FUTURE}><Routes><Route path="/vehicles/:vin" element={<VehiclePage />} /></Routes></MemoryRouter></QueryClientProvider>);

  await waitFor(() => expect(realtimeSpy).toHaveBeenCalledTimes(1));
  const realtimeQuery = client.getQueryCache().find({ queryKey: ['vehicle-detail-realtime', initialRealtime.vin] });
  expect((realtimeQuery?.options as { refetchInterval?: number }).refetchInterval).toBe(10_000);
  const params = realtimeSpy.mock.calls[0][0];
  expect(params).toBeDefined();
  expect(params!.get('keywords')).toBe(initialRealtime.vin);
  expect(params!.get('limit')).toBe('1');
  await waitFor(() => expect(fleetMapVehicles).toHaveBeenLastCalledWith([
    expect.objectContaining({ longitude: 113.28, latitude: 23.15, reportIntervalMs: 30_000 })
  ]));
  const commandCard = view.container.querySelector('.v2-identity-band');
  expect(commandCard).toHaveClass('semi-card', 'v2-vehicle-command-card', 'v2-live-overview');
  expect(screen.getByLabelText('粤A12345 车辆档案')).toHaveClass('v2-vehicle-record-v3');
  expect(screen.getByLabelText('单车任务栏')).toHaveClass('v2-workspace-command-bar', 'v2-vehicle-record-command');
  expect(screen.getByLabelText('单车任务栏')).toHaveTextContent('粤A12345');
  expect(screen.getByLabelText('单车任务栏')).toHaveTextContent('LTEST000000000001');
  expect(screen.getByRole('list', { name: '车辆实时指标' })).toHaveClass('v2-live-grid');
  expect(commandCard?.querySelectorAll('.v2-live-grid > [role="listitem"]')).toHaveLength(6);
  expect(commandCard?.querySelector('.v2-live-grid > div:first-child')).toHaveTextContent('速度');
  expect(commandCard?.querySelector('.v2-live-grid > div:nth-child(2)')).toHaveTextContent('SOC');
  expect(commandCard?.querySelector('.v2-live-grid > div:nth-child(4)')).toHaveTextContent('当日里程');
  expect(commandCard?.querySelector('.v2-live-grid > div:nth-child(5)')).toHaveTextContent('推荐来源JT/T 808');
  expect(commandCard?.querySelector('.v2-live-primary-protocol')).toHaveClass('v2-protocol-tag', 'semi-tag-cyan-light');
  expect(commandCard?.querySelector('.v2-identity-sources .v2-protocol-tag')).toHaveClass('semi-tag-cyan-light');
  expect(screen.getByLabelText('粤A12345 车辆档案')).toHaveFocus();
  expect(screen.getByText('扫描 0 帧 · 上海时间 10:00:00')).toBeInTheDocument();
  expect(screen.getByRole('heading', { name: '最新上报', level: 5 })).toBeInTheDocument();
  expect(screen.getByRole('heading', { name: '实时位置', level: 5 })).toBeInTheDocument();
  expect(screen.getByRole('heading', { name: '最近事件', level: 5 })).toBeInTheDocument();
  expect(screen.getByRole('heading', { name: '实时遥测', level: 5 })).toBeInTheDocument();
  expect(screen.getByRole('heading', { name: '车辆主档', level: 5 })).toBeInTheDocument();
  expect(screen.getByRole('navigation', { name: '单车详情导航' })).toBeInTheDocument();
  const telemetryPanel = view.container.querySelector<HTMLElement>('#vehicle-telemetry-panel')!;
  const scrollIntoView = vi.fn();
  Object.defineProperty(telemetryPanel, 'scrollIntoView', { configurable: true, value: scrollIntoView });
  fireEvent.click(screen.getByRole('button', { name: '跳转到实时遥测' }));
  expect(scrollIntoView).toHaveBeenCalledWith({ behavior: 'smooth', block: 'start' });
  expect(telemetryPanel).toHaveFocus();
  expect(view.container.querySelector('.v2-record-descriptions.semi-descriptions')).toBeInTheDocument();
  expect(screen.getByText('待维护').closest('.semi-tag')).toBeInTheDocument();
  expect(sourceEvidence).not.toHaveBeenCalled();
  const locationSource = screen.getByRole('button', { name: '查看全部位置来源' });
  expect(locationSource).toHaveClass('semi-button', 'v2-map-source-link');
  expect(locationSource.closest('.semi-card')).toHaveClass('v2-single-map-card');
  const totalMileageSource = screen.getByRole('button', { name: '查看总里程全部来源' });
  expect(totalMileageSource).toHaveClass('semi-button', 'v2-live-source-link');
  expect(totalMileageSource.closest('.semi-card')).toHaveClass('v2-live-overview');
  expect(totalMileageSource.closest('.semi-card')).toBe(commandCard);
  fireEvent.click(locationSource);
  await waitFor(() => expect(sourceEvidence).toHaveBeenCalledTimes(1));
});

test('holds the vehicle identity at the top through late layout shifts and yields to deliberate scrolling', async () => {
  vi.spyOn(api, 'vehicleDetail').mockResolvedValue(detail);
  vi.spyOn(api, 'vehicleRealtime').mockResolvedValue({ items: [initialRealtime], total: 1, limit: 1, offset: 0 });
  vi.spyOn(api, 'latestTelemetry').mockResolvedValue({ values: [], categories: [], scannedFrames: 0 } as never);
  const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
  const workspace = document.createElement('main');
  workspace.className = 'v2-content';
  document.body.appendChild(workspace);

  render(<QueryClientProvider client={client}><MemoryRouter initialEntries={[`/vehicles/${initialRealtime.vin}`]} future={ROUTER_FUTURE}><Routes><Route path="/vehicles/:vin" element={<VehiclePage />} /></Routes></MemoryRouter></QueryClientProvider>, { container: workspace });

  await screen.findByLabelText('粤A12345 车辆档案');
  await new Promise((resolve) => window.setTimeout(resolve, 420));
  workspace.scrollTop = 312;
  fireEvent.scroll(workspace);
  await waitFor(() => expect(workspace.scrollTop).toBe(0));

  fireEvent.wheel(workspace);
  workspace.scrollTop = 188;
  fireEvent.scroll(workspace);
  await new Promise((resolve) => window.setTimeout(resolve, 32));
  expect(workspace.scrollTop).toBe(188);
  workspace.remove();
});

test('uses the shared Semi empty recovery surface when a vehicle cannot be resolved', async () => {
  vi.spyOn(api, 'vehicleDetail').mockResolvedValue({ ...detail, lookupResolved: false });
  vi.spyOn(api, 'vehicleRealtime').mockResolvedValue({ items: [], total: 0, limit: 1, offset: 0 });
  vi.spyOn(api, 'latestTelemetry').mockResolvedValue({ values: [], categories: [], scannedFrames: 0 } as never);
  const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });

  const view = render(<QueryClientProvider client={client}><MemoryRouter initialEntries={['/vehicles/UNKNOWN']} future={ROUTER_FUTURE}><Routes><Route path="/vehicles/:vin" element={<VehiclePage />} /></Routes></MemoryRouter></QueryClientProvider>);

  expect(await screen.findByText('未找到车辆')).toBeInTheDocument();
  expect(view.container.querySelector('.v2-not-found.semi-card .semi-empty')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: /重新查询/ })).toHaveClass('semi-button-primary');
});

test('shows deduplicated Semi vehicle candidates and opens the selected VIN', async () => {
  const workspace = document.createElement('main');
  workspace.className = 'v2-content';
  workspace.scrollTop = 320;
  const scrollTo = vi.fn();
  Object.defineProperty(workspace, 'scrollTo', { configurable: true, value: scrollTo });
  document.body.appendChild(workspace);
  const candidate = {
    vin: initialRealtime.vin,
    plate: initialRealtime.plate,
    phone: '13800000000',
    oem: '测试品牌',
    protocols: ['JT808'],
    missingProtocols: [],
    sourceStatus: [],
    sourceCount: 1,
    onlineSourceCount: 1,
    online: true,
    lastSeen: initialRealtime.lastSeen,
    locationText: '广东省广州市',
    bindingScore: 100,
    bindingStatus: 'bound'
  };
  const vehicles = vi.spyOn(api, 'vehicleCoverage').mockResolvedValue({
    items: [candidate, { ...candidate }],
    total: 2,
    limit: 12,
    offset: 0
  });
  vi.spyOn(api, 'vehicleDetail').mockResolvedValue(detail);
  vi.spyOn(api, 'vehicleRealtime').mockResolvedValue({ items: [initialRealtime], total: 1, limit: 1, offset: 0 });
  vi.spyOn(api, 'latestTelemetry').mockResolvedValue({ values: [], categories: [], scannedFrames: 0 } as never);
  const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });

  const view = render(<QueryClientProvider client={client}><MemoryRouter initialEntries={['/vehicles']} future={ROUTER_FUTURE}><Routes><Route path="/vehicles" element={<VehiclePage />} /><Route path="/vehicles/:vin" element={<VehiclePage />} /></Routes></MemoryRouter></QueryClientProvider>);

  expect(await screen.findByRole('heading', { name: '车辆快速定位', level: 5 })).toBeInTheDocument();
  expect(screen.getByLabelText('查车操作')).toHaveClass('v2-workspace-command-bar', 'v2-vehicle-command-bar');
  expect(screen.getByRole('heading', { name: '车辆定位', level: 5 }).closest('.semi-card')).toHaveClass('v2-vehicle-query-panel', 'v2-workspace-filter-panel');
  const input = screen.getByRole('textbox', { name: '搜索车辆' });
  expect(input).toHaveAttribute('aria-expanded', 'false');
  expect(input).toHaveAttribute('aria-controls', 'v2-vehicle-search-options');
  expect(view.container.querySelector('.v2-vehicle-search-options')).not.toBeInTheDocument();
  await waitFor(() => expect(vehicles).toHaveBeenCalledTimes(1));
  const initialQuery = vehicles.mock.calls[0]?.[0];
  expect(initialQuery).toBeDefined();
  expect(initialQuery!.get('limit')).toBe('10');
  expect(screen.getByRole('heading', { name: '授权车辆目录', level: 5 })).toBeInTheDocument();
  expect(await screen.findByText('本页 1 辆')).toBeInTheDocument();
  expect(screen.getByText('本页在线 1')).toBeInTheDocument();
  expect(screen.getByText('多源车辆 0')).toBeInTheDocument();
  expect(view.container.querySelector('.v2-vehicle-directory-table.semi-table-wrapper')).toBeInTheDocument();
  expect(view.container.querySelector('.v2-vehicle-search-page')).toHaveClass('v2-vehicle-directory-v3');
  expect(view.container.querySelector('.v2-vehicle-directory-profile')).toHaveTextContent('测试品牌13800000000');
  expect(view.container.querySelector('.v2-vehicle-directory-protocols .v2-protocol-tag')).toHaveClass('semi-tag-cyan-light');
  expect(view.container.querySelector('.v2-vehicle-discovery-shell')).toContainElement(screen.getByLabelText('查车操作'));
  expect(screen.getByRole('button', { name: `打开 ${initialRealtime.vin} 车辆档案` })).toBeInTheDocument();
  fireEvent.focus(input);
  expect(input).toHaveAttribute('aria-expanded', 'false');
  expect(view.container.querySelector('.v2-vehicle-search-options')).not.toBeInTheDocument();
  fireEvent.change(input, { target: { value: initialRealtime.plate } });
  await waitFor(() => expect(vehicles).toHaveBeenCalledTimes(2));
  expect(input).toHaveAttribute('aria-expanded', 'true');
  expect(screen.getByRole('heading', { name: '匹配车辆', level: 5 })).toBeInTheDocument();
  const option = await screen.findByRole('option', { name: `${initialRealtime.plate} ${initialRealtime.vin} JT/T 808 选择` });
  expect(view.container.querySelector('#v2-vehicle-search-options.v2-vehicle-candidate-list')).toBeInTheDocument();
  expect(view.container.querySelector('#v2-vehicle-search-options')).toHaveAttribute('aria-busy', 'false');
  expect(screen.getAllByRole('option')).toHaveLength(1);

  fireEvent.mouseDown(option);
  fireEvent.click(option);

  expect(await screen.findByText(initialRealtime.vin)).toBeInTheDocument();
  expect(workspace.scrollTop).toBe(0);
  expect(scrollTo).toHaveBeenCalledWith({ top: 0, left: 0, behavior: 'auto' });
  expect(screen.getByRole('group', { name: '车辆快捷操作' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '切换车辆' })).toHaveClass('semi-button', 'semi-button-primary');
  workspace.remove();
});

test('keeps the mobile vehicle directory primary and applies search from a Semi bottom sheet', async () => {
  layout.mobile = true;
  const items = Array.from({ length: 10 }, (_, index) => ({
    vin: `LTEST${String(index).padStart(12, '0')}`,
    plate: `粤A${String(index).padStart(5, '0')}`,
    phone: '',
    oem: '测试品牌',
    protocols: [index % 2 ? 'JT808' : 'GB32960'],
    missingProtocols: [],
    sourceStatus: [],
    sourceCount: 1,
    onlineSourceCount: index < 6 ? 1 : 0,
    online: index < 6,
    lastSeen: `2026-07-16 10:00:${String(index).padStart(2, '0')}`,
    locationText: '广东省广州市',
    bindingScore: 100,
    bindingStatus: 'bound'
  }));
  vi.spyOn(api, 'vehicleCoverage').mockResolvedValue({ items, total: 10, limit: 8, offset: 0 });
  const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });

  const view = render(<QueryClientProvider client={client}><MemoryRouter initialEntries={['/vehicles']} future={ROUTER_FUTURE}><Routes><Route path="/vehicles" element={<VehiclePage />} /></Routes></MemoryRouter></QueryClientProvider>);

  expect(await screen.findByText('本页 8 辆')).toBeInTheDocument();
  expect(screen.getAllByRole('listitem')).toHaveLength(8);
  expect(screen.getAllByRole('button', { name: /打开 .* 车辆档案/ })).toHaveLength(8);
  expect(screen.getAllByText(/GB\/T 32960|JT\/T 808/).length).toBeGreaterThan(0);
  expect(view.container.querySelector('.v2-vehicle-directory-table')).not.toBeInTheDocument();
  expect(view.container.querySelector('.v2-vehicle-query-panel')).not.toBeInTheDocument();
  expect(screen.queryByLabelText('查车操作')).not.toBeInTheDocument();
  const toggle = screen.getByRole('button', { name: '修改车辆范围：10 辆授权车辆' });
  expect(toggle).toHaveAttribute('aria-expanded', 'false');
  fireEvent.click(toggle);
  const input = screen.getByRole('textbox', { name: '搜索车辆' });
  expect(input).toBeVisible();
  expect(document.querySelector('.v2-vehicle-mobile-filter-sidesheet')).toBeInTheDocument();
  expect(screen.getAllByRole('button', { name: /打开 .* 车辆档案/ })).toHaveLength(8);
  fireEvent.change(input, { target: { value: items[0].plate } });
  expect(await screen.findByRole('listbox')).toHaveAttribute('id', 'v2-vehicle-mobile-search-options');
  expect(screen.getAllByRole('button', { name: /打开 .* 车辆档案/ })).toHaveLength(8);
  fireEvent.click(screen.getByRole('button', { name: '应用搜索' }));
  await waitFor(() => expect(screen.getByRole('button', { name: `修改车辆范围：${items[0].plate} · 10 辆匹配` })).toHaveAttribute('aria-expanded', 'false'));
  expect(screen.getByRole('heading', { name: '匹配车辆', level: 5 })).toBeInTheDocument();

  fireEvent.click(screen.getByRole('button', { name: `修改车辆范围：${items[0].plate} · 10 辆匹配` }));
  fireEvent.change(screen.getByRole('textbox', { name: '搜索车辆' }), { target: { value: items[1].plate } });
  fireEvent.click(screen.getByRole('button', { name: '关闭车辆搜索' }));
  await waitFor(() => expect(screen.getByRole('button', { name: `修改车辆范围：${items[0].plate} · 10 辆匹配` })).toBeInTheDocument());

  fireEvent.click(screen.getByRole('button', { name: `修改车辆范围：${items[0].plate} · 10 辆匹配` }));
  fireEvent.click(screen.getByRole('button', { name: '重置范围' }));
  expect(screen.getByRole('dialog', { name: '车辆搜索' })).toBeInTheDocument();
  expect(screen.getByRole('button', { name: `关闭车辆范围：${items[0].plate} · 10 辆匹配` })).toHaveAttribute('aria-expanded', 'true');
  fireEvent.click(screen.getByRole('button', { name: '应用搜索' }));
  await waitFor(() => expect(screen.getByRole('button', { name: '修改车辆范围：10 辆授权车辆' })).toBeInTheDocument());
  expect(screen.getByRole('heading', { name: '授权车辆', level: 5 })).toBeInTheDocument();
});

test('keeps a partial vehicle query in the directory instead of navigating to an invalid detail route', async () => {
  const candidate = {
    vin: initialRealtime.vin,
    plate: initialRealtime.plate,
    phone: '13800000000',
    oem: '测试品牌',
    protocols: ['JT808'],
    missingProtocols: [],
    sourceStatus: [],
    sourceCount: 1,
    onlineSourceCount: 1,
    online: true,
    lastSeen: initialRealtime.lastSeen,
    locationText: '广东省广州市',
    bindingScore: 100,
    bindingStatus: 'bound'
  };
  vi.spyOn(api, 'vehicleCoverage').mockResolvedValue({ items: [candidate], total: 1, limit: 10, offset: 0 });
  const vehicleDetail = vi.spyOn(api, 'vehicleDetail');
  const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });

  const view = render(<QueryClientProvider client={client}><MemoryRouter initialEntries={['/vehicles']} future={ROUTER_FUTURE}><Routes><Route path="/vehicles" element={<VehiclePage />} /><Route path="/vehicles/:vin" element={<VehiclePage />} /></Routes></MemoryRouter></QueryClientProvider>);

  await screen.findByRole('heading', { name: '授权车辆目录', level: 5 });
  const input = screen.getByRole('textbox', { name: '搜索车辆' });
  fireEvent.change(input, { target: { value: '粤A' } });
  expect(await screen.findByRole('listbox')).toBeInTheDocument();

  fireEvent.click(screen.getByRole('button', { name: /查询车辆/ }));

  expect(view.container.querySelector('.v2-vehicle-search-options')).not.toBeInTheDocument();
  expect(screen.getByRole('heading', { name: '匹配车辆', level: 5 })).toBeInTheDocument();
  expect(vehicleDetail).not.toHaveBeenCalled();
});

test('paginates the complete desktop authorized vehicle directory', async () => {
  const firstPage = Array.from({ length: 10 }, (_, index) => ({
    vin: `LTEST${String(index).padStart(12, '0')}`,
    plate: `粤A${String(index).padStart(5, '0')}`,
    phone: '',
    oem: '测试品牌',
    protocols: [index % 2 ? 'JT808' : 'GB32960'],
    missingProtocols: [],
    sourceStatus: [],
    sourceCount: 1,
    onlineSourceCount: index < 6 ? 1 : 0,
    online: index < 6,
    lastSeen: `2026-07-16 10:00:${String(index).padStart(2, '0')}`,
    locationText: '广东省广州市',
    bindingScore: 100,
    bindingStatus: 'bound'
  }));
  const lastVehicle = { ...firstPage[0], vin: 'LTEST000000000010', plate: '粤A00010' };
  const vehicles = vi.spyOn(api, 'vehicleCoverage').mockImplementation((params) => {
    const query = params ?? new URLSearchParams();
    return Promise.resolve({
      items: query.get('offset') === '10' ? [lastVehicle] : firstPage,
      total: 11,
      limit: 10,
      offset: Number(query.get('offset') ?? 0)
    });
  });
  const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });

  const view = render(<QueryClientProvider client={client}><MemoryRouter initialEntries={['/vehicles']} future={ROUTER_FUTURE}><Routes><Route path="/vehicles" element={<VehiclePage />} /></Routes></MemoryRouter></QueryClientProvider>);

  expect(await screen.findByText('粤A00000')).toBeInTheDocument();
  expect(view.container.querySelector('.v2-vehicle-directory-table.semi-table-wrapper')).toBeInTheDocument();
  expect(screen.getByText('共 11 辆 · 本页 10 辆')).toBeInTheDocument();
  expect(document.querySelector('.v2-table-pagination .semi-page-item-small')).toHaveTextContent('1/2');

  fireEvent.click(screen.getByRole('button', { name: 'Next' }));

  await waitFor(() => expect(vehicles.mock.calls[vehicles.mock.calls.length - 1]?.[0]?.get('offset')).toBe('10'));
  expect(await screen.findByText('粤A00010')).toBeInTheDocument();
  expect(screen.getByText('共 11 辆 · 本页 1 辆')).toBeInTheDocument();
  expect(document.querySelector('.v2-table-pagination .semi-page-item-small')).toHaveTextContent('2/2');
});

test('keeps the monitor return on the vehicle page and its nested investigation actions', async () => {
  vi.spyOn(api, 'vehicleDetail').mockResolvedValue(detail);
  vi.spyOn(api, 'vehicleRealtime').mockResolvedValue({ items: [initialRealtime], total: 1, limit: 1, offset: 0 });
  vi.spyOn(api, 'latestTelemetry').mockResolvedValue({ values: [], categories: [], scannedFrames: 0 } as never);
  const addressSpy = vi.spyOn(api, 'reverseGeocode').mockResolvedValue({ provider: 'amap', longitude: 113.26, latitude: 23.13, formattedAddress: '广东省广州市测试道路' });
  const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
  const monitorPath = buildMonitorPath({
    mode: 'map', keyword: '', protocol: '', status: '', selectedVin: 'LTEST000000000001', detailOpen: true,
    viewport: { zoom: 13, bounds: '113.1,23.0,113.4,23.3' }, listOffset: 0, listLimit: 50, hasViewport: true
  });
  const initialEntry = withMonitorReturn(`/vehicles/${initialRealtime.vin}`, monitorPath);

  render(<QueryClientProvider client={client}><MemoryRouter initialEntries={[initialEntry]} future={ROUTER_FUTURE}><Routes><Route path="/vehicles/:vin" element={<VehiclePage />} /></Routes></MemoryRouter></QueryClientProvider>);

  expect(await screen.findByRole('link', { name: /返回全局监控/ })).toHaveAttribute('href', monitorPath);
  const actionGroup = screen.getByRole('group', { name: '车辆快捷操作' });
  expect(actionGroup).toHaveTextContent('切换车辆');
  expect(actionGroup).toHaveTextContent('轨迹回放');
  expect(actionGroup).toHaveTextContent('历史数据');
  expect(actionGroup).toHaveTextContent('里程查询');
  expect(actionGroup).toHaveTextContent('告警事件');
  expect(withMonitorReturn(`/tracks?vin=${encodeURIComponent(initialRealtime.vin)}`, monitorPath)).toContain(`monitorReturn=${encodeURIComponent(monitorPath)}`);
  expect(withMonitorReturn(`/history?vin=${encodeURIComponent(initialRealtime.vin)}`, monitorPath)).toContain(`monitorReturn=${encodeURIComponent(monitorPath)}`);
  expect(withMonitorReturn(`/statistics?vins=${encodeURIComponent(initialRealtime.vin)}`, monitorPath)).toContain(`monitorReturn=${encodeURIComponent(monitorPath)}`);

  const liveOverview = screen.getByText('最新上报').closest('.semi-card');
  const archive = screen.getByRole('heading', { name: '车辆主档', level: 5 }).closest('.semi-card');
  expect(liveOverview).not.toBeNull();
  expect(archive).not.toBeNull();
  expect(liveOverview).toHaveClass('v2-record-card', 'v2-live-overview');
  expect(archive).toHaveClass('v2-record-card', 'v2-archive-card');
  expect(liveOverview!.compareDocumentPosition(archive!)).toBe(Node.DOCUMENT_POSITION_FOLLOWING);

  expect(addressSpy).not.toHaveBeenCalled();
  const addressAction = screen.getByRole('button', { name: '解析当前位置' });
  expect(addressAction).toHaveClass('semi-button', 'v2-current-address-action');
  fireEvent.click(addressAction);
  expect(await screen.findByText('广东省广州市测试道路')).toBeInTheDocument();
  expect(addressSpy).toHaveBeenCalledTimes(1);
});

test('shows unavailable live fields as dashes instead of fabricated zeroes', async () => {
  const unavailable = {
    ...initialRealtime,
    speedAvailable: false,
    speedKmh: 0,
    socAvailable: false,
    socPercent: 0,
    mileageAvailable: false,
    totalMileageKm: 0,
    todayMileageAvailable: false,
    todayMileageKm: 0
  };
  vi.spyOn(api, 'vehicleDetail').mockResolvedValue({
    ...detail,
    realtimeSummary: unavailable,
    mileage: {
      items: [{ vin: unavailable.vin, plate: unavailable.plate, date: '2026-07-15', startMileageKm: 900, endMileageKm: 988.8, dailyMileageKm: 88.8, source: 'JT808' }],
      total: 1,
      limit: 20,
      offset: 0
    }
  });
  vi.spyOn(api, 'vehicleRealtime').mockResolvedValue({ items: [unavailable], total: 1, limit: 1, offset: 0 });
  vi.spyOn(api, 'latestTelemetry').mockResolvedValue({ values: [], categories: [], scannedFrames: 0 } as never);
  const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });

  render(<QueryClientProvider client={client}><MemoryRouter initialEntries={[`/vehicles/${initialRealtime.vin}`]} future={ROUTER_FUTURE}><Routes><Route path="/vehicles/:vin" element={<VehiclePage />} /></Routes></MemoryRouter></QueryClientProvider>);

  const liveOverview = (await screen.findByText('最新上报')).closest('.semi-card')!;
  for (const label of ['速度', 'SOC', '总里程', '当日里程']) {
    const metric = Array.from(liveOverview.querySelectorAll('.v2-live-grid > div')).find((item) => item.querySelector('small')?.textContent === label);
    expect(metric).toHaveTextContent('—');
    expect(metric).not.toHaveTextContent('88.8');
    expect(metric).not.toHaveTextContent(/^0/);
  }
});

test('keeps protocol telemetry independent and explains mileage semantics', async () => {
  vi.spyOn(api, 'vehicleDetail').mockResolvedValue({ ...detail, sources: ['GB32960', 'JT808'] });
  vi.spyOn(api, 'vehicleRealtime').mockResolvedValue({ items: [{ ...initialRealtime, protocols: ['GB32960', 'JT808'] }], total: 1, limit: 1, offset: 0 });
  vi.spyOn(api, 'latestTelemetry').mockResolvedValue({
    vin: initialRealtime.vin,
    categories: [{ key: 'vehicle', label: '整车数据', count: 3 }],
    values: [
      {
        key: 'total_mileage_km', sourceField: 'gb32960.vehicle.total_mileage_km', label: '仪表盘总里程', unit: 'km',
        category: 'vehicle', valueType: 'numeric', value: 1200, protocol: 'GB32960', frameId: 'gb', deviceTime: '', serverTime: '2026-07-16 10:00:00',
        quality: 'good', qualityReason: '正常', freshnessSeconds: 1
      },
      {
        key: 'cell_voltages', sourceField: 'gb32960.extended.cell_voltages', label: '单体电压列表', unit: 'V',
        category: 'vehicle', valueType: 'array', value: [3.21, 3.22, 3.23, 3.24], protocol: 'GB32960', frameId: 'gb-array', deviceTime: '', serverTime: '2026-07-16 10:00:00',
        quality: 'good', qualityReason: '正常', freshnessSeconds: 1, sourceEndpoint: '10.0.0.1:8080'
      },
      {
        key: 'total_mileage_km', sourceField: 'jt808.location.total_mileage_km', label: 'GPS 总里程', unit: 'km',
        category: 'vehicle', valueType: 'numeric', value: 1180, protocol: 'JT808', frameId: 'jt', deviceTime: '', serverTime: '2026-07-16 10:00:00',
        quality: 'good', qualityReason: '正常', freshnessSeconds: 1
      }
    ],
    asOf: '2026-07-16T10:00:00+08:00', staleAfterSeconds: 300, scannedFrames: 2, evidence: 'test'
  });
  const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });

  render(<QueryClientProvider client={client}><MemoryRouter initialEntries={[`/vehicles/${initialRealtime.vin}`]} future={ROUTER_FUTURE}><Routes><Route path="/vehicles/:vin" element={<VehiclePage />} /></Routes></MemoryRouter></QueryClientProvider>);

  expect(await screen.findByRole('tab', { name: /GB\/T 32960/, selected: true })).toBeInTheDocument();
  expect(document.querySelector('.v2-telemetry-table.semi-table-wrapper')).toBeInTheDocument();
  expect(document.querySelectorAll('.v2-telemetry-table [role="columnheader"]')).toHaveLength(5);
  expect(document.querySelector('.v2-telemetry-mobile-list')).not.toBeInTheDocument();
  expect(document.querySelector('.v2-event-list.semi-list')).toBeInTheDocument();
  expect(document.querySelector('.v2-event-empty.semi-empty')).toBeInTheDocument();
  expect(screen.getByText('仪表盘总里程')).toBeInTheDocument();
  expect(screen.queryByText('GPS 总里程')).not.toBeInTheDocument();
  const complexValue = screen.getByRole('button', { name: '查看 单体电压列表 完整数据' });
  expect(complexValue).toHaveClass('semi-button', 'v2-telemetry-value-trigger');
  expect(complexValue).toHaveTextContent('4 个值');
  expect(complexValue).toHaveTextContent('3.21，3.22，3.23');
  fireEvent.click(complexValue);
  expect(await screen.findByRole('dialog', { name: '遥测字段详情' })).toBeInTheDocument();
  expect(document.querySelector('.v2-telemetry-evidence-sidesheet')).toHaveClass('v2-workspace-detail-sidesheet');
  expect(document.querySelector('.v2-telemetry-detail-protocol')).toHaveClass('semi-tag-blue-light');
  expect(document.querySelector('.v2-telemetry-value-meta')).toHaveTextContent('数据类型列表');
  const detailSummary = screen.getByRole('list', { name: '遥测字段详情摘要' });
  expect(detailSummary).toHaveTextContent('协议来源');
  expect(detailSummary).toHaveTextContent('10.0.0.1:8080');
  expect(detailSummary).toHaveTextContent('完整值');
  expect(detailSummary).toHaveTextContent('4 个值');
  expect(detailSummary).toHaveTextContent('数据质量');
  expect(detailSummary).toHaveTextContent('正常');
  expect(document.querySelector('.v2-telemetry-value-detail pre')).toHaveTextContent('3.24');
  fireEvent.click(screen.getByRole('tab', { name: /JT\/T 808/ }));
  expect(screen.getByRole('tab', { name: /JT\/T 808/, selected: true })).toBeInTheDocument();
  expect(screen.getByText('GPS 总里程')).toBeInTheDocument();
  expect(screen.queryByText('仪表盘总里程')).not.toBeInTheDocument();
});

test('uses compact Semi telemetry evidence cards on mobile', async () => {
  layout.mobile = true;
  vi.spyOn(api, 'vehicleDetail').mockResolvedValue({ ...detail, sources: ['GB32960', 'JT808', 'YUTONG_MQTT'] });
  vi.spyOn(api, 'vehicleRealtime').mockResolvedValue({ items: [initialRealtime], total: 1, limit: 1, offset: 0 });
  vi.spyOn(api, 'latestTelemetry').mockResolvedValue({
    vin: initialRealtime.vin,
    categories: [{ key: 'vehicle', label: '整车数据', count: 1 }],
    values: [{
      key: 'speed_kmh', sourceField: 'jt808.location.speed_kmh', label: 'GPS 速度', unit: 'km/h',
      category: 'vehicle', valueType: 'numeric', value: 36, protocol: 'JT808', frameId: 'jt-mobile', deviceTime: '2026-07-16 10:00:00',
      serverTime: '2026-07-16 10:00:01', quality: 'good', qualityReason: '正常', freshnessSeconds: 1
    }],
    asOf: '2026-07-16T10:00:01+08:00', staleAfterSeconds: 300, scannedFrames: 1, evidence: 'test'
  });
  const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });

  render(<QueryClientProvider client={client}><MemoryRouter initialEntries={[`/vehicles/${initialRealtime.vin}`]} future={ROUTER_FUTURE}><Routes><Route path="/vehicles/:vin" element={<VehiclePage />} /><Route path="/history" element={<div data-testid="mobile-history-route">历史数据页面</div>} /></Routes></MemoryRouter></QueryClientProvider>);

  expect(await screen.findByText('GPS 速度')).toBeInTheDocument();
  expect(document.querySelector('.v2-telemetry-table')).not.toBeInTheDocument();
  expect(document.querySelector('.v2-telemetry-mobile-list.semi-list')).toBeInTheDocument();
  expect(document.querySelector('.v2-telemetry-mobile-item.semi-list-item')).toHaveTextContent('36km/h正常');
  expect(screen.getByRole('button', { name: '查看 GPS 速度 字段详情' })).toHaveClass('v2-telemetry-mobile-action');
  fireEvent.click(screen.getByRole('button', { name: '查看 GPS 速度 字段详情' }));
  expect(await screen.findByRole('dialog', { name: '遥测字段详情' })).toBeInTheDocument();
  expect(document.querySelector('.v2-telemetry-evidence-sidesheet')).toHaveClass('v2-workspace-detail-sidesheet', 'semi-sidesheet-bottom');
  expect(document.querySelector('.v2-telemetry-value-meta')).toHaveTextContent('数据类型数值');
  expect(document.querySelector('.v2-telemetry-detail-protocol')).toHaveClass('semi-tag-cyan-light');
  expect(screen.getByRole('button', { name: '关闭遥测字段详情' })).toBeInTheDocument();
  expect(document.querySelector('.v2-vehicle-record-page')).toHaveClass('v2-vehicle-record-v3', 'is-mobile-layout');
  const mobileActions = screen.getByRole('group', { name: '车辆快捷操作' });
  expect(mobileActions.querySelectorAll('.semi-button')).toHaveLength(3);
  expect(mobileActions).toHaveTextContent('切换车辆轨迹回放更多');
  expect(mobileActions).not.toHaveTextContent('历史数据里程查询告警事件');
  const moreActions = screen.getByRole('button', { name: '更多车辆操作' });
  expect(moreActions).toHaveAttribute('aria-haspopup', 'dialog');
  expect(moreActions).toHaveAttribute('aria-controls', 'v2-vehicle-mobile-actions');
  expect(moreActions).toHaveAttribute('aria-expanded', 'false');
  fireEvent.click(moreActions);
  expect(await screen.findByRole('dialog', { name: '更多车辆操作' })).toBeVisible();
  expect(document.querySelector('.v2-vehicle-mobile-actions-sidesheet')).toHaveClass('v2-workspace-action-sidesheet', 'semi-sidesheet-bottom');
  expect(screen.getByRole('button', { name: '历史数据' })).toBeVisible();
  expect(screen.getByRole('button', { name: '里程查询' })).toBeVisible();
  expect(screen.getByRole('button', { name: '告警事件' })).toBeVisible();
  expect(screen.getByRole('button', { name: '关闭更多车辆操作' })).toBeInTheDocument();
  expect(document.querySelectorAll('.v2-identity-sources .semi-tag')).toHaveLength(3);
  expect(document.querySelector('.v2-identity-sources')).toHaveTextContent('GB/T 32960JT/T 808宇通 MQTT');
  expect(document.querySelector('.v2-identity-sources .semi-tag-blue-light')).toHaveTextContent('GB/T 32960');
  expect(document.querySelector('.v2-identity-sources .semi-tag-cyan-light')).toHaveTextContent('JT/T 808');
  expect(document.querySelector('.v2-identity-sources .semi-tag-violet-light')).toHaveTextContent('宇通 MQTT');
  expect(document.querySelector('.v2-vehicle-command-card .v2-live-grid')).toHaveAttribute('aria-label', '车辆实时指标');
  expect(document.querySelectorAll('.v2-vehicle-command-card .v2-live-grid > [role="listitem"]')).toHaveLength(6);
  expect(document.querySelector('.v2-live-metric.is-speed')).toHaveTextContent('速度');
  expect(document.querySelector('.v2-live-metric.is-today-mileage')).toHaveTextContent('当日里程');
  expect(document.querySelector('.v2-live-metric.is-source-status')).toHaveTextContent('来源状态');
  fireEvent.click(screen.getByRole('button', { name: '历史数据' }));
  expect(await screen.findByTestId('mobile-history-route')).toBeInTheDocument();
});
