/**
 * Unit tests for internationalization (i18n) functionality
 * Tests translation loading, language switching, and text updates
 */

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

describe('Localization', () => {
    let localization;
    let originalLocalStorage;
    let originalNavigator;

    beforeEach(() => {
        // Mock localStorage
        originalLocalStorage = global.localStorage;
        global.localStorage = {
            data: {},
            getItem(key) {
                return this.data[key] || null;
            },
            setItem(key, value) {
                this.data[key] = value;
            },
            removeItem(key) {
                delete this.data[key];
            },
            clear() {
                this.data = {};
            }
        };

        // Mock navigator
        originalNavigator = global.navigator;
        global.navigator = {
            language: 'en-US',
            userLanguage: 'en-US'
        };

        // Mock document
        global.document = {
            querySelectorAll: vi.fn(() => []),
            getElementById: vi.fn(() => null)
        };

        // Load the Localization class
        const LocalizationClass = class Localization {
            constructor() {
                this.currentLanguage = 'en';
                this.fallbackLanguage = 'en';
                this.translations = {};
                this.loadTranslations();
            }

            loadTranslations() {
                this.translations.en = {
                    'config-title': 'Configuration',
                    'toggle-theme': 'Toggle theme',
                    'search-placeholder': 'Search for anime...',
                    'series-collection': 'Series Collection',
                    'download-queue': 'Download Queue',
                    'loading': 'Loading...',
                    'close': 'Close',
                    'ok': 'OK'
                };

                this.translations.de = {
                    'config-title': 'Konfiguration',
                    'toggle-theme': 'Design wechseln',
                    'search-placeholder': 'Nach Anime suchen...',
                    'series-collection': 'Serien-Sammlung',
                    'download-queue': 'Download-Warteschlange',
                    'loading': 'Wird geladen...',
                    'close': 'Schließen',
                    'ok': 'OK'
                };

                const savedLanguage = localStorage.getItem('language') || this.detectLanguage();
                this.setLanguage(savedLanguage);
            }

            detectLanguage() {
                const browserLang = navigator.language || navigator.userLanguage;
                const langCode = browserLang.split('-')[0];
                return this.translations[langCode] ? langCode : this.fallbackLanguage;
            }

            setLanguage(langCode) {
                if (this.translations[langCode]) {
                    this.currentLanguage = langCode;
                    localStorage.setItem('language', langCode);
                    this.updatePageTexts();
                }
            }

            getText(key, fallback = key) {
                const translation = this.translations[this.currentLanguage];
                if (translation && translation[key]) {
                    return translation[key];
                }

                const fallbackTranslation = this.translations[this.fallbackLanguage];
                if (fallbackTranslation && fallbackTranslation[key]) {
                    return fallbackTranslation[key];
                }

                return fallback;
            }

            updatePageTexts() {
                document.querySelectorAll('[data-text]').forEach(element => {
                    const key = element.getAttribute('data-text');
                    const text = this.getText(key);
                    
                    if (element.tagName === 'INPUT' && element.type === 'text') {
                        element.placeholder = text;
                    } else {
                        element.textContent = text;
                    }
                });
            }

            getAvailableLanguages() {
                return Object.keys(this.translations).map(code => ({
                    code: code,
                    name: this.getLanguageName(code)
                }));
            }

            getLanguageName(code) {
                const names = {
                    'en': 'English',
                    'de': 'Deutsch'
                };
                return names[code] || code.toUpperCase();
            }

            formatMessage(key, ...args) {
                let message = this.getText(key);
                args.forEach((arg, index) => {
                    message = message.replace(`{${index}}`, arg);
                });
                return message;
            }
        };

        localization = new LocalizationClass();
    });

    afterEach(() => {
        global.localStorage = originalLocalStorage;
        global.navigator = originalNavigator;
    });

    describe('Initialization', () => {
        it('should initialize with default English language', () => {
            expect(localization.currentLanguage).toBe('en');
            expect(localization.fallbackLanguage).toBe('en');
        });

        it('should load English translations', () => {
            expect(localization.translations.en).toBeDefined();
            expect(localization.translations.en['config-title']).toBe('Configuration');
        });

        it('should load German translations', () => {
            expect(localization.translations.de).toBeDefined();
            expect(localization.translations.de['config-title']).toBe('Konfiguration');
        });

        it('should detect browser language', () => {
            global.navigator.language = 'de-DE';
            const langCode = localization.detectLanguage();
            expect(langCode).toBe('de');
        });

        it('should fallback to English for unsupported languages', () => {
            global.navigator.language = 'fr-FR';
            const langCode = localization.detectLanguage();
            expect(langCode).toBe('en');
        });

        it('should load saved language from localStorage', () => {
            localStorage.setItem('language', 'de');
            const LocalizationClass = localization.constructor;
            const newInstance = new LocalizationClass();
            expect(newInstance.currentLanguage).toBe('de');
        });
    });

    describe('Language Switching', () => {
        it('should switch to German', () => {
            localization.setLanguage('de');
            expect(localization.currentLanguage).toBe('de');
        });

        it('should switch to English', () => {
            localization.setLanguage('en');
            expect(localization.currentLanguage).toBe('en');
        });

        it('should save language preference to localStorage', () => {
            localization.setLanguage('de');
            expect(localStorage.getItem('language')).toBe('de');
        });

        it('should not switch to unsupported language', () => {
            const originalLang = localization.currentLanguage;
            localization.setLanguage('fr');
            expect(localization.currentLanguage).toBe(originalLang);
        });

        it('should update page texts when switching language', () => {
            const mockElements = [
                {
                    getAttribute: () => 'config-title',
                    textContent: '',
                    tagName: 'H1'
                }
            ];
            document.querySelectorAll = vi.fn(() => mockElements);

            localization.setLanguage('de');
            expect(document.querySelectorAll).toHaveBeenCalledWith('[data-text]');
        });
    });

    describe('Text Retrieval', () => {
        it('should get English text for current language', () => {
            localization.setLanguage('en');
            const text = localization.getText('config-title');
            expect(text).toBe('Configuration');
        });

        it('should get German text for current language', () => {
            localization.setLanguage('de');
            const text = localization.getText('config-title');
            expect(text).toBe('Konfiguration');
        });

        it('should return fallback for missing key in current language', () => {
            localization.setLanguage('de');
            const text = localization.getText('nonexistent-key', 'Fallback');
            expect(text).toBe('Fallback');
        });

        it('should return key as fallback when no translation exists', () => {
            const text = localization.getText('missing-key');
            expect(text).toBe('missing-key');
        });

        it('should fallback to English for missing German translations', () => {
            localization.setLanguage('de');
            delete localization.translations.de['loading'];
            const text = localization.getText('loading');
            expect(text).toBe('Loading...');
        });
    });

    describe('Page Text Updates', () => {
        it('should update element text content', () => {
            const mockElement = {
                getAttribute: () => 'config-title',
                textContent: '',
                tagName: 'H1'
            };
            document.querySelectorAll = vi.fn(() => [mockElement]);

            localization.setLanguage('en');
            localization.updatePageTexts();

            expect(mockElement.textContent).toBe('Configuration');
        });

        it('should update input placeholder', () => {
            const mockInput = {
                getAttribute: () => 'search-placeholder',
                placeholder: '',
                tagName: 'INPUT',
                type: 'text'
            };
            document.querySelectorAll = vi.fn(() => [mockInput]);

            localization.setLanguage('en');
            localization.updatePageTexts();

            expect(mockInput.placeholder).toBe('Search for anime...');
        });

        it('should update multiple elements', () => {
            const mockElements = [
                { getAttribute: () => 'config-title', textContent: '', tagName: 'H1' },
                { getAttribute: () => 'loading', textContent: '', tagName: 'SPAN' },
                { getAttribute: () => 'close', textContent: '', tagName: 'BUTTON' }
            ];
            document.querySelectorAll = vi.fn(() => mockElements);

            localization.updatePageTexts();

            expect(mockElements[0].textContent).toBe('Configuration');
            expect(mockElements[1].textContent).toBe('Loading...');
            expect(mockElements[2].textContent).toBe('Close');
        });

        it('should handle missing elements gracefully', () => {
            document.querySelectorAll = vi.fn(() => []);
            expect(() => localization.updatePageTexts()).not.toThrow();
        });
    });

    describe('Available Languages', () => {
        it('should return list of available languages', () => {
            const languages = localization.getAvailableLanguages();
            expect(languages).toHaveLength(2);
            expect(languages).toContainEqual({ code: 'en', name: 'English' });
            expect(languages).toContainEqual({ code: 'de', name: 'Deutsch' });
        });

        it('should get English language name', () => {
            const name = localization.getLanguageName('en');
            expect(name).toBe('English');
        });

        it('should get German language name', () => {
            const name = localization.getLanguageName('de');
            expect(name).toBe('Deutsch');
        });

        it('should return uppercase code for unknown language', () => {
            const name = localization.getLanguageName('fr');
            expect(name).toBe('FR');
        });
    });

    describe('Message Formatting', () => {
        it('should format message with single argument', () => {
            localization.translations.en['welcome-message'] = 'Welcome, {0}!';
            const formatted = localization.formatMessage('welcome-message', 'John');
            expect(formatted).toBe('Welcome, John!');
        });

        it('should format message with multiple arguments', () => {
            localization.translations.en['greeting'] = 'Hello {0}, you have {1} new messages';
            const formatted = localization.formatMessage('greeting', 'Alice', '5');
            expect(formatted).toBe('Hello Alice, you have 5 new messages');
        });

        it('should handle missing placeholders', () => {
            localization.translations.en['simple'] = 'No placeholders here';
            const formatted = localization.formatMessage('simple', 'unused');
            expect(formatted).toBe('No placeholders here');
        });

        it('should format message in German', () => {
            localization.setLanguage('de');
            localization.translations.de['welcome-message'] = 'Willkommen, {0}!';
            const formatted = localization.formatMessage('welcome-message', 'Johann');
            expect(formatted).toBe('Willkommen, Johann!');
        });
    });

    describe('Translation Completeness', () => {
        it('should have same keys in English and German', () => {
            const enKeys = Object.keys(localization.translations.en);
            const deKeys = Object.keys(localization.translations.de);
            
            expect(enKeys.sort()).toEqual(deKeys.sort());
        });

        it('should not have empty translations', () => {
            const checkTranslations = (lang) => {
                const translations = localization.translations[lang];
                Object.entries(translations).forEach(([key, value]) => {
                    expect(value).toBeTruthy();
                    expect(value.trim()).not.toBe('');
                });
            };

            checkTranslations('en');
            checkTranslations('de');
        });

        it('should have non-identical translations for different languages', () => {
            const enKeys = Object.keys(localization.translations.en);
            let identicalCount = 0;

            enKeys.forEach(key => {
                if (localization.translations.en[key] === localization.translations.de[key]) {
                    identicalCount++;
                }
            });

            // Some translations might be identical (e.g., "OK"), but not all
            expect(identicalCount).toBeLessThan(enKeys.length);
        });
    });

    describe('Edge Cases', () => {
        it('should handle null key', () => {
            const text = localization.getText(null);
            expect(text).toBe(null);
        });

        it('should handle undefined key', () => {
            const text = localization.getText(undefined);
            expect(text).toBe(undefined);
        });

        it('should handle empty string key', () => {
            const text = localization.getText('');
            expect(text).toBe('');
        });

        it('should handle rapid language switching', () => {
            localization.setLanguage('de');
            localization.setLanguage('en');
            localization.setLanguage('de');
            localization.setLanguage('en');
            
            expect(localization.currentLanguage).toBe('en');
        });

        it('should handle localStorage errors gracefully', () => {
            const originalSetItem = localStorage.setItem;
            localStorage.setItem = () => {
                throw new Error('localStorage full');
            };

            expect(() => localization.setLanguage('de')).not.toThrow();
            
            localStorage.setItem = originalSetItem;
        });

        it('should handle missing navigator.language', () => {
            delete global.navigator.language;
            global.navigator.userLanguage = 'de-DE';
            
            const langCode = localization.detectLanguage();
            expect(langCode).toBe('de');
        });

        it('should handle missing both language properties', () => {
            delete global.navigator.language;
            delete global.navigator.userLanguage;
            
            const langCode = localization.detectLanguage();
            expect(langCode).toBe('en');
        });
    });

    describe('Integration with Document', () => {
        it('should query for elements with data-text attribute', () => {
            const spy = vi.spyOn(document, 'querySelectorAll');
            localization.updatePageTexts();
            expect(spy).toHaveBeenCalledWith('[data-text]');
        });

        it('should handle elements without getAttribute method', () => {
            const mockElements = [
                { getAttribute: undefined, textContent: '', tagName: 'DIV' }
            ];
            document.querySelectorAll = vi.fn(() => mockElements);

            expect(() => localization.updatePageTexts()).not.toThrow();
        });

        it('should handle elements with null attributes', () => {
            const mockElements = [
                { getAttribute: () => null, textContent: '', tagName: 'DIV' }
            ];
            document.querySelectorAll = vi.fn(() => mockElements);

            expect(() => localization.updatePageTexts()).not.toThrow();
        });
    });

    describe('Persistence', () => {
        it('should persist language across page reloads', () => {
            localization.setLanguage('de');
            const savedLang = localStorage.getItem('language');
            expect(savedLang).toBe('de');

            // Simulate page reload
            const LocalizationClass = localization.constructor;
            const newInstance = new LocalizationClass();
            expect(newInstance.currentLanguage).toBe('de');
        });

        it('should clear previous language when switching', () => {
            localization.setLanguage('de');
            expect(localStorage.getItem('language')).toBe('de');

            localization.setLanguage('en');
            expect(localStorage.getItem('language')).toBe('en');
        });
    });
});
