import React, { act } from 'react';
import { createRoot } from 'react-dom/client';
import { afterEach, describe, expect, it, vi } from 'vitest';
import { MemoryRouter, Route, Routes, useNavigate } from 'react-router';
import { RefreshButton } from './RefreshButton';
import { BackTop } from './BackTop';
import { ScrollToTop } from './ScrollToTop';
import { useSubmitShortcut } from '../hooks/useSubmitShortcut';

let container: HTMLDivElement | null = null;
let root: ReturnType<typeof createRoot> | null = null;

const flush = () => new Promise((resolve) => setTimeout(resolve, 0));

const mount = (node: React.ReactNode) => {
  const host = document.createElement('div');
  container = host;
  document.body.appendChild(host);
  root = createRoot(host);
  act(() => root?.render(node));
};

afterEach(async () => {
  if (root) {
    await act(async () => root?.unmount());
  }
  container?.remove();
  root = null;
  container = null;
  vi.restoreAllMocks();
});

describe('UX 组件与交互', () => {
  it('RefreshButton 点击触发 onRefresh，loading 时展示加载态', async () => {
    const onRefresh = vi.fn();
    mount(<RefreshButton onRefresh={onRefresh} />);
    const button = container?.querySelector('button');
    if (!button) throw new Error('button not rendered');
    act(() => button.dispatchEvent(new MouseEvent('click', { bubbles: true })));
    await flush();
    expect(onRefresh).toHaveBeenCalledTimes(1);

    mount(<RefreshButton onRefresh={onRefresh} loading />);
    expect(container?.querySelector('.ant-btn-loading')).toBeTruthy();
  });

  it('useSubmitShortcut 未激活时不响应 Cmd+Enter', async () => {
    const onSubmit = vi.fn();
    const Harness = () => {
      useSubmitShortcut(false, onSubmit);
      return <button type="button">ok</button>;
    };
    mount(<Harness />);
    window.dispatchEvent(
      new KeyboardEvent('keydown', { key: 'Enter', metaKey: true, bubbles: true }),
    );
    await flush();
    expect(onSubmit).not.toHaveBeenCalled();
  });

  it('useSubmitShortcut 激活时响应 Cmd/Ctrl+Enter 且阻止默认行为', async () => {
    const onSubmit = vi.fn();
    const Harness = () => {
      useSubmitShortcut(true, onSubmit);
      return <button type="button">ok</button>;
    };
    mount(<Harness />);

    const metaEvent = new KeyboardEvent('keydown', {
      key: 'Enter',
      metaKey: true,
      bubbles: true,
      cancelable: true,
    });
    window.dispatchEvent(metaEvent);
    expect(metaEvent.defaultPrevented).toBe(true);

    window.dispatchEvent(
      new KeyboardEvent('keydown', { key: 'Enter', ctrlKey: true, bubbles: true }),
    );
    await flush();
    expect(onSubmit).toHaveBeenCalledTimes(2);
  });

  it('BackTop 超过阈值后出现，点击回到顶部', async () => {
    const scrollSpy = vi.spyOn(window, 'scrollTo').mockImplementation(() => {});
    mount(<BackTop threshold={-1} />);
    await flush();
    const button = container?.querySelector('button');
    expect(button).toBeTruthy();
    if (button) {
      act(() => button.dispatchEvent(new MouseEvent('click', { bubbles: true })));
    }
    await flush();
    expect(scrollSpy).toHaveBeenCalledWith(expect.objectContaining({ top: 0 }));
  });

  it('ScrollToTop 在路由切换时把滚动位置复位到顶部', async () => {
    const scrollSpy = vi.spyOn(window, 'scrollTo').mockImplementation(() => {});
    const Nav = () => {
      const navigate = useNavigate();
      return (
        <button type="button" onClick={() => navigate('/other')}>
          go
        </button>
      );
    };
    mount(
      <MemoryRouter initialEntries={['/']}>
        <ScrollToTop />
        <Routes>
          <Route path="/" element={<Nav />} />
          <Route path="/other" element={<div>other</div>} />
        </Routes>
      </MemoryRouter>,
    );
    scrollSpy.mockClear();

    const button = container?.querySelector('button');
    if (!button) throw new Error('button not rendered');
    act(() => button.dispatchEvent(new MouseEvent('click', { bubbles: true })));
    await flush();
    expect(scrollSpy).toHaveBeenCalledWith(0, 0);
  });
});
