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 { ROUTER_FUTURE } from '../routing/routerConfig';

const routing = vi.hoisted(() => ({
  preloadRoute: vi.fn(async () => undefined),
  scheduleIdleRoutePreloads: vi.fn(() => vi.fn()),
  shouldPreloadRouteOnIntent: vi.fn(() => true)
}));
const auth = vi.hoisted(() => ({
  session: { name: 'test-user', username: 'test-user', role: 'viewer' as const } as any,
  logout: vi.fn()
}));
const apiMocks = vi.hoisted(() => ({
  changePassword: vi.fn()
}));

vi.mock('../routing/routeModules', () => routing);
vi.mock('../auth/AuthGate', () => ({
  usePlatformSession: () => ({ session: auth.session, logout: auth.logout })
}));
vi.mock('../../api/client', () => ({ api: { changePassword: apiMocks.changePassword } }));

import { AppShell } from './AppShell';

afterEach(() => {
  cleanup();
  vi.restoreAllMocks();
  vi.clearAllMocks();
  auth.session = { name: 'test-user', username: 'test-user', role: 'viewer' };
  apiMocks.changePassword.mockReset();
});

test('renders only explicitly assigned customer menus', () => {
  auth.session = { name: '客户甲', role: 'customer', userType: 'customer', menuKeys: ['monitor', 'vehicles', 'tracks', 'statistics'] };
  render(<MemoryRouter future={ROUTER_FUTURE} initialEntries={['/monitor']}>
    <Routes><Route element={<AppShell />}><Route path="*" element={<span>页面内容</span>} /></Route></Routes>
  </MemoryRouter>);

  expect(screen.getByRole('link', { name: '全局监控' })).toBeInTheDocument();
  expect(screen.getByRole('link', { name: '车辆查询' })).toBeInTheDocument();
  expect(screen.getByRole('link', { name: '轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('link', { name: '里程查询' })).toBeInTheDocument();
  expect(screen.queryByRole('link', { name: '历史数据' })).not.toBeInTheDocument();
  expect(screen.queryByRole('link', { name: '告警中心' })).not.toBeInTheDocument();
  expect(screen.queryByRole('link', { name: '接入管理' })).not.toBeInTheDocument();
  expect(screen.queryByRole('link', { name: '账号管理' })).not.toBeInTheDocument();
  expect(screen.queryByRole('link', { name: '运维质量' })).not.toBeInTheDocument();
});

test('reschedules likely route preloads when the active module changes', async () => {
  const firstCleanup = vi.fn();
  const secondCleanup = vi.fn();
  routing.scheduleIdleRoutePreloads
    .mockReturnValueOnce(firstCleanup)
    .mockReturnValueOnce(secondCleanup);

  render(<MemoryRouter future={ROUTER_FUTURE} initialEntries={['/monitor']}>
    <Routes><Route element={<AppShell />}><Route path="*" element={<span>页面内容</span>} /></Route></Routes>
  </MemoryRouter>);

  expect(routing.scheduleIdleRoutePreloads).toHaveBeenCalledWith({ activePathname: '/monitor' });
  fireEvent.click(screen.getByRole('link', { name: '历史数据' }));

  await waitFor(() => expect(routing.scheduleIdleRoutePreloads).toHaveBeenLastCalledWith({ activePathname: '/history' }));
  expect(firstCleanup).toHaveBeenCalledTimes(1);

  cleanup();
  expect(secondCleanup).toHaveBeenCalledTimes(1);
});

test('marks the content as a full-height track workspace after sidebar navigation', async () => {
  const scrollTo = vi.fn();
  Object.defineProperty(HTMLElement.prototype, 'scrollTo', { configurable: true, value: scrollTo });
  render(<MemoryRouter future={ROUTER_FUTURE} initialEntries={['/monitor']}>
    <Routes><Route element={<AppShell />}><Route path="*" element={<span>页面内容</span>} /></Route></Routes>
  </MemoryRouter>);

  const content = document.querySelector<HTMLElement>('.v2-content')!;
  expect(content).not.toHaveClass('is-track-workspace');
  content.scrollTop = 240;
  scrollTo.mockClear();
  fireEvent.click(screen.getByRole('link', { name: '轨迹回放' }));

  await waitFor(() => expect(document.querySelector('.v2-content')).toHaveClass('is-track-workspace'));
  expect(document.querySelector<HTMLElement>('.v2-content')?.scrollTop).toBe(0);
  expect(scrollTo).toHaveBeenCalledWith({ top: 0, left: 0, behavior: 'auto' });
  expect(screen.getByRole('heading', { name: '轨迹回放' })).toBeInTheDocument();
});

test('opens contextual help for the active module and closes it without navigating', () => {
  render(<MemoryRouter future={ROUTER_FUTURE} initialEntries={['/monitor']}>
    <Routes><Route element={<AppShell />}><Route path="*" element={<span>页面内容</span>} /></Route></Routes>
  </MemoryRouter>);

  const help = screen.getByRole('button', { name: '帮助' });
  expect(help).toHaveAttribute('aria-expanded', 'false');
  fireEvent.click(help);

  expect(screen.getByRole('dialog', { name: '全局监控' })).toHaveTextContent('筛选会自动生效');
  expect(document.querySelector('.v2-help-sheet')).toHaveClass('v2-workspace-help-sidesheet');
  const helpSummary = screen.getByRole('list', { name: '全局监控摘要' });
  expect(helpSummary).toHaveTextContent('页面目标快速完成');
  expect(helpSummary).toHaveTextContent('建议步骤3 项');
  expect(helpSummary).toHaveTextContent('快捷关闭Esc');
  expect(document.querySelector('.v2-help-overview.semi-card')).toBeInTheDocument();
  expect(screen.getByText('建议操作')).toBeInTheDocument();
  expect(help).toHaveAttribute('aria-expanded', 'true');
  fireEvent.click(screen.getByLabelText('关闭帮助'));
  expect(help).toHaveAttribute('aria-expanded', 'false');
  expect(document.body.style.overflow).not.toBe('hidden');
  expect(screen.getByText('页面内容')).toBeInTheDocument();
});

test('uses one Semi account menu for identity, password and logout actions', async () => {
  render(<MemoryRouter future={ROUTER_FUTURE} initialEntries={['/monitor']}>
    <Routes><Route element={<AppShell />}><Route path="*" element={<span>页面内容</span>} /></Route></Routes>
  </MemoryRouter>);

  const trigger = screen.getByRole('button', { name: '账号菜单，test-user' });
  expect(trigger).toHaveAttribute('aria-expanded', 'false');
  fireEvent.click(trigger);

  expect(await screen.findByText('@test-user')).toBeInTheDocument();
  expect(screen.getByText('当前登录账号')).toBeInTheDocument();
  await waitFor(() => expect(screen.getByRole('button', { name: '账号菜单，test-user' })).toHaveAttribute('aria-expanded', 'true'));
  fireEvent.click(screen.getByText('修改登录密码'));

  expect(screen.getByRole('dialog', { name: '修改登录密码' })).toBeInTheDocument();
  fireEvent.click(screen.getByRole('button', { name: '取消' }));

  fireEvent.click(trigger);
  fireEvent.click(await screen.findByText('退出登录'));
  expect(auth.logout).toHaveBeenCalledTimes(1);
});

test('validates password policy while typing and submits only a complete secure change', async () => {
  apiMocks.changePassword.mockResolvedValue({ changed: true });
  render(<MemoryRouter future={ROUTER_FUTURE} initialEntries={['/monitor']}>
    <Routes><Route element={<AppShell />}><Route path="*" element={<span>页面内容</span>} /></Route></Routes>
  </MemoryRouter>);

  fireEvent.click(screen.getByRole('button', { name: '账号菜单，test-user' }));
  fireEvent.click(await screen.findByText('修改登录密码'));

  const submit = screen.getByRole('button', { name: '确认修改' });
  expect(submit).toBeDisabled();
  expect(document.querySelector('.v2-password-dialog > .semi-banner-warning')).toHaveTextContent('当前设备和其他设备上的旧会话都会失效');

  fireEvent.change(screen.getByLabelText('当前密码'), { target: { value: 'CurrentPass!1' } });
  fireEvent.change(screen.getByLabelText('新密码'), { target: { value: 'StrongPass!1' } });
  expect(screen.getByLabelText('新密码安全规则').querySelectorAll('.is-valid')).toHaveLength(2);
  fireEvent.change(screen.getByLabelText('确认新密码'), { target: { value: 'different' } });
  expect(screen.getByRole('status')).toHaveTextContent('两次输入的新密码不一致');
  expect(submit).toBeDisabled();

  fireEvent.change(screen.getByLabelText('确认新密码'), { target: { value: 'StrongPass!1' } });
  expect(screen.getByRole('status')).toHaveTextContent('两次输入一致');
  expect(submit).toBeEnabled();
  fireEvent.click(submit);

  await waitFor(() => expect(apiMocks.changePassword).toHaveBeenCalledWith({ currentPassword: 'CurrentPass!1', newPassword: 'StrongPass!1' }));
  expect(auth.logout).toHaveBeenCalledTimes(1);
});

test('uses the compact mobile navigation and opens secondary modules in a Semi SideSheet', () => {
  vi.spyOn(window, 'matchMedia').mockReturnValue({
    matches: true,
    media: '(max-width: 680px)',
    onchange: null,
    addListener: vi.fn(),
    removeListener: vi.fn(),
    addEventListener: vi.fn(),
    removeEventListener: vi.fn(),
    dispatchEvent: vi.fn()
  });

  render(<MemoryRouter future={ROUTER_FUTURE} initialEntries={['/monitor']}>
    <Routes><Route element={<AppShell />}><Route path="*" element={<span>页面内容</span>} /></Route></Routes>
  </MemoryRouter>);

  expect(screen.getByRole('heading', { name: '全局监控' })).toHaveClass('v2-topbar-page-title');
  expect(screen.getByRole('navigation', { name: '主导航' })).toHaveStyle({ '--v2-mobile-nav-count': '5' });
  expect(screen.getByRole('link', { name: '全局监控' })).toBeInTheDocument();
  expect(screen.getByRole('link', { name: '车辆查询' })).toBeInTheDocument();
  expect(screen.getByRole('link', { name: '轨迹回放' })).toBeInTheDocument();
  expect(screen.getByRole('link', { name: '里程查询' })).toBeInTheDocument();
  expect(screen.queryByRole('link', { name: '历史数据' })).not.toBeInTheDocument();

  fireEvent.click(screen.getByRole('button', { name: '更多功能' }));

  const moreSheet = screen.getByRole('dialog', { name: '更多功能' });
  expect(moreSheet.closest('.semi-sidesheet')).toHaveClass('v2-workspace-action-sidesheet', 'v2-mobile-more-sidesheet');
  expect(moreSheet).toHaveTextContent('数据分析、业务处置与平台治理');
  expect(screen.getByRole('navigation', { name: '分析与处置' })).toBeInTheDocument();
  expect(screen.getByRole('navigation', { name: '平台治理' })).toBeInTheDocument();
  expect(moreSheet).toHaveTextContent('历史证据与业务告警');
  expect(moreSheet).toHaveTextContent('接入、账号与运维质量');
  expect(screen.getByRole('link', { name: '历史数据' })).toBeInTheDocument();
  expect(screen.getByRole('link', { name: '告警中心' })).toBeInTheDocument();
  expect(screen.getByRole('link', { name: '接入管理' })).toBeInTheDocument();
  expect(screen.getByRole('link', { name: '账号管理' })).toBeInTheDocument();
  expect(screen.getByRole('link', { name: '运维质量' })).toBeInTheDocument();

  fireEvent.click(screen.getByLabelText('关闭更多功能'));
  expect(screen.getByRole('button', { name: '更多功能' })).toHaveAttribute('aria-expanded', 'false');
  expect(document.body.style.overflow).not.toBe('hidden');
});

test('lets customer mobile navigation use the full width when no more menu is needed', () => {
  auth.session = { name: '客户甲', role: 'customer', userType: 'customer', menuKeys: ['monitor', 'vehicles', 'tracks', 'statistics'] };
  vi.spyOn(window, 'matchMedia').mockReturnValue({
    matches: true,
    media: '(max-width: 680px)',
    onchange: null,
    addListener: vi.fn(),
    removeListener: vi.fn(),
    addEventListener: vi.fn(),
    removeEventListener: vi.fn(),
    dispatchEvent: vi.fn()
  });

  render(<MemoryRouter future={ROUTER_FUTURE} initialEntries={['/statistics']}>
    <Routes><Route element={<AppShell />}><Route path="*" element={<span>页面内容</span>} /></Route></Routes>
  </MemoryRouter>);

  expect(screen.getByRole('heading', { name: '里程查询' })).toHaveClass('v2-topbar-page-title');
  expect(screen.getByRole('navigation', { name: '主导航' })).toHaveStyle({ '--v2-mobile-nav-count': '4' });
  expect(screen.queryByRole('button', { name: '更多功能' })).not.toBeInTheDocument();
  expect(screen.getByRole('link', { name: '里程查询' })).toHaveAttribute('aria-current', 'page');
});

test('automatically collapses the Semi sidebar at tablet width', () => {
  vi.spyOn(window, 'matchMedia').mockImplementation((query) => ({
    matches: query === '(max-width: 900px)',
    media: query,
    onchange: null,
    addListener: vi.fn(),
    removeListener: vi.fn(),
    addEventListener: vi.fn(),
    removeEventListener: vi.fn(),
    dispatchEvent: vi.fn()
  }));

  render(<MemoryRouter future={ROUTER_FUTURE} initialEntries={['/monitor']}>
    <Routes><Route element={<AppShell />}><Route path="*" element={<span>页面内容</span>} /></Route></Routes>
  </MemoryRouter>);

  expect(screen.getByRole('link', { name: '全局监控' })).toHaveAttribute('title', '全局监控');
  expect(screen.queryByRole('button', { name: '收起侧栏' })).not.toBeInTheDocument();
  expect(document.querySelector('.v2-sidebar')).toHaveClass('is-collapsed');
});
