/**
 * TTSService - Centralized Text-to-Speech service
 * Provides consistent TTS functionality across all games
 */
class TTSService {
    constructor() {
        this._voicesReady = false;
        this._voices = [];
        this._currentUtterance = null;
        this._defaultRate = 0.85;
        this._defaultPitch = 1.0;
        this._defaultVolume = 1.0;
        this._preferredVoices = {}; // Map of language -> voice name

        // Internal queue system
        this._queue = [];
        this._isProcessing = false;

        this._init();
    }

    /**
     * Initialize the TTS service and preload voices
     * @private
     */
    _init() {
        if (!('speechSynthesis' in window)) {
            console.warn('🔇 Speech Synthesis API not available in this browser');
            return;
        }

        // Preload voices
        this._loadVoices().then(() => {
            this._voicesReady = true;
            console.log(`🔊 TTS Service initialized with ${this._voices.length} voices available`);

            // Log available Chinese voices for debugging
            const chineseVoices = this._voices.filter(v => v.lang.startsWith('zh'));
            console.log(`🔊 Chinese voices available (${chineseVoices.length}):`,
                chineseVoices.map(v => `${v.name} (${v.lang}) ${v.default ? '[DEFAULT]' : ''}`));
        });
    }

    /**
     * Speak text using the Web Speech API
     * @param {string} text - Text to speak
     * @param {string} language - Language code (e.g., 'en-US', 'zh-CN', 'fr-FR')
     * @param {Object} options - Optional TTS settings
     * @param {number} options.rate - Speech rate (0.1 to 10, default 0.85)
     * @param {number} options.pitch - Speech pitch (0 to 2, default 1.0)
     * @param {number} options.volume - Speech volume (0 to 1, default 1.0)
     * @param {boolean} options.queue - Whether to queue speech or cancel previous (default false)
     * @returns {Promise<void>}
     */
    async speak(text, language = 'en-US', options = {}) {
        if (!('speechSynthesis' in window)) {
            console.warn('Speech Synthesis not available');
            return;
        }

        if (!text || typeof text !== 'string') {
            console.warn('Invalid text provided to TTS:', text);
            return;
        }

        // Cancel any ongoing speech and clear queue unless queuing is requested
        if (!options.queue) {
            this.cancel();
            this._queue = [];
        }

        // Ensure voices are loaded
        if (!this._voicesReady || this._voices.length === 0) {
            await this._loadVoices();
        }

        // Add to queue and process
        return new Promise((resolve, reject) => {
            this._queue.push({
                text,
                language,
                options,
                resolve,
                reject
            });

            // Start processing if not already processing
            if (!this._isProcessing) {
                this._processQueue();
            }
        });
    }

    /**
     * Process the internal speech queue
     * @private
     */
    async _processQueue() {
        if (this._queue.length === 0) {
            this._isProcessing = false;
            return;
        }

        this._isProcessing = true;
        const item = this._queue.shift();

        try {
            await this._speakImmediate(item.text, item.language, item.options);
            item.resolve();
        } catch (error) {
            item.reject(error);
        }

        // Process next item after a small delay to avoid browser throttling
        setTimeout(() => {
            this._processQueue();
        }, 50);
    }

    /**
     * Internal immediate speak (bypasses queue)
     * @private
     */
    async _speakImmediate(text, language = 'en-US', options = {}) {

        // Create utterance
        const utterance = new SpeechSynthesisUtterance(text);
        utterance.lang = language;
        utterance.rate = options.rate ?? this._defaultRate;
        utterance.pitch = options.pitch ?? this._defaultPitch;
        utterance.volume = options.volume ?? this._defaultVolume;

        // Select best voice for the language
        const voice = this._selectVoice(language);
        if (voice) {
            utterance.voice = voice;
            console.log(`🔊 Selected voice: ${voice.name} (${voice.lang}) for language: ${language}`);
        } else {
            console.warn(`⚠️ No voice found for language: ${language}`);
        }

        // Store current utterance
        this._currentUtterance = utterance;

        // Return a promise that resolves when speech ends
        return new Promise((resolve, reject) => {
            let fallbackAttempted = false;
            let speechStarted = false;
            let speechEnded = false;

            utterance.onstart = () => {
                speechStarted = true;
            };

            utterance.onend = () => {
                speechEnded = true;
                this._currentUtterance = null;
                resolve();
            };

            utterance.onerror = (event) => {
                // If speech already ended successfully, ignore the error
                if (speechEnded) {
                    return;
                }

                // If speech already started, it's probably a minor error (like 'interrupted') - let it finish
                if (speechStarted) {
                    return;
                }

                // Only log critical errors that prevent speech from starting
                if (event.error !== 'interrupted' && event.error !== 'canceled') {
                    console.warn(`⚠️ TTS Error: ${event.error}`);
                }

                this._currentUtterance = null;
                reject(event.error);
            };

            window.speechSynthesis.speak(utterance);
        });
    }

