/**
 * Unit tests for user preferences functionality
 * Tests preference storage, loading, and application
 */

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

describe('UserPreferences', () => {
    let originalLocalStorage;
    let originalDocument;
    let consoleErrorSpy;

    const STORAGE_KEY = 'aniworld_preferences';

    // User Preferences module implementation
    const createUserPreferences = () => {
        const UserPrefs = {
            loadPreferences() {
                try {
                    const stored = localStorage.getItem(STORAGE_KEY);
                    if (stored) {
                        const preferences = JSON.parse(stored);
                        this.applyPreferences(preferences);
                        return preferences;
                    }
                    return {};
                } catch (error) {
                    console.error('[User Preferences] Error loading:', error);
                    return {};
                }
            },

            savePreferences(preferences) {
                try {
                    localStorage.setItem(STORAGE_KEY, JSON.stringify(preferences));
                    return true;
                } catch (error) {
                    console.error('[User Preferences] Error saving:', error);
                    return false;
                }
            },

            applyPreferences(preferences) {
                if (preferences.theme) {
                    document.documentElement.setAttribute('data-theme', preferences.theme);
                }
                if (preferences.language) {
                    // Language preference would be applied here
                    document.documentElement.setAttribute('lang', preferences.language);
                }
            },

            getPreferences() {
                try {
                    const stored = localStorage.getItem(STORAGE_KEY);
                    return stored ? JSON.parse(stored) : {};
                } catch (error) {
                    console.error('[User Preferences] Error getting preferences:', error);
                    return {};
                }
            },

            updatePreference(key, value) {
                const preferences = this.getPreferences();
                preferences[key] = value;
                this.savePreferences(preferences);
                this.applyPreferences(preferences);
            },

            resetPreferences() {
                try {
                    localStorage.removeItem(STORAGE_KEY);
                    return true;
                } catch (error) {
                    console.error('[User Preferences] Error resetting:', error);
                    return false;
                }
            }
        };

        return UserPrefs;
    };

    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 document
        originalDocument = global.document;
        global.document = {
            documentElement: {
                attributes: {},
                setAttribute(key, value) {
                    this.attributes[key] = value;
                },
                getAttribute(key) {
                    return this.attributes[key] || null;
                },
                removeAttribute(key) {
                    delete this.attributes[key];
                }
            },
            readyState: 'complete',
            addEventListener: vi.fn()
        };

        // Spy on console.error
        consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
    });

    afterEach(() => {
        global.localStorage = originalLocalStorage;
        global.document = originalDocument;
        consoleErrorSpy.mockRestore();
    });

    describe('Loading Preferences', () => {
        it('should load preferences from localStorage', () => {
            const UserPrefs = createUserPreferences();
            const testPrefs = { theme: 'dark', language: 'de' };
            localStorage.setItem(STORAGE_KEY, JSON.stringify(testPrefs));

            const loaded = UserPrefs.loadPreferences();

            expect(loaded).toEqual(testPrefs);
        });

        it('should return empty object when no preferences exist', () => {
            const UserPrefs = createUserPreferences();
            const loaded = UserPrefs.loadPreferences();

            expect(loaded).toEqual({});
        });

        it('should handle invalid JSON gracefully', () => {
            const UserPrefs = createUserPreferences();
            localStorage.setItem(STORAGE_KEY, 'invalid-json{');

            const loaded = UserPrefs.loadPreferences();

            expect(loaded).toEqual({});
            expect(consoleErrorSpy).toHaveBeenCalled();
        });

        it('should handle localStorage errors', () => {
            const UserPrefs = createUserPreferences();
            const originalGetItem = localStorage.getItem;
            localStorage.getItem = () => {
                throw new Error('localStorage error');
            };

            const loaded = UserPrefs.loadPreferences();

            expect(loaded).toEqual({});
            expect(consoleErrorSpy).toHaveBeenCalled();

            localStorage.getItem = originalGetItem;
        });

        it('should apply preferences after loading', () => {
            const UserPrefs = createUserPreferences();
            const testPrefs = { theme: 'dark' };
            localStorage.setItem(STORAGE_KEY, JSON.stringify(testPrefs));

            UserPrefs.loadPreferences();

            expect(document.documentElement.getAttribute('data-theme')).toBe('dark');
        });
    });

    describe('Saving Preferences', () => {
        it('should save preferences to localStorage', () => {
            const UserPrefs = createUserPreferences();
            const testPrefs = { theme: 'light', language: 'en' };

            const result = UserPrefs.savePreferences(testPrefs);

            expect(result).toBe(true);
            expect(localStorage.getItem(STORAGE_KEY)).toBe(JSON.stringify(testPrefs));
        });

        it('should overwrite existing preferences', () => {
            const UserPrefs = createUserPreferences();
            localStorage.setItem(STORAGE_KEY, JSON.stringify({ theme: 'dark' }));

            UserPrefs.savePreferences({ theme: 'light' });

            const stored = JSON.parse(localStorage.getItem(STORAGE_KEY));
            expect(stored.theme).toBe('light');
        });

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

            const result = UserPrefs.savePreferences({ theme: 'dark' });

            expect(result).toBe(false);
            expect(consoleErrorSpy).toHaveBeenCalled();

            localStorage.setItem = originalSetItem;
        });

        it('should handle null preferences', () => {
            const UserPrefs = createUserPreferences();

            expect(() => UserPrefs.savePreferences(null)).not.toThrow();
        });

        it('should handle undefined preferences', () => {
            const UserPrefs = createUserPreferences();

            expect(() => UserPrefs.savePreferences(undefined)).not.toThrow();
        });
    });

    describe('Getting Preferences', () => {
        it('should get stored preferences', () => {
            const UserPrefs = createUserPreferences();
            const testPrefs = { theme: 'dark', language: 'de' };
            localStorage.setItem(STORAGE_KEY, JSON.stringify(testPrefs));

            const prefs = UserPrefs.getPreferences();

            expect(prefs).toEqual(testPrefs);
        });

        it('should return empty object when none exist', () => {
            const UserPrefs = createUserPreferences();

            const prefs = UserPrefs.getPreferences();

            expect(prefs).toEqual({});
        });

        it('should handle parse errors gracefully', () => {
            const UserPrefs = createUserPreferences();
            localStorage.setItem(STORAGE_KEY, '{invalid}');

            const prefs = UserPrefs.getPreferences();

            expect(prefs).toEqual({});
            expect(consoleErrorSpy).toHaveBeenCalled();
        });

        it('should not modify original stored data', () => {
            const UserPrefs = createUserPreferences();
            const testPrefs = { theme: 'dark' };
            localStorage.setItem(STORAGE_KEY, JSON.stringify(testPrefs));

            const prefs = UserPrefs.getPreferences();
            prefs.theme = 'light';

            const storedPrefs = UserPrefs.getPreferences();
            expect(storedPrefs.theme).toBe('dark');
        });
    });

    describe('Applying Preferences', () => {
        it('should apply theme preference to document', () => {
            const UserPrefs = createUserPreferences();
            const preferences = { theme: 'dark' };

            UserPrefs.applyPreferences(preferences);

            expect(document.documentElement.getAttribute('data-theme')).toBe('dark');
        });

        it('should apply language preference to document', () => {
            const UserPrefs = createUserPreferences();
            const preferences = { language: 'de' };

            UserPrefs.applyPreferences(preferences);

            expect(document.documentElement.getAttribute('lang')).toBe('de');
        });

        it('should apply multiple preferences', () => {
            const UserPrefs = createUserPreferences();
            const preferences = { theme: 'dark', language: 'de' };

            UserPrefs.applyPreferences(preferences);

            expect(document.documentElement.getAttribute('data-theme')).toBe('dark');
            expect(document.documentElement.getAttribute('lang')).toBe('de');
        });

        it('should handle empty preferences object', () => {
            const UserPrefs = createUserPreferences();

            expect(() => UserPrefs.applyPreferences({})).not.toThrow();
        });

        it('should handle undefined preferences', () => {
            const UserPrefs = createUserPreferences();

            expect(() => UserPrefs.applyPreferences(undefined)).not.toThrow();
        });

        it('should only apply defined preferences', () => {
            const UserPrefs = createUserPreferences();
            const preferences = { theme: 'dark' };

            UserPrefs.applyPreferences(preferences);

            expect(document.documentElement.getAttribute('data-theme')).toBe('dark');
            expect(document.documentElement.getAttribute('lang')).toBeNull();
        });
    });

    describe('Updating Specific Preference', () => {
        it('should update single preference', () => {
            const UserPrefs = createUserPreferences();
            
            UserPrefs.updatePreference('theme', 'dark');

            const prefs = UserPrefs.getPreferences();
            expect(prefs.theme).toBe('dark');
        });

        it('should update existing preference', () => {
            const UserPrefs = createUserPreferences();
            UserPrefs.savePreferences({ theme: 'light', language: 'en' });

            UserPrefs.updatePreference('theme', 'dark');

            const prefs = UserPrefs.getPreferences();
            expect(prefs.theme).toBe('dark');
            expect(prefs.language).toBe('en');
        });

        it('should add new preference to existing ones', () => {
            const UserPrefs = createUserPreferences();
            UserPrefs.savePreferences({ theme: 'light' });

            UserPrefs.updatePreference('language', 'de');

            const prefs = UserPrefs.getPreferences();
            expect(prefs.theme).toBe('light');
            expect(prefs.language).toBe('de');
        });

        it('should apply preference after updating', () => {
            const UserPrefs = createUserPreferences();

            UserPrefs.updatePreference('theme', 'dark');

            expect(document.documentElement.getAttribute('data-theme')).toBe('dark');
        });

        it('should persist updated preference', () => {
            const UserPrefs = createUserPreferences();

            UserPrefs.updatePreference('theme', 'dark');

            const stored = JSON.parse(localStorage.getItem(STORAGE_KEY));
            expect(stored.theme).toBe('dark');
        });
    });

    describe('Resetting Preferences', () => {
        it('should remove preferences from localStorage', () => {
            const UserPrefs = createUserPreferences();
            UserPrefs.savePreferences({ theme: 'dark' });

            const result = UserPrefs.resetPreferences();

            expect(result).toBe(true);
            expect(localStorage.getItem(STORAGE_KEY)).toBeNull();
        });

        it('should handle missing preferences gracefully', () => {
            const UserPrefs = createUserPreferences();

            expect(() => UserPrefs.resetPreferences()).not.toThrow();
        });

        it('should handle localStorage errors', () => {
            const UserPrefs = createUserPreferences();
            const originalRemoveItem = localStorage.removeItem;
            localStorage.removeItem = () => {
                throw new Error('localStorage error');
            };

            const result = UserPrefs.resetPreferences();

            expect(result).toBe(false);
            expect(consoleErrorSpy).toHaveBeenCalled();

            localStorage.removeItem = originalRemoveItem;
        });
    });

    describe('Persistence Across Sessions', () => {
        it('should persist theme across sessions', () => {
            const UserPrefs1 = createUserPreferences();
            UserPrefs1.updatePreference('theme', 'dark');

            // Simulate new session
            const UserPrefs2 = createUserPreferences();
            const prefs = UserPrefs2.getPreferences();

            expect(prefs.theme).toBe('dark');
        });

        it('should persist language across sessions', () => {
            const UserPrefs1 = createUserPreferences();
            UserPrefs1.updatePreference('language', 'de');

            // Simulate new session
            const UserPrefs2 = createUserPreferences();
            const prefs = UserPrefs2.getPreferences();

            expect(prefs.language).toBe('de');
        });

        it('should persist multiple preferences', () => {
            const UserPrefs1 = createUserPreferences();
            UserPrefs1.savePreferences({ 
                theme: 'dark', 
                language: 'de',
                autoUpdate: true 
            });

            // Simulate new session
            const UserPrefs2 = createUserPreferences();
            const prefs = UserPrefs2.getPreferences();

            expect(prefs.theme).toBe('dark');
            expect(prefs.language).toBe('de');
            expect(prefs.autoUpdate).toBe(true);
        });
    });

    describe('Edge Cases', () => {
        it('should handle very large preferences object', () => {
            const UserPrefs = createUserPreferences();
            const largePrefs = {};
            for (let i = 0; i < 100; i++) {
                largePrefs[`key${i}`] = `value${i}`;
            }

            const result = UserPrefs.savePreferences(largePrefs);

            expect(result).toBe(true);
            const loaded = UserPrefs.getPreferences();
            expect(loaded).toEqual(largePrefs);
        });

        it('should handle special characters in values', () => {
            const UserPrefs = createUserPreferences();
            const prefs = {
                theme: 'dark-<script>alert("xss")</script>',
                language: '日本語'
            };

            UserPrefs.savePreferences(prefs);
            const loaded = UserPrefs.getPreferences();

            expect(loaded).toEqual(prefs);
        });

        it('should handle numeric values', () => {
            const UserPrefs = createUserPreferences();
            const prefs = { fontSize: 16, volume: 0.5 };

            UserPrefs.savePreferences(prefs);
            const loaded = UserPrefs.getPreferences();

            expect(loaded.fontSize).toBe(16);
            expect(loaded.volume).toBe(0.5);
        });

        it('should handle boolean values', () => {
            const UserPrefs = createUserPreferences();
            const prefs = { autoUpdate: true, notifications: false };

            UserPrefs.savePreferences(prefs);
            const loaded = UserPrefs.getPreferences();

            expect(loaded.autoUpdate).toBe(true);
            expect(loaded.notifications).toBe(false);
        });

        it('should handle nested objects', () => {
            const UserPrefs = createUserPreferences();
            const prefs = {
                display: {
                    theme: 'dark',
                    fontSize: 16
                }
            };

            UserPrefs.savePreferences(prefs);
            const loaded = UserPrefs.getPreferences();

            expect(loaded.display.theme).toBe('dark');
            expect(loaded.display.fontSize).toBe(16);
        });

        it('should handle arrays in preferences', () => {
            const UserPrefs = createUserPreferences();
            const prefs = { 
                recentSearches: ['naruto', 'one piece', 'bleach']
            };

            UserPrefs.savePreferences(prefs);
            const loaded = UserPrefs.getPreferences();

            expect(loaded.recentSearches).toEqual(['naruto', 'one piece', 'bleach']);
        });

        it('should handle rapid updates', () => {
            const UserPrefs = createUserPreferences();

            UserPrefs.updatePreference('theme', 'dark');
            UserPrefs.updatePreference('theme', 'light');
            UserPrefs.updatePreference('theme', 'dark');

            const prefs = UserPrefs.getPreferences();
            expect(prefs.theme).toBe('dark');
        });

        it('should handle empty string values', () => {
            const UserPrefs = createUserPreferences();
            const prefs = { theme: '' };

            UserPrefs.savePreferences(prefs);
            const loaded = UserPrefs.getPreferences();

            expect(loaded.theme).toBe('');
        });
    });

    describe('Default Preferences', () => {
        it('should return empty object as default', () => {
            const UserPrefs = createUserPreferences();

            const prefs = UserPrefs.getPreferences();

            expect(prefs).toEqual({});
        });

        it('should not apply anything when no preferences exist', () => {
            const UserPrefs = createUserPreferences();

            UserPrefs.loadPreferences();

            expect(document.documentElement.getAttribute('data-theme')).toBeNull();
            expect(document.documentElement.getAttribute('lang')).toBeNull();
        });
    });

    describe('Storage Key', () => {
        it('should use correct storage key', () => {
            const UserPrefs = createUserPreferences();
            UserPrefs.savePreferences({ theme: 'dark' });

            const stored = localStorage.getItem(STORAGE_KEY);
            expect(stored).toBeTruthy();
        });

        it('should not interfere with other localStorage keys', () => {
            const UserPrefs = createUserPreferences();
            localStorage.setItem('other_key', 'other_value');

            UserPrefs.savePreferences({ theme: 'dark' });

            expect(localStorage.getItem('other_key')).toBe('other_value');
        });
    });
});
