/**
 * BrowserTTSModule
 * Provides TTS via Browser's Web Speech API
 */
import { TTSHandlerModule } from './tts-handler-module.js';

export class BrowserTTSModule extends TTSHandlerModule {
    constructor() {
        super('browser-tts', 'Browser TTS');
        
        // Declare proper dependencies according to architecture principles
        this.dependencies = ['persistence-manager', 'localization', 'game-config'];
        
        // Voice options
        this.voiceOptions = {
            voice: null,  // Will be set during initialization
            speed: 1.0,
            pitch: 1.0,
            volume: 1.0
        };
        
        // State variables
        this.voices = [];
        this.voicesByLang = {};
        this.lastPreprocessedText = '';
        this.isSpeaking = false;
        this.currentUtterance = null;
        
        // Bind additional methods
        this.bindMethods(['handleVoicePreferenceChanged']);
    }
    
    /**
     * Initialize the Browser TTS module
     * @returns {Promise<boolean>} - Resolves with success status
     */
    async initialize() {
        try {
            this.reportProgress(10, 'Initializing Browser TTS');
            
            // Initialize parent
            const parentInit = await super.initialize();
            if (!parentInit) {
                console.error('Browser TTS: Parent initialization failed');
                return false;
            }
            
            // Get dependencies using proper pattern
            const persistenceManager = this.getModule('persistence-manager');
            if (!persistenceManager) {
                console.error('Browser TTS: Persistence Manager dependency not found');
                return false;
            }
            
            const localization = this.getModule('localization');
            if (!localization) {
                console.error('Browser TTS: Localization dependency not found');
                return false;
            }

            this.addEventListener(document, 'preference-updated', (event) => {
                const { category, key } = event.detail || {};
                if (category === 'audio' && ['masterVolume', 'ttsVolume', 'masterVolumeEnabled', 'ttsVolumeEnabled'].includes(key) && this.currentUtterance) {
                    this.currentUtterance.volume = this.getPlaybackVolume();
                }
            });
            
            // Check if browser supports speech synthesis
            if (!window.speechSynthesis) {
                console.error('Browser TTS: Speech synthesis not available in this browser');
                return false;
            }
            
            // Load voices
            this.reportProgress(30, 'Loading browser voices');
            await this.loadVoices();
            
            // Set up voice from preferences
            this.reportProgress(70, 'Setting up voice preferences');
            await this.setupVoiceFromPreferences();
            
            // Set up event listeners
            document.addEventListener('tts:browser:voicePreferenceChanged', this.handleVoicePreferenceChanged);
            
            // Set up utterance handlers
            this.setupUtteranceHandlers();
            
            // Mark as ready
            this.isReady = true;
            
            this.reportProgress(100, 'Browser TTS initialization complete');
            return true;
        } catch (error) {
            console.error('Browser TTS: Initialization error:', error);
            return false;
        }
    }
    
    /**
     * Load voices from browser speech synthesis API
     * @returns {Promise<boolean>} - Resolves with success status
     */
    async loadVoices() {
        // Helper function to process voices
        const processVoices = () => {
            // Get all voices from speechSynthesis
            const synVoices = window.speechSynthesis.getVoices() || [];
            
            if (synVoices.length === 0) {
                console.warn('Browser TTS: No voices available');
                return false;
            }
            
            // Transform to our format
            this.voices = synVoices.map((voice, index) => ({
                id: voice.voiceURI || `voice-${index}`,
                name: voice.name,
                language: voice.lang,
                localService: voice.localService,
                default: voice.default,
                original: voice  // Keep reference to original voice
            }));
            
            // Group voices by language
            this.voicesByLang = {};
            this.voices.forEach(voice => {
                if (voice.language) {
                    const langCode = voice.language.split('-')[0].toLowerCase();
                    if (!this.voicesByLang[langCode]) {
                        this.voicesByLang[langCode] = [];
                    }
                    this.voicesByLang[langCode].push(voice);
                }
            });
            
            return true;
        };
        
        // If voices are already loaded, process them
        if (window.speechSynthesis.getVoices().length > 0) {
            return processVoices();
        }
        
        // Otherwise, wait for voiceschanged event
        return new Promise(resolve => {
            // Set up timeout to handle browsers that don't trigger voiceschanged
            const timeoutId = setTimeout(() => {
                if (window.speechSynthesis.getVoices().length > 0) {
                    window.speechSynthesis.removeEventListener('voiceschanged', this.onVoicesChanged);
                    resolve(processVoices());
                } else {
                    console.warn('Browser TTS: Voices not loaded after timeout');
                    resolve(false);
                }
            }, 1000);
            
            this.onVoicesChanged = () => {
                clearTimeout(timeoutId);
                window.speechSynthesis.removeEventListener('voiceschanged', this.onVoicesChanged);
                resolve(processVoices());
            };
            
            window.speechSynthesis.addEventListener('voiceschanged', this.onVoicesChanged);
        });
    }
    
