import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
import { afterEach, expect, test, vi } from 'vitest';
import { MemoryRouter } from 'react-router-dom';
import type { AlertEvent, Page } from '../../api/types';
import AlertsPage from './AlertsPage';
import { ROUTER_FUTURE } from '../routing/routerConfig';

const mocks = vi.hoisted(() => ({
  alertSummaryV2: vi.fn(), alertEventsV2: vi.fn(), alertEventV2: vi.fn(), actOnAlertV2: vi.fn(),
  alertRulesV2: vi.fn(), metricCatalog: vi.fn(), alertNotificationsV2: vi.fn(), readAlertNotificationsV2: vi.fn(),
  saveAlertRuleV2: vi.fn(), setAlertRuleEnabledV2: vi.fn()
}));
vi.mock('../../api/client', () => ({ api: mocks }));
vi.mock('../auth/AuthGate', () => ({ usePlatformSession: () => ({ session: { role: 'admin' } }) }));

afterEach(() => {
  cleanup();
  Object.values(mocks).forEach((mock) => mock.mockReset());
});

function alertEvent(id: string, vin: string, plate: string): AlertEvent {
  return {
    id, ruleId: 'speed-rule', ruleName: `${plate}速度告警`, ruleVersion: 1, severity: 'major', status: 'unprocessed',
    vin, plate, protocol: 'JT808', metric: 'speed_kmh', operator: 'gt', triggerValue: 90, threshold: 80, thresholdHigh: 0,
    unit: 'km/h', durationSec: 60, location: '测试位置', sourceEventId: `${id}-source`, eventAt: '2026-07-16T04:00:00Z', receivedAt: '2026-07-16T04:00:01Z', triggeredAt: '2026-07-16T04:00:00Z', recoveredAt: '', handler: '', version: 1, actions: []
  };
}

test('preserves an unsubmitted event filter draft across alert tab URL changes', async () => {
  mocks.alertSummaryV2.mockResolvedValue({ active: 0, unprocessed: 0, processing: 0, recovered: 0, closed: 0, ignored: 0, unreadNotifications: 0, asOf: '' });
  mocks.alertEventsV2.mockResolvedValue({ items: [], total: 0, limit: 20, offset: 0 });
  mocks.alertRulesV2.mockResolvedValue([]);
  mocks.metricCatalog.mockResolvedValue({ metrics: [], asOf: '' });
  mocks.alertNotificationsV2.mockResolvedValue({ items: [], total: 0, limit: 100, offset: 0 });
  const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
  render(<QueryClientProvider client={client}><MemoryRouter future={ROUTER_FUTURE} initialEntries={['/alerts']}><AlertsPage /></MemoryRouter></QueryClientProvider>);

  const keyword = await screen.findByPlaceholderText('车牌 / VIN / 规则名称');
  fireEvent.change(keyword, { target: { value: '保留筛选草稿' } });
  fireEvent.click(screen.getByRole('button', { name: /站内通知/ }));
  expect(await screen.findByText('仅站内通道具备真实送达与已读状态')).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: /告警事件/ }));

  expect(await screen.findByPlaceholderText('车牌 / VIN / 规则名称')).toHaveValue('保留筛选草稿');
});

test('loads only event dependencies on the default alert tab', async () => {
  mocks.alertSummaryV2.mockResolvedValue({ active: 0, unprocessed: 0, processing: 0, recovered: 0, closed: 0, ignored: 0, unreadNotifications: 0, asOf: '' });
  mocks.alertEventsV2.mockResolvedValue({ items: [], total: 0, limit: 20, offset: 0 });
  mocks.alertRulesV2.mockResolvedValue([]);
  mocks.alertNotificationsV2.mockResolvedValue({ items: [], total: 0, limit: 100, offset: 0 });
  const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
  render(<QueryClientProvider client={client}><MemoryRouter future={ROUTER_FUTURE} initialEntries={['/alerts']}><AlertsPage /></MemoryRouter></QueryClientProvider>);

  await screen.findByText('当前筛选条件没有告警事件');
  expect(mocks.alertRulesV2).toHaveBeenCalledTimes(1);
  expect(mocks.metricCatalog).not.toHaveBeenCalled();
  expect(mocks.alertNotificationsV2).toHaveBeenCalledTimes(1);
  expect(mocks.alertNotificationsV2.mock.calls[0][0].toString()).toBe('unreadOnly=true&limit=100');
});

