/**
 * AudioManager Module
 * Manages loading and playback of non-TTS audio effects triggered by tags.
 */
import { BaseModule } from './base-module.js';
import { moduleRegistry } from './module-registry.js';

class AudioManagerModule extends BaseModule {
    constructor() {
        super('audio-manager', 'Audio Manager');
        this.sounds = new Map();
        this.currentAudio = null;
        this.currentLoop = null;
        this.masterVolume = 1.0;
        this.musicVolume = 1.0;
        this.sfxVolume = 1.0;
    }
    
    /**
     * Load module dependencies
     * @returns {Promise} - Resolves when dependencies are loaded
     */
    async loadDependencies() {
        try {
            this.reportProgress(40, "Initializing audio system");
            return true;
        } catch (error) {
            console.error("Error loading AudioManager dependencies:", error);
            return false;
        }
    }
    
    /**
     * Initialize the module
     * @returns {Promise<boolean>} - Resolves with success status
     */
    async initialize() {
        try {
            // Set up audio context if needed
            this.setupAudioContext();
            
            // Load some basic sound effects
            this.reportProgress(80, "Loading sound effects");
            
            this.reportProgress(100, "Audio system ready");
            return true;
        } catch (error) {
            console.error("Error initializing AudioManager:", error);
            return false;
        }
    }
    
    /**
     * Set up Web Audio API context if needed
     */
    setupAudioContext() {
        // Only create if needed for advanced audio features
        if (typeof AudioContext !== 'undefined' || typeof webkitAudioContext !== 'undefined') {
            const AudioContextClass = window.AudioContext || window.webkitAudioContext;
            this.audioContext = new AudioContextClass();
        }
    }

    /**
     * Load a sound file
     * @param {string} id - The identifier for the sound
     * @param {string} url - The URL of the sound file
     * @returns {Promise} A promise that resolves when the sound is loaded
     */
    loadSound(id, url) {
        return new Promise((resolve, reject) => {
            const audio = new Audio(url);
            audio.addEventListener('canplaythrough', () => {
                this.sounds.set(id, audio);
                resolve(audio);
            }, { once: true });
            audio.addEventListener('error', (error) => {
                reject(error);
            });
            audio.load();
        });
    }

    /**
     * Play a sound
     * @param {string} id - The identifier for the sound
     * @param {boolean} loop - Whether to loop the sound
     * @returns {HTMLAudioElement|null} The audio element or null if not found
     */
    playSound(id, loop = false) {
        const audio = this.sounds.get(id);
        if (!audio) {
            console.warn(`Sound with id "${id}" not found.`);
            return null;
        }

        if (loop) {
            if (this.currentLoop) {
                this.currentLoop.pause();
                this.currentLoop.currentTime = 0;
            }
            audio.loop = true;
            this.currentLoop = audio;
        } else {
            if (this.currentAudio) {
                this.currentAudio.pause();
                this.currentAudio.currentTime = 0;
            }
            this.currentAudio = audio;
        }

        audio.play().catch(error => {
            console.error('Error playing audio:', error);
        });

        return audio;
    }

    /**
     * Play a sound from a URL directly (without preloading)
     * @param {string} url - The URL of the sound file
     * @param {boolean} loop - Whether to loop the sound
     * @returns {HTMLAudioElement} The audio element
     */
    playSoundFromUrl(url, loop = false) {
        if (loop) {
            if (this.currentLoop) {
                this.currentLoop.pause();
                this.currentLoop.removeAttribute('src');
                this.currentLoop.load();
            }
            this.currentLoop = new Audio(url);
            this.currentLoop.loop = true;
            this.currentLoop.play().catch(error => {
                console.error('Error playing audio loop:', error);
            });
            return this.currentLoop;
        } else {
            if (this.currentAudio) {
                this.currentAudio.pause();
                this.currentAudio.removeAttribute('src');
                this.currentAudio.load();
            }
            this.currentAudio = new Audio(url);
            this.currentAudio.play().catch(error => {
                console.error('Error playing audio:', error);
            });
            return this.currentAudio;
        }
    }