    /**
     * Set up voice based on preferences and locale
     * @returns {Promise<boolean>} - Resolves with success status
     */
    async setupVoiceFromPreferences() {
        const persistenceManager = this.getModule('persistence-manager');
        const gameConfig = this.getModule('game-config');
        
        if (!persistenceManager || this.voices.length === 0) {
            return false;
        }
        
        // Get preferred voice ID from preferences
        const preferredVoiceId = persistenceManager.getPreference('tts', 'browser_voice', '');
        
        // Get current locale
        const currentLocale = gameConfig?.getLocale?.() || 'en_US';
        
        // If we have a preferred voice ID, use it
        if (preferredVoiceId && this.voices.some(v => v.id === preferredVoiceId)) {
            this.voiceOptions.voice = preferredVoiceId;
            return true;
        }
        
        // Otherwise, select voice based on locale
        if (currentLocale) {
            return this.selectVoiceForLocale(currentLocale);
        }
        
        // Fall back to default voice
        return this.selectDefaultVoice();
    }
    
    /**
     * Select a voice for the given locale
     * @param {string} locale - Locale code
     * @returns {boolean} - Success status
     */
    selectVoiceForLocale(locale) {
        if (!locale || this.voices.length === 0) {
            return this.selectDefaultVoice();
        }
        
        const normalizedLocale = String(locale).replace('_', '-').toLowerCase();
        const langCode = normalizedLocale.split('-')[0];
        
        // First try to find a voice that exactly matches the locale
        let matchedVoice = this.voices.find(v => v.language && v.language.toLowerCase() === normalizedLocale);
        
        // If not found, try to find a voice for the language
        if (!matchedVoice && this.voicesByLang[langCode]) {
            // Prefer default voices if available
            matchedVoice = this.voicesByLang[langCode].find(v => v.default) || this.voicesByLang[langCode][0];
        }
        
        if (matchedVoice) {
            this.voiceOptions.voice = matchedVoice.id;
            return true;
        }
        
        // Fall back to default voice
        return this.selectDefaultVoice();
    }

    getPlaybackVolume() {
        const persistenceManager = this.getModule('persistence-manager');
        if (!persistenceManager) {
            return this.voiceOptions.volume || 1.0;
        }

        const masterVolume = persistenceManager.getPreference('audio', 'masterVolume', 1.0);
        const ttsVolume = persistenceManager.getPreference('audio', 'ttsVolume', 1.0);
        const masterEnabled = persistenceManager.getPreference('audio', 'masterVolumeEnabled', true) !== false;
        const ttsEnabled = persistenceManager.getPreference('audio', 'ttsVolumeEnabled', true) !== false;
        const configuredVolume = this.voiceOptions.volume || 1.0;

        return Math.max(0, Math.min(1, configuredVolume * (masterEnabled ? masterVolume : 0) * (ttsEnabled ? ttsVolume : 0)));
    }
    
    /**
     * Select a default voice
     * @returns {boolean} - Success status
     */
    selectDefaultVoice() {
        if (this.voices.length === 0) {
            return false;
        }
        
        // Find a default English voice if available
        const defaultEnVoice = this.voices.find(v => v.default && v.language && v.language.startsWith('en'));
        
        // Otherwise use any default voice
        const defaultVoice = defaultEnVoice || this.voices.find(v => v.default) || this.voices[0];
        
        this.voiceOptions.voice = defaultVoice.id;
        return true;
    }
    
    /**
     * Set up utterance handlers for speech events
     */
    setupUtteranceHandlers() {
        // Handler functions for utterance events
        this.utteranceHandlers = {
            start: () => {
                this.isSpeaking = true;
                document.dispatchEvent(new CustomEvent('tts:audio-started', {
                    detail: { provider: this.id || this.name }
                }));
            },
            end: () => {
                this.isSpeaking = false;
                this.currentUtterance = null;
            },
            error: (event) => {
                console.error('Browser TTS: Speech error:', event);
                this.isSpeaking = false;
                this.currentUtterance = null;
            },
            pause: () => {
                this.isSpeaking = false;
            },
            resume: () => {
                this.isSpeaking = true;
            }
        };
    }
    
    /**
     * Handle voice preference changed event
     * @param {Event} event - Event object
     */
    handleVoicePreferenceChanged(event) {
        if (event && event.detail) {
            this.setVoiceOptions(event.detail);
        }
    }
    
    /**
     * Preprocess text for TTS
     * @param {string} text - Text to preprocess
     * @returns {string} - Processed text
     */
    preprocessText(text) {
        if (!text) {
            return '';
        }
        
        // Remove HTML tags
        let processed = text.replace(/<[^>]*>/g, ' ');
        
        // Replace special characters
        processed = processed.replace(/&/g, ' and ');
        
        // Normalize whitespace
        processed = processed.replace(/\s+/g, ' ').trim();
        
        // Add trailing period if missing
        if (!/[.!?]$/.test(processed)) {
            processed += '.';
        }
        
        this.lastPreprocessedText = processed;
        return processed;
    }
    