test('loads one full notification query on a direct notifications entry', async () => {
  mocks.alertNotificationsV2.mockResolvedValue({ items: [], total: 0, limit: 100, offset: 0 });
  const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
  render(<QueryClientProvider client={client}><MemoryRouter future={ROUTER_FUTURE} initialEntries={['/alerts?tab=notifications']}><AlertsPage /></MemoryRouter></QueryClientProvider>);

  await screen.findByText('仅站内通道具备真实送达与已读状态');
  expect(mocks.alertRulesV2).not.toHaveBeenCalled();
  expect(mocks.metricCatalog).not.toHaveBeenCalled();
  expect(mocks.alertNotificationsV2).toHaveBeenCalledTimes(1);
  expect(mocks.alertNotificationsV2.mock.calls[0][0].toString()).toBe('limit=100');
});

test('ends notification mutation feedback with a visible retryable error instead of a silent pending state', async () => {
  mocks.alertNotificationsV2.mockResolvedValue({ items: [{ id: 7, eventId: 'event-7', title: '车辆告警', content: '测试告警', severity: 'major', read: false, createdAt: '2026-07-16T04:00:00Z' }], total: 1, limit: 100, offset: 0 });
  mocks.readAlertNotificationsV2.mockRejectedValue(new Error('通知状态更新超时'));
  const client = new QueryClient({ defaultOptions: { queries: { retry: false }, mutations: { retry: false } } });
  render(<QueryClientProvider client={client}><MemoryRouter future={ROUTER_FUTURE} initialEntries={['/alerts?tab=notifications']}><AlertsPage /></MemoryRouter></QueryClientProvider>);

  fireEvent.click(await screen.findByRole('button', { name: '标为已读' }));

  expect(await screen.findByText('通知状态更新超时')).toBeInTheDocument();
  expect(screen.getByRole('button', { name: '标为已读' })).toBeEnabled();
});

test('clears old alert rows and inspector evidence when the event scope changes', async () => {
  const oldEvent = alertEvent('old-event', 'OLDVIN', '旧告警车牌');
  const newEvent = alertEvent('new-event', 'NEWVIN', '新告警车牌');
  let resolveNew!: (value: Page<AlertEvent>) => void;
  mocks.alertSummaryV2.mockResolvedValue({ active: 1, unprocessed: 1, processing: 0, recovered: 0, closed: 0, ignored: 0, unreadNotifications: 0, asOf: '' });
  mocks.alertEventsV2.mockImplementation((query: { keyword?: string }) => query.keyword === 'OLDVIN'
    ? Promise.resolve({ items: [oldEvent], total: 1, limit: 20, offset: 0 })
    : new Promise<Page<AlertEvent>>((resolve) => { resolveNew = resolve; }));
  mocks.alertEventV2.mockImplementation((id: string) => Promise.resolve(id === oldEvent.id ? oldEvent : newEvent));
  mocks.alertRulesV2.mockResolvedValue([]);
  mocks.alertNotificationsV2.mockResolvedValue({ items: [], total: 0, limit: 100, offset: 0 });
  const client = new QueryClient({ defaultOptions: { queries: { retry: false } } });
  render(<QueryClientProvider client={client}><MemoryRouter future={ROUTER_FUTURE} initialEntries={['/alerts?keyword=OLDVIN']}><AlertsPage /></MemoryRouter></QueryClientProvider>);

  expect((await screen.findAllByText('旧告警车牌')).length).toBeGreaterThan(0);
  expect(await screen.findByText('old-event')).toBeInTheDocument();
  fireEvent.change(screen.getByPlaceholderText('车牌 / VIN / 规则名称'), { target: { value: 'NEWVIN' } });
  fireEvent.click(screen.getByRole('button', { name: '查询' }));

  expect(await screen.findByText('正在更新事件…')).toBeInTheDocument();
  expect(screen.queryByText('旧告警车牌')).not.toBeInTheDocument();
  expect(screen.queryByText('old-event')).not.toBeInTheDocument();
  expect(screen.getByText('选择告警事件')).toBeInTheDocument();

  await act(async () => resolveNew({ items: [newEvent], total: 1, limit: 20, offset: 0 }));
  expect((await screen.findAllByText('新告警车牌')).length).toBeGreaterThan(0);
  expect(await screen.findByText('new-event')).toBeInTheDocument();
  await waitFor(() => expect(screen.queryByText('正在更新事件…')).not.toBeInTheDocument());
});