    /**
     * Cancel any ongoing speech and clear queue
     */
    cancel() {
        if ('speechSynthesis' in window) {
            window.speechSynthesis.cancel();
            this._currentUtterance = null;
            this._queue = [];
            this._isProcessing = false;
        }
    }

    /**
     * Pause ongoing speech
     */
    pause() {
        if ('speechSynthesis' in window && window.speechSynthesis.speaking) {
            window.speechSynthesis.pause();
        }
    }

    /**
     * Resume paused speech
     */
    resume() {
        if ('speechSynthesis' in window && window.speechSynthesis.paused) {
            window.speechSynthesis.resume();
        }
    }

    /**
     * Check if TTS is currently speaking
     * @returns {boolean}
     */
    isSpeaking() {
        return 'speechSynthesis' in window && window.speechSynthesis.speaking;
    }

    /**
     * Check if TTS is available
     * @returns {boolean}
     */
    isAvailable() {
        return 'speechSynthesis' in window;
    }

    /**
     * Get all available voices
     * @returns {Promise<SpeechSynthesisVoice[]>}
     */
    async getVoices() {
        if (!this._voicesReady || this._voices.length === 0) {
            await this._loadVoices();
        }
        return [...this._voices];
    }

    /**
     * Get voices for a specific language
     * @param {string} language - Language code (e.g., 'en-US', 'zh-CN')
     * @returns {Promise<SpeechSynthesisVoice[]>}
     */
    async getVoicesForLanguage(language) {
        const voices = await this.getVoices();
        const langPrefix = language.split('-')[0];
        return voices.filter(voice => voice.lang.startsWith(langPrefix));
    }

    /**
     * Load available voices from the browser
     * @returns {Promise<SpeechSynthesisVoice[]>}
     * @private
     */
    _loadVoices() {
        return new Promise((resolve) => {
            if (!('speechSynthesis' in window)) {
                resolve([]);
                return;
            }

            // Try to get voices immediately
            let voices = window.speechSynthesis.getVoices();

            // If voices are already loaded, return them
            if (voices.length > 0) {
                this._voices = voices;
                this._voicesReady = true;
                resolve(voices);
                return;
            }

            // Otherwise, wait for voiceschanged event
            const voicesChangedHandler = () => {
                voices = window.speechSynthesis.getVoices();
                if (voices.length > 0) {
                    this._voices = voices;
                    this._voicesReady = true;
                    window.speechSynthesis.removeEventListener('voiceschanged', voicesChangedHandler);
                    resolve(voices);
                }
            };

            window.speechSynthesis.addEventListener('voiceschanged', voicesChangedHandler);

            // Fallback timeout in case voices never load
            setTimeout(() => {
                window.speechSynthesis.removeEventListener('voiceschanged', voicesChangedHandler);
                const finalVoices = window.speechSynthesis.getVoices();
                this._voices = finalVoices;
                this._voicesReady = true;
                resolve(finalVoices);
            }, 1000);
        });
    }

