import { renderHook, act } from '@testing-library/react';
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { ThemeProvider, useTheme } from '@/contexts/ThemeContext';

describe('ThemeContext', () => {
  beforeEach(() => {
    localStorage.clear();
  });

  it('returns default theme on initial load', () => {
    const { result } = renderHook(() => useTheme(), { wrapper: ThemeProvider });

    expect(result.current.themeId).toBe('default');
    expect(result.current.followSystem).toBe(false);
    expect(result.current.systemIsDark).toBe(false);
    expect(result.current.effectiveThemeId).toBe('default');
  });

  it('restores themeId from localStorage', () => {
    localStorage.setItem('nex-theme-id', 'mui');

    const { result } = renderHook(() => useTheme(), { wrapper: ThemeProvider });

    expect(result.current.themeId).toBe('mui');
  });

  it('restores followSystem from localStorage', () => {
    localStorage.setItem('nex-follow-system', 'true');

    const { result } = renderHook(() => useTheme(), { wrapper: ThemeProvider });

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

  it('falls back to default for invalid themeId', () => {
    localStorage.setItem('nex-theme-id', 'invalid-theme');

    const { result } = renderHook(() => useTheme(), { wrapper: ThemeProvider });

    expect(result.current.themeId).toBe('default');
  });

  it('setThemeId updates themeId and persists to localStorage', () => {
    const { result } = renderHook(() => useTheme(), { wrapper: ThemeProvider });

    act(() => {
      result.current.setThemeId('shadcn');
    });

    expect(result.current.themeId).toBe('shadcn');
    expect(localStorage.getItem('nex-theme-id')).toBe('shadcn');
  });

  it('setFollowSystem updates followSystem and persists to localStorage', () => {
    const { result } = renderHook(() => useTheme(), { wrapper: ThemeProvider });

    act(() => {
      result.current.setFollowSystem(true);
    });

    expect(result.current.followSystem).toBe(true);
    expect(localStorage.getItem('nex-follow-system')).toBe('true');
  });

  it('computes effectiveThemeId as dark when followSystem and systemIsDark', () => {
    localStorage.setItem('nex-theme-id', 'mui');
    localStorage.setItem('nex-follow-system', 'true');

    const mediaQuerySpy = vi.spyOn(window, 'matchMedia').mockReturnValue({
      matches: true,
      media: '(prefers-color-scheme: dark)',
      onchange: null,
      addListener: vi.fn(),
      removeListener: vi.fn(),
      addEventListener: vi.fn(),
      removeEventListener: vi.fn(),
      dispatchEvent: vi.fn(),
    });

    const { result } = renderHook(() => useTheme(), { wrapper: ThemeProvider });

    expect(result.current.effectiveThemeId).toBe('dark');

    mediaQuerySpy.mockRestore();
  });

  it('computes effectiveThemeId as themeId when followSystem but system is light', () => {
    localStorage.setItem('nex-theme-id', 'mui');
    localStorage.setItem('nex-follow-system', 'true');

    const { result } = renderHook(() => useTheme(), { wrapper: ThemeProvider });

    expect(result.current.effectiveThemeId).toBe('mui');
  });

  it('computes effectiveThemeId as themeId when followSystem is off', () => {
    localStorage.setItem('nex-theme-id', 'glass');
    localStorage.setItem('nex-follow-system', 'false');

    const mediaQuerySpy = vi.spyOn(window, 'matchMedia').mockReturnValue({
      matches: true,
      media: '(prefers-color-scheme: dark)',
      onchange: null,
      addListener: vi.fn(),
      removeListener: vi.fn(),
      addEventListener: vi.fn(),
      removeEventListener: vi.fn(),
      dispatchEvent: vi.fn(),
    });

    const { result } = renderHook(() => useTheme(), { wrapper: ThemeProvider });

    expect(result.current.effectiveThemeId).toBe('glass');

    mediaQuerySpy.mockRestore();
  });

  it('throws error when useTheme is used outside ThemeProvider', () => {
    expect(() => {
      renderHook(() => useTheme());
    }).toThrow('useTheme must be used within ThemeProvider');
  });
});
