/**
 * Tests for usePrefersReducedMotion hook
 * Tests media query detection for accessibility preferences
 */

import { renderHook, act } from '@testing-library/react';
import { usePrefersReducedMotion } from '@/hooks/usePrefersReducedMotion';

describe('usePrefersReducedMotion', () => {
  let mockMatchMedia: jest.Mock;
  let mockListeners: ((event: MediaQueryListEvent) => void)[];

  beforeEach(() => {
    mockListeners = [];

    mockMatchMedia = jest.fn().mockImplementation((query: string) => ({
      matches: false,
      media: query,
      onchange: null,
      addEventListener: jest.fn((event: string, listener: (event: MediaQueryListEvent) => void) => {
        if (event === 'change') {
          mockListeners.push(listener);
        }
      }),
      removeEventListener: jest.fn(
        (event: string, listener: (event: MediaQueryListEvent) => void) => {
          if (event === 'change') {
            const index = mockListeners.indexOf(listener);
            if (index > -1) {
              mockListeners.splice(index, 1);
            }
          }
        }
      ),
      dispatchEvent: jest.fn(),
    }));

    Object.defineProperty(window, 'matchMedia', {
      writable: true,
      value: mockMatchMedia,
    });
  });

  afterEach(() => {
    jest.clearAllMocks();
  });

  it('returns false when user does not prefer reduced motion', () => {
    mockMatchMedia.mockImplementation((query: string) => ({
      matches: false,
      media: query,
      onchange: null,
      addEventListener: jest.fn(),
      removeEventListener: jest.fn(),
      dispatchEvent: jest.fn(),
    }));

    const { result } = renderHook(() => usePrefersReducedMotion());

    expect(result.current).toBe(false);
    expect(mockMatchMedia).toHaveBeenCalledWith('(prefers-reduced-motion: reduce)');
  });

  it('returns true when user prefers reduced motion', () => {
    mockMatchMedia.mockImplementation((query: string) => ({
      matches: true,
      media: query,
      onchange: null,
      addEventListener: jest.fn(),
      removeEventListener: jest.fn(),
      dispatchEvent: jest.fn(),
    }));

    const { result } = renderHook(() => usePrefersReducedMotion());

    expect(result.current).toBe(true);
  });

  it('updates when media query preference changes to true', () => {
    const mockMediaQuery = {
      matches: false,
      media: '(prefers-reduced-motion: reduce)',
      onchange: null,
      addEventListener: jest.fn((event: string, listener: (event: MediaQueryListEvent) => void) => {
        if (event === 'change') {
          mockListeners.push(listener);
        }
      }),
      removeEventListener: jest.fn(),
      dispatchEvent: jest.fn(),
    };

    mockMatchMedia.mockReturnValue(mockMediaQuery);

    const { result } = renderHook(() => usePrefersReducedMotion());

    expect(result.current).toBe(false);

    // Simulate media query change
    act(() => {
      mockListeners.forEach((listener) => {
        listener({ matches: true } as MediaQueryListEvent);
      });
    });

    expect(result.current).toBe(true);
  });

  it('updates when media query preference changes to false', () => {
    const mockMediaQuery = {
      matches: true,
      media: '(prefers-reduced-motion: reduce)',
      onchange: null,
      addEventListener: jest.fn((event: string, listener: (event: MediaQueryListEvent) => void) => {
        if (event === 'change') {
          mockListeners.push(listener);
        }
      }),
      removeEventListener: jest.fn(),
      dispatchEvent: jest.fn(),
    };

    mockMatchMedia.mockReturnValue(mockMediaQuery);

    const { result } = renderHook(() => usePrefersReducedMotion());

    expect(result.current).toBe(true);

    // Simulate media query change
    act(() => {
      mockListeners.forEach((listener) => {
        listener({ matches: false } as MediaQueryListEvent);
      });
    });

    expect(result.current).toBe(false);
  });

  it('cleans up event listener on unmount', () => {
    const removeEventListenerSpy = jest.fn();

    const mockMediaQuery = {
      matches: false,
      media: '(prefers-reduced-motion: reduce)',
      onchange: null,
      addEventListener: jest.fn((event: string, listener: (event: MediaQueryListEvent) => void) => {
        if (event === 'change') {
          mockListeners.push(listener);
        }
      }),
      removeEventListener: removeEventListenerSpy,
      dispatchEvent: jest.fn(),
    };

    mockMatchMedia.mockReturnValue(mockMediaQuery);

    const { unmount } = renderHook(() => usePrefersReducedMotion());

    expect(mockMediaQuery.addEventListener).toHaveBeenCalledWith('change', expect.any(Function));

    unmount();

    expect(removeEventListenerSpy).toHaveBeenCalledWith('change', expect.any(Function));
  });

  it('handles SSR environment safely', () => {
    const originalWindow = global.window;

    // @ts-expect-error - Simulating SSR
    delete global.window;

    const { result } = renderHook(() => usePrefersReducedMotion());

    // Should return false in SSR environment
    expect(result.current).toBe(false);

    // Restore window
    global.window = originalWindow;
  });
});