    /**
     * Stop a specific sound
     * @param {string} id - The identifier for the sound
     */
    stopSound(id) {
        const audio = this.sounds.get(id);
        if (audio) {
            audio.pause();
            audio.currentTime = 0;
        }
    }

    /**
     * Stop all sounds
     */
    stopAllSounds() {
        if (this.currentAudio) {
            this.currentAudio.pause();
            this.currentAudio.currentTime = 0;
            this.currentAudio = null;
        }

        if (this.currentLoop) {
            this.currentLoop.pause();
            this.currentLoop.currentTime = 0;
            this.currentLoop = null;
        }

        this.sounds.forEach(audio => {
            audio.pause();
            audio.currentTime = 0;
        });
    }

    /**
     * Set the master volume for all sounds
     * @param {number} volume - The volume level (0.0 to 1.0)
     */
    setMasterVolume(volume) {
        this.masterVolume = Math.max(0, Math.min(1, volume));
        this.updateVolumes();
    }
    
    /**
     * Set the music volume
     * @param {number} volume - The volume level (0.0 to 1.0)
     */
    setMusicVolume(volume) {
        this.musicVolume = Math.max(0, Math.min(1, volume));
        // Apply to current loop if it exists
        if (this.currentLoop) {
            this.currentLoop.volume = this.masterVolume * this.musicVolume;
        }
    }
    
    /**
     * Set the sound effects volume
     * @param {number} volume - The volume level (0.0 to 1.0)
     */
    setSfxVolume(volume) {
        this.sfxVolume = Math.max(0, Math.min(1, volume));
        // Apply to current non-loop audio if it exists
        if (this.currentAudio) {
            this.currentAudio.volume = this.masterVolume * this.sfxVolume;
        }
    }
    
    /**
     * Update all volume levels based on current settings
     */
    updateVolumes() {
        this.sounds.forEach(audio => {
            const isMusic = audio.loop;
            audio.volume = this.masterVolume * (isMusic ? this.musicVolume : this.sfxVolume);
        });

        if (this.currentAudio) {
            this.currentAudio.volume = this.masterVolume * this.sfxVolume;
        }

        if (this.currentLoop) {
            this.currentLoop.volume = this.masterVolume * this.musicVolume;
        }
    }

    /**
     * Check if a sound is currently playing
     * @param {string} id - The identifier for the sound
     * @returns {boolean} Whether the sound is playing
     */
    isPlaying(id) {
        const audio = this.sounds.get(id);
        return audio ? !audio.paused : false;
    }

    /**
     * Fade out the current audio
     * @param {number} duration - The duration of the fade in milliseconds
     * @returns {Promise} A promise that resolves when the fade is complete
     */
    fadeOutCurrentAudio(duration = 1000) {
        return new Promise((resolve) => {
            if (!this.currentAudio || this.currentAudio.paused) {
                resolve();
                return;
            }

            const audio = this.currentAudio;
            const initialVolume = audio.volume;
            const volumeStep = initialVolume / (duration / 50);
            let currentVolume = initialVolume;

            const fadeInterval = setInterval(() => {
                currentVolume -= volumeStep;
                if (currentVolume <= 0) {
                    clearInterval(fadeInterval);
                    audio.pause();
                    audio.currentTime = 0;
                    audio.volume = initialVolume; // Reset volume for future use
                    resolve();
                } else {
                    audio.volume = currentVolume;
                }
            }, 50);
        });
    }
}

// Create the singleton instance
const AudioManager = new AudioManagerModule();

// Register with the module registry
moduleRegistry.register(AudioManager);

// Export the module
export { AudioManager };

// Keep a reference in window for loader system
window.AudioManager = AudioManager;
