import React from 'react';
import { act, render, screen } from '@testing-library/react';
import { ThemeProvider } from 'styled-components';
import ActivityIndicator from './ActivityIndicator';
import type { ActivityHistoryEntry } from '../../utils/wsFrames';

const theme = {
  main: '#336699',
  focus: '#224466',
  darkMode: true,
  colors: {
    text: '#ffffff',
    cardBackground: 'rgba(0,0,0,0.3)',
    cardBorder: 'rgba(255,255,255,0.1)',
  },
};

const renderIndicator = (
  props: React.ComponentProps<typeof ActivityIndicator>,
) =>
  render(
    <ThemeProvider theme={theme as never}>
      <ActivityIndicator {...props} />
    </ThemeProvider>,
  );

describe('ActivityIndicator (#96)', () => {
  it('has status role and polite live region regardless of state', () => {
    renderIndicator({ stage: null });
    const status = screen.getByRole('status');
    expect(status).toHaveAttribute('aria-live', 'polite');
  });

  it('falls back to three dots when stage is null', () => {
    renderIndicator({ stage: null });
    expect(screen.getByTestId('activity-dots-fallback')).toBeInTheDocument();
  });

  it('renders the label verbatim for unknown/future stage values', () => {
    renderIndicator({ stage: 'some_future_stage', label: 'Doing something new' });
    expect(screen.getByText('Doing something new')).toBeInTheDocument();
    expect(screen.queryByTestId('activity-dots-fallback')).not.toBeInTheDocument();
  });

  it('shows optional detail text', () => {
    renderIndicator({
      stage: 'searching',
      label: 'Searching the web',
      detail: 'query: best pizza in town',
    });
    expect(screen.getByText('Searching the web')).toBeInTheDocument();
    expect(screen.getByText('query: best pizza in town')).toBeInTheDocument();
  });

  it('renders completed stages above with check marks', () => {
    const history: ActivityHistoryEntry[] = [
      { stage: 'searching', label: 'Searched the web', startedAt: 0, finishedAt: 100 },
      { stage: 'reading', label: 'Read 3 sources', startedAt: 100, finishedAt: 400 },
    ];
    renderIndicator({
      stage: 'writing',
      label: null,
      history,
    });
    expect(screen.getByText('Searched the web')).toBeInTheDocument();
    expect(screen.getByText('Read 3 sources')).toBeInTheDocument();
    expect(screen.getAllByText('✓')).toHaveLength(2);
  });

  it('hides everything when interrupted', () => {
    const { container } = renderIndicator({
      stage: 'searching',
      label: 'Searching the web',
      interrupted: true,
    });
    expect(container).toBeEmptyDOMElement();
  });

  it('shows a generic label when stage is present but label is cleared (writing stage)', () => {
    renderIndicator({ stage: 'writing', label: null });
    expect(screen.getByText('Working…')).toBeInTheDocument();
  });

  describe('prefers-reduced-motion', () => {
    const originalMatchMedia = window.matchMedia;

    afterEach(() => {
      Object.defineProperty(window, 'matchMedia', {
        configurable: true,
        writable: true,
        value: originalMatchMedia,
      });
    });

    it('disables the spinner animation when the user prefers reduced motion', () => {
      const matchMediaMock = jest.fn().mockImplementation((query: string) => ({
        matches: query.includes('prefers-reduced-motion'),
        media: query,
        addEventListener: jest.fn(),
        removeEventListener: jest.fn(),
        addListener: jest.fn(),
        removeListener: jest.fn(),
      }));
      Object.defineProperty(window, 'matchMedia', {
        configurable: true,
        writable: true,
        value: matchMediaMock,
      });

      renderIndicator({ stage: 'searching', label: 'Searching the web' });
      const spinner = screen.getByTestId('activity-spinner');
      expect(spinner).toHaveAttribute('data-reduced-motion', 'true');
    });
  });

  it('shows elapsed time after ~10s and "Still working…" after ~30s of silence', () => {
    jest.useFakeTimers();
    try {
      renderIndicator({ stage: 'searching', label: 'Searching the web' });

      act(() => {
        jest.advanceTimersByTime(11_000);
      });
      expect(screen.getByText('11s')).toBeInTheDocument();

      act(() => {
        jest.advanceTimersByTime(20_000);
      });
      expect(screen.getByText('Still working…')).toBeInTheDocument();
    } finally {
      jest.useRealTimers();
    }
  });
});
