import { act, fireEvent, render, screen } from '@testing-library/react';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { MainLayout } from '@/components/layout/MainLayout';
import { useOpencodeStore } from '@/stores/opencode';
import { useSettingsStore } from '@/stores/settings';

type PeekProps = {
  sidebarPeekOpen?: boolean;
  onSidebarPeekChange?: (open: boolean, source: 'toggle' | 'focus' | 'sidebar' | 'titlebar') => void;
};

vi.mock('@/components/layout/TitleBar', () => ({
  TitleBar: ({ sidebarPeekOpen, onSidebarPeekChange }: PeekProps) => (
    <>
      <button
        type="button"
        data-testid="peek-trigger"
        data-peek-open={sidebarPeekOpen ? 'true' : 'false'}
        onPointerEnter={() => onSidebarPeekChange?.(true, 'toggle')}
        onPointerLeave={() => onSidebarPeekChange?.(false, 'toggle')}
      />
      <div
        data-testid="peek-titlebar"
        onPointerEnter={() => onSidebarPeekChange?.(true, 'titlebar')}
        onPointerLeave={() => onSidebarPeekChange?.(false, 'titlebar')}
      />
    </>
  ),
}));

vi.mock('@/components/layout/Sidebar', () => ({
  Sidebar: ({ sidebarPeekOpen, onSidebarPeekChange }: PeekProps) => (
    <aside
      data-testid="peek-sidebar"
      data-peek-open={sidebarPeekOpen ? 'true' : 'false'}
      onPointerEnter={() => onSidebarPeekChange?.(true, 'sidebar')}
      onPointerLeave={() => onSidebarPeekChange?.(false, 'sidebar')}
    />
  ),
}));

function renderLayout() {
  render(
    <MemoryRouter initialEntries={['/opencode-chat']}>
      <Routes>
        <Route element={<MainLayout />}>
          <Route path="*" element={null} />
        </Route>
      </Routes>
    </MemoryRouter>,
  );
}

describe('MainLayout sidebar hover preview', () => {
  beforeEach(() => {
    vi.useFakeTimers();
    useSettingsStore.setState({ sidebarCollapsed: true });
    useOpencodeStore.setState({ activeProject: null });
  });

  afterEach(() => {
    vi.useRealTimers();
  });

  it('does not close while the pointer moves from the toggle into the preview', () => {
    renderLayout();

    const trigger = screen.getByTestId('peek-trigger');
    const sidebar = screen.getByTestId('peek-sidebar');
    fireEvent.pointerEnter(trigger);
    expect(trigger).toHaveAttribute('data-peek-open', 'true');

    fireEvent.pointerLeave(trigger);
    fireEvent.pointerEnter(sidebar);
    act(() => {
      vi.advanceTimersByTime(180);
    });

    expect(sidebar).toHaveAttribute('data-peek-open', 'true');

    fireEvent.pointerLeave(sidebar);
    act(() => {
      vi.advanceTimersByTime(179);
    });
    expect(sidebar).toHaveAttribute('data-peek-open', 'true');

    act(() => {
      vi.advanceTimersByTime(1);
    });
    expect(sidebar).toHaveAttribute('data-peek-open', 'false');
  });

  it('keeps the preview open while the pointer crosses the expanded titlebar surface', () => {
    renderLayout();

    const trigger = screen.getByTestId('peek-trigger');
    const titlebar = screen.getByTestId('peek-titlebar');
    fireEvent.pointerEnter(trigger);
    fireEvent.pointerLeave(trigger);
    fireEvent.pointerEnter(titlebar);

    act(() => vi.advanceTimersByTime(180));
    expect(screen.getByTestId('peek-trigger')).toHaveAttribute('data-peek-open', 'true');

    fireEvent.pointerLeave(titlebar);
    act(() => vi.advanceTimersByTime(180));
    expect(screen.getByTestId('peek-trigger')).toHaveAttribute('data-peek-open', 'false');
  });

  it('closes once after leaving the only hover zone', () => {
    renderLayout();

    const trigger = screen.getByTestId('peek-trigger');
    fireEvent.pointerEnter(trigger);
    fireEvent.pointerLeave(trigger);

    act(() => {
      vi.advanceTimersByTime(179);
    });
    expect(trigger).toHaveAttribute('data-peek-open', 'true');

    act(() => {
      vi.advanceTimersByTime(1);
    });
    expect(trigger).toHaveAttribute('data-peek-open', 'false');
  });

  it('treats repeated pointer-enter notifications as one hover source', () => {
    renderLayout();

    const trigger = screen.getByTestId('peek-trigger');
    fireEvent.pointerEnter(trigger);
    fireEvent.pointerEnter(trigger);
    fireEvent.pointerLeave(trigger);
    fireEvent.pointerLeave(trigger);

    act(() => {
      vi.advanceTimersByTime(180);
    });
    expect(trigger).toHaveAttribute('data-peek-open', 'false');
  });
});
