/**
 * SoundSystem.js
 * Helper for managing Web Audio API sound generation and playback
 * Generates programmatic retro game sounds without external audio files
 */

export class SoundSystem {
    constructor() {
        this._audioContext = null;
        this._sounds = {};
        this._initialized = false;
    }

    /**
     * Initialize the Web Audio Context
     * @returns {boolean} - True if initialization successful
     */
    initialize() {
        if (this._initialized) {
            console.log('🔊 Sound system already initialized');
            return true;
        }

        try {
            // Initialize Web Audio Context
            this._audioContext = new (window.AudioContext || window.webkitAudioContext)();
            console.log('🔊 Sound system initialized');

            // Create sound library
            this._createSoundLibrary();
            this._initialized = true;
            return true;
        } catch (error) {
            console.warn('⚠️ Sound system not available:', error);
            this._audioContext = null;
            this._initialized = false;
            return false;
        }
    }

    /**
     * Create the library of available sounds
     * Each sound is defined with parameters for programmatic generation
     */
    _createSoundLibrary() {
        // Sound definitions with parameters for programmatic generation
        this._sounds = {
            jump: {
                type: 'sweep',
                frequency: 330,
                endFrequency: 600,
                duration: 0.1
            },
            coin: {
                type: 'bell',
                frequency: 800,
                duration: 0.3
            },
            powerup: {
                type: 'arpeggio',
                frequencies: [264, 330, 396, 528],
                duration: 0.6
            },
            enemy_defeat: {
                type: 'noise_sweep',
                frequency: 200,
                endFrequency: 50,
                duration: 0.2
            },
            question_block: {
                type: 'sparkle',
                frequency: 600,
                endFrequency: 1200,
                duration: 0.4
            },
            level_complete: {
                type: 'victory',
                frequencies: [523, 659, 784, 1047],
                duration: 1.0
            },
            death: {
                type: 'descend',
                frequency: 300,
                endFrequency: 100,
                duration: 0.8
            },
            finish_stars: {
                type: 'magical',
                frequencies: [880, 1100, 1320, 1760],
                duration: 2.0
            }
        };

        console.log('🎵 Sound library created with', Object.keys(this._sounds).length, 'sounds');
    }

    /**
     * Play a sound by name
     * @param {string} soundName - Name of the sound to play
     * @param {number} volume - Volume level (0.0 to 1.0)
     */
    play(soundName, volume = 0.3) {
        if (!this._initialized || !this._audioContext || !this._sounds[soundName]) {
            if (!this._sounds[soundName] && this._initialized) {
                console.warn(`⚠️ Sound not found: ${soundName}`);
            }
            return;
        }

        try {
            const sound = this._sounds[soundName];
            const oscillator = this._audioContext.createOscillator();
            const gainNode = this._audioContext.createGain();

            oscillator.connect(gainNode);
            gainNode.connect(this._audioContext.destination);

            const currentTime = this._audioContext.currentTime;
            const duration = sound.duration;

            // Set volume envelope (fade in/out)
            gainNode.gain.setValueAtTime(0, currentTime);
            gainNode.gain.linearRampToValueAtTime(volume, currentTime + 0.01);
            gainNode.gain.exponentialRampToValueAtTime(0.01, currentTime + duration);

            // Configure sound based on type
            this._configureSoundType(oscillator, sound, currentTime, duration);

            oscillator.start(currentTime);
            oscillator.stop(currentTime + duration);

            console.log(`🎵 Playing sound: ${soundName}`);

        } catch (error) {
            console.warn('⚠️ Failed to play sound:', soundName, error);
        }
    }

    /**
     * Configure oscillator based on sound type
     * @param {OscillatorNode} oscillator - Web Audio oscillator
     * @param {Object} sound - Sound configuration
     * @param {number} currentTime - Current audio context time
     * @param {number} duration - Sound duration
     */
    _configureSoundType(oscillator, sound, currentTime, duration) {
        switch (sound.type) {
            case 'sweep':
                // Frequency sweep (jump sound)
                oscillator.type = 'square';
                oscillator.frequency.setValueAtTime(sound.frequency, currentTime);
                oscillator.frequency.linearRampToValueAtTime(sound.endFrequency, currentTime + duration);
                break;

            case 'bell':
                // Bell-like sound (coin)
                oscillator.type = 'sine';
                oscillator.frequency.setValueAtTime(sound.frequency, currentTime);
                oscillator.frequency.exponentialRampToValueAtTime(sound.frequency * 0.5, currentTime + duration);
                break;

            case 'noise_sweep':
                // Noise sweep (enemy defeat)
                oscillator.type = 'sawtooth';
                oscillator.frequency.setValueAtTime(sound.frequency, currentTime);
                oscillator.frequency.linearRampToValueAtTime(sound.endFrequency, currentTime + duration);
                break;

            case 'sparkle':
                // Sparkle effect (question block)
                oscillator.type = 'triangle';
                oscillator.frequency.setValueAtTime(sound.frequency, currentTime);
                oscillator.frequency.linearRampToValueAtTime(sound.endFrequency, currentTime + duration * 0.7);
                oscillator.frequency.linearRampToValueAtTime(sound.frequency, currentTime + duration);
                break;

            case 'descend':
                // Descending tone (death)
                oscillator.type = 'square';
                oscillator.frequency.setValueAtTime(sound.frequency, currentTime);
                oscillator.frequency.exponentialRampToValueAtTime(sound.endFrequency, currentTime + duration);
                break;

            case 'arpeggio':
            case 'victory':
            case 'magical':
                // Complex multi-note sounds
                oscillator.type = sound.type === 'magical' ? 'triangle' : 'square';
                oscillator.frequency.setValueAtTime(sound.frequencies[0], currentTime);

                // Schedule frequency changes for arpeggio effect
                const noteLength = duration / sound.frequencies.length;
                sound.frequencies.forEach((freq, index) => {
                    if (index > 0) {
                        oscillator.frequency.setValueAtTime(freq, currentTime + noteLength * index);
                    }
                });
                break;

            default:
                // Default fallback
                oscillator.type = 'square';
                oscillator.frequency.setValueAtTime(sound.frequency || 440, currentTime);
        }
    }

    /**
     * Add a custom sound to the library
     * @param {string} name - Sound name
     * @param {Object} config - Sound configuration
     */
    addSound(name, config) {
        if (!name || !config) {
            console.warn('⚠️ Invalid sound configuration');
            return;
        }

        this._sounds[name] = config;
        console.log(`🎵 Added custom sound: ${name}`);
    }

    /**
     * Remove a sound from the library
     * @param {string} name - Sound name to remove
     */
    removeSound(name) {
        if (this._sounds[name]) {
            delete this._sounds[name];
            console.log(`🎵 Removed sound: ${name}`);
        }
    }

    /**
     * Get list of available sounds
     * @returns {Array} - Array of sound names
     */
    getAvailableSounds() {
        return Object.keys(this._sounds);
    }

    /**
     * Check if sound system is initialized
     * @returns {boolean}
     */
    isInitialized() {
        return this._initialized;
    }

    /**
     * Get the audio context (for advanced usage)
     * @returns {AudioContext|null}
     */
    getAudioContext() {
        return this._audioContext;
    }

    /**
     * Cleanup and close audio context
     */
    destroy() {
        if (this._audioContext) {
            this._audioContext.close();
            this._audioContext = null;
        }
        this._sounds = {};
        this._initialized = false;
        console.log('🔊 Sound system destroyed');
    }
}

// Export singleton instance for convenience
export const soundSystem = new SoundSystem();