    /**
     * Speak text
     * @param {string} text - Text to speak
     * @param {Function} callback - Callback for when speech completes
     * @returns {boolean} - Success status
     */
    speak(text, callback = null) {
        if (!this.isReady || !text) {
            if (callback) {
                callback({ success: false, reason: 'not_ready_or_empty_text' });
            }
            return false;
        }
        
        try {
            // Stop any ongoing speech
            this.stop();
            
            // Process the text
            const processedText = this.preprocessText(text);
            
            // Create a new utterance
            const utterance = new SpeechSynthesisUtterance(processedText);
            
            // Set voice options
            if (this.voiceOptions.voice) {
                const voice = this.voices.find(v => v.id === this.voiceOptions.voice);
                if (voice && voice.original) {
                    utterance.voice = voice.original;
                }
            }
            
            utterance.rate = this.voiceOptions.speed || 1.0;
            utterance.pitch = this.voiceOptions.pitch || 1.0;
            utterance.volume = this.getPlaybackVolume();
            
            // Set up event handlers
            utterance.onstart = this.utteranceHandlers.start;
            utterance.onend = () => {
                this.utteranceHandlers.end();
                if (callback) {
                    callback({ success: true });
                }
            };
            utterance.onerror = (event) => {
                this.utteranceHandlers.error(event);
                if (callback) {
                    callback({ success: false, reason: 'synthesis_error', error: event });
                }
            };
            utterance.onpause = this.utteranceHandlers.pause;
            utterance.onresume = this.utteranceHandlers.resume;
            
            // Start speaking
            this.currentUtterance = utterance;
            speechSynthesis.speak(utterance);
            
            return true;
        } catch (error) {
            console.error('Browser TTS: Failed to speak:', error);
            if (callback) {
                callback({ success: false, reason: 'speak_error', error });
            }
            return false;
        }
    }
    
    /**
     * Stop speaking
     * @returns {boolean} - Success status
     */
    stop() {
        try {
            speechSynthesis.cancel();
            this.isSpeaking = false;
            this.currentUtterance = null;
            return true;
        } catch (error) {
            console.error('Browser TTS: Failed to stop speech:', error);
            return false;
        }
    }
    
    /**
     * Pause speaking
     * @returns {boolean} - Success status
     */
    pause() {
        try {
            if (this.isSpeaking) {
                speechSynthesis.pause();
                return true;
            }
            return false;
        } catch (error) {
            console.error('Browser TTS: Failed to pause speech:', error);
            return false;
        }
    }
    
    /**
     * Resume speaking
     * @returns {boolean} - Success status
     */
    resume() {
        try {
            speechSynthesis.resume();
            return true;
        } catch (error) {
            console.error('Browser TTS: Failed to resume speech:', error);
            return false;
        }
    }
    
    /**
     * Get available voices
     * @returns {Array} - Array of voice objects
     */
    getAvailableVoices() {
        return this.voices;
    }
    
    /**
     * Set voice options
     * @param {Object} options - Voice options
     */
    setVoiceOptions(options = {}) {
        if (options.voice) {
            this.voiceOptions.voice = options.voice;
            
            // Save voice preference
            const persistenceManager = this.getModule('persistence-manager');
            if (persistenceManager) {
                persistenceManager.updatePreference('tts', 'browser_voice', options.voice);
            }
        }
        
        if (typeof options.speed === 'number') {
            // Web Speech rate uses 1.0 as normal, matching the app-wide slider.
            this.voiceOptions.speed = Math.max(0.1, Math.min(10.0, options.speed));
        }
        
        if (typeof options.pitch === 'number') {
            this.voiceOptions.pitch = Math.max(0.5, Math.min(2.0, options.pitch));
            
            // Save pitch preference
            const persistenceManager = this.getModule('persistence-manager');
            if (persistenceManager) {
                persistenceManager.updatePreference('tts', 'browser_pitch', options.pitch);
            }
        }
        
        if (typeof options.volume === 'number') {
            this.voiceOptions.volume = Math.max(0, Math.min(1.0, options.volume));
        }
    }
    
    /**
     * Preload speech for later playback
     * Not applicable for the browser TTS (always returns null)
     * @param {string} text - Text to preload
     * @returns {Promise<Object>} - Promise that resolves to null
     */
    async preloadSpeech(text) {
        // Browser TTS can't preload speech
        return { success: false, reason: 'not_supported' };
    }
    
    /**
     * Speak preloaded speech
     * Not applicable for the browser TTS (always returns false)
     * @param {Object} preloadData - Preloaded speech data
     * @param {Function} callback - Callback for when speech completes
     * @returns {boolean} - Success status (always false)
     */
    speakPreloaded(preloadData, callback = null) {
        if (typeof callback === 'function') {
            callback({ success: false, reason: 'not_supported' });
        }
        return false;
    }
}

const browserTTSModule = new BrowserTTSModule();

export { browserTTSModule };
