import { renderHook, act } from '@testing-library/react';
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import {
  LanguageProvider,
  useLanguage,
  DEFAULT_LANGUAGES,
  type LanguageDefinition,
} from './LanguageContext';
import React from 'react';

function makeWrapper(props?: Partial<React.ComponentProps<typeof LanguageProvider>>) {
  const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } });
  return ({ children }: { children: React.ReactNode }) => (
    <QueryClientProvider client={queryClient}>
      <LanguageProvider {...props}>{children}</LanguageProvider>
    </QueryClientProvider>
  );
}

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

  describe('default behavior', () => {
    it('provides default language (pt-BR)', () => {
      const { result } = renderHook(() => useLanguage(), { wrapper: makeWrapper() });
      expect(result.current.language).toBe('pt-BR');
    });

    it('exposes the default three languages', () => {
      const { result } = renderHook(() => useLanguage(), { wrapper: makeWrapper() });
      expect(result.current.availableLanguages).toHaveLength(3);
      expect(result.current.availableLanguages.map(l => l.code)).toEqual(['pt-BR', 'en', 'es']);
    });

    it('is not monolingual when using defaults', () => {
      const { result } = renderHook(() => useLanguage(), { wrapper: makeWrapper() });
      expect(result.current.isMonolingual).toBe(false);
    });

    it('updates language', () => {
      const { result } = renderHook(() => useLanguage(), { wrapper: makeWrapper() });
      act(() => result.current.setLanguage('en'));
      expect(result.current.language).toBe('en');
    });
  });

  describe('monolingual mode', () => {
    const englishOnly: LanguageDefinition[] = [{ code: 'en', label: 'English' }];

    it('reports isMonolingual = true with a single language', () => {
      const { result } = renderHook(() => useLanguage(), {
        wrapper: makeWrapper({ availableLanguages: englishOnly }),
      });
      expect(result.current.isMonolingual).toBe(true);
      expect(result.current.availableLanguages).toEqual(englishOnly);
    });

    it('defaults to the only language available', () => {
      const { result } = renderHook(() => useLanguage(), {
        wrapper: makeWrapper({ availableLanguages: englishOnly }),
      });
      expect(result.current.language).toBe('en');
    });
  });

  describe('custom languages', () => {
    it('accepts extra languages beyond the defaults', () => {
      const extended: LanguageDefinition[] = [
        ...DEFAULT_LANGUAGES,
        { code: 'fr', label: 'Français', shortLabel: 'FR' },
      ];
      const { result } = renderHook(() => useLanguage(), {
        wrapper: makeWrapper({ availableLanguages: extended }),
      });
      expect(result.current.availableLanguages).toHaveLength(4);
      act(() => result.current.setLanguage('fr'));
      expect(result.current.language).toBe('fr');
    });

    it('ignores attempts to switch to a language not in the list', () => {
      const { result } = renderHook(() => useLanguage(), {
        wrapper: makeWrapper({ availableLanguages: [{ code: 'en', label: 'English' }] }),
      });
      act(() => result.current.setLanguage('xx-XX' as any));
      // language should remain 'en' (the only valid option)
      expect(result.current.language).toBe('en');
    });
  });

  describe('defaultLanguage prop', () => {
    it('respects an explicit defaultLanguage', () => {
      const { result } = renderHook(() => useLanguage(), {
        wrapper: makeWrapper({ defaultLanguage: 'es' }),
      });
      expect(result.current.language).toBe('es');
    });
  });

  describe('error handling', () => {
    it('throws when availableLanguages is an empty array', () => {
      // Suppress React's error logging for this expected throw
      const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
      expect(() =>
        renderHook(() => useLanguage(), { wrapper: makeWrapper({ availableLanguages: [] }) })
      ).toThrow(/availableLanguages.*cannot be empty/);
      consoleErrorSpy.mockRestore();
    });
  });

  describe('reference stability', () => {
    it('keeps setLanguage stable across re-renders with the same languages', () => {
      const { result, rerender } = renderHook(() => useLanguage(), { wrapper: makeWrapper() });
      const firstSetLanguage = result.current.setLanguage;
      rerender();
      expect(result.current.setLanguage).toBe(firstSetLanguage);
    });
  });
});