    /**
     * Select the best voice for a given language
     * @param {string} language - Language code (e.g., 'en-US', 'zh-CN')
     * @returns {SpeechSynthesisVoice|null}
     * @private
     */
    _selectVoice(language) {
        if (this._voices.length === 0) {
            return null;
        }

        const langPrefix = language.split('-')[0]; // e.g., 'zh' from 'zh-CN'

        // Check if user has set a preferred voice for this EXACT language
        if (this._preferredVoices[language]) {
            const preferredVoice = this._voices.find(v => v.name === this._preferredVoices[language]);
            if (preferredVoice) {
                console.log(`🔊 Using user-preferred voice (exact): ${preferredVoice.name} for ${language}`);
                return preferredVoice;
            }
        }

        // Check if user has set a preferred voice for ANY variant of this language
        for (const [lang, voiceName] of Object.entries(this._preferredVoices)) {
            if (lang.startsWith(langPrefix)) {
                const preferredVoice = this._voices.find(v => v.name === voiceName);
                if (preferredVoice) {
                    console.log(`🔊 Using user-preferred voice (prefix): ${preferredVoice.name} for ${language} (from ${lang})`);
                    return preferredVoice;
                }
            }
        }

        // Quality indicators for voices (in order of preference)
        const isQualityVoice = (name) => {
            const quality = [
                'Google', 'Neural', 'Premium', 'Natural', 'Enhanced',
                'Microsoft', 'HD', 'Wavenet', 'Studio'
            ];
            return quality.some(q => name.includes(q));
        };

        // Priority 1: Local service voices with quality names (often best quality)
        let voice = this._voices.find(v =>
            v.lang === language &&
            v.localService &&
            isQualityVoice(v.name)
        );

        if (voice) return voice;

        // Priority 2: Any local service voice (exact language)
        voice = this._voices.find(v =>
            v.lang === language &&
            v.localService
        );

        if (voice) return voice;

        // Priority 3: Exact language match with quality voice
        voice = this._voices.find(v =>
            v.lang === language &&
            isQualityVoice(v.name)
        );

        if (voice) return voice;

        // Priority 4: Language prefix match with local service + quality
        voice = this._voices.find(v =>
            v.lang.startsWith(langPrefix) &&
            v.localService &&
            isQualityVoice(v.name)
        );

        if (voice) return voice;

        // Priority 5: Language prefix match with local service
        voice = this._voices.find(v =>
            v.lang.startsWith(langPrefix) &&
            v.localService
        );

        if (voice) return voice;

        // Priority 6: Language prefix match with quality voice
        voice = this._voices.find(v =>
            v.lang.startsWith(langPrefix) &&
            isQualityVoice(v.name)
        );

        if (voice) return voice;

        // Priority 7: Exact language match with default voice
        voice = this._voices.find(v => v.lang === language && v.default);

        if (voice) return voice;

        // Priority 8: Language prefix match with default voice
        voice = this._voices.find(v => v.lang.startsWith(langPrefix) && v.default);

        if (voice) return voice;

        // Priority 9: Any exact language match
        voice = this._voices.find(v => v.lang === language);

        if (voice) return voice;

        // Priority 10: Any language prefix match
        voice = this._voices.find(v => v.lang.startsWith(langPrefix));

        if (voice) return voice;

        // Fallback: First available voice
        console.warn(`🔊 No matching voice found for ${language}, using fallback`);
        return this._voices[0] || null;
    }

    /**
     * Set default TTS settings
     * @param {Object} settings - Default settings
     * @param {number} settings.rate - Default speech rate
     * @param {number} settings.pitch - Default speech pitch
     * @param {number} settings.volume - Default speech volume
     */
    setDefaults(settings = {}) {
        if (settings.rate !== undefined) this._defaultRate = settings.rate;
        if (settings.pitch !== undefined) this._defaultPitch = settings.pitch;
        if (settings.volume !== undefined) this._defaultVolume = settings.volume;
    }

    /**
     * Get current default settings
     * @returns {Object}
     */
    getDefaults() {
        return {
            rate: this._defaultRate,
            pitch: this._defaultPitch,
            volume: this._defaultVolume
        };
    }

    /**
     * Set preferred voice for a specific language
     * @param {string} language - Language code (e.g., 'en-US', 'zh-CN')
     * @param {string} voiceName - Name of the voice to use (or null to clear)
     */
    setPreferredVoice(language, voiceName) {
        if (voiceName === null || voiceName === '') {
            delete this._preferredVoices[language];
        } else {
            this._preferredVoices[language] = voiceName;
        }
    }

    /**
     * Set multiple preferred voices at once
     * @param {Object} voicesByLanguage - Map of language -> voice name
     */
    setPreferredVoices(voicesByLanguage) {
        this._preferredVoices = { ...voicesByLanguage };
    }

    /**
     * Get preferred voices map
     * @returns {Object}
     */
    getPreferredVoices() {
        return { ...this._preferredVoices };
    }

    /**
     * Test TTS with a sample phrase in the given language
     * @param {string} language - Language code to test
     */
    async test(language = 'en-US') {
        const testPhrases = {
            'en': 'Hello, this is a test of the text to speech system.',
            'zh': '你好，这是文本转语音系统的测试。',
            'fr': 'Bonjour, ceci est un test du système de synthèse vocale.',
            'es': 'Hola, esta es una prueba del sistema de texto a voz.',
            'de': 'Hallo, dies ist ein Test des Text-zu-Sprache-Systems.',
            'ja': 'こんにちは、これはテキスト読み上げシステムのテストです。',
            'ko': '안녕하세요, 이것은 텍스트 음성 변환 시스템의 테스트입니다.'
        };

        const langPrefix = language.split('-')[0];
        const testPhrase = testPhrases[langPrefix] || testPhrases['en'];

        console.log(`🔊 Testing TTS for ${language}...`);
        await this.speak(testPhrase, language);
    }
}

// Create singleton instance
const ttsService = new TTSService();

export default ttsService;
