/**
 * API TTS Module Base Class
 * Base class for API-based TTS modules
 */
import { TTSHandlerModule } from './tts-handler-module.js';

export class ApiTTSModuleBase extends TTSHandlerModule {
    constructor(id, name) {
        super(id, name);
        
        // Declare proper dependencies according to architecture principles
        this.dependencies = ['persistence-manager', 'localization', 'game-config'];
        
        // Basic voice options
        this.voiceOptions = {
            speed: 1.0,
            voice: null
        };
        
        // API settings
        this.apiKey = '';
        this.apiBaseUrl = '';
        
        // State
        this.currentAudio = null;
        this.currentPlaybackFinish = null;
        
        // Bind additional methods
        this.bindMethods([
            'handleApiKeyChanged',
            'handleApiUrlChanged',
            'speakPreloaded',
            'loadVoices',
            'selectVoiceForLocale',
            'selectDefaultVoice',
            'generateSpeechAudio',
            'preprocessText',
            'getPlaybackVolume',
            'applyCurrentVolume'
        ]);
    }
    
    /**
     * Initialize the API TTS module
     * @returns {Promise<boolean>} - Resolves with success status
     */
    async initialize() {
        this.reportProgress(10, `Initializing ${this.name}`);
        
        // Initialize parent
        const parentInit = await super.initialize();
        if (!parentInit) {
            return false;
        }
        
        // Get persistence manager
        const persistenceManager = this.getModule('persistence-manager');
        if (!persistenceManager) {
            console.error(`${this.name}: Required dependency 'persistence-manager' not found`);
            return false;
        }
        
        // Load API key from preferences
        this.apiKey = persistenceManager.getPreference('tts', `${this.id}_api_key`) || '';
        
        // Get default API URL
        const defaultApiUrl = this.getDefaultApiBaseUrl();
        
        // Set up API base URL from preferences or use default
        const savedApiUrl = persistenceManager.getPreference('tts', `${this.id}_api_url`);
        this.apiBaseUrl = savedApiUrl || defaultApiUrl;
        
        // If no API URL was saved in preferences, save the default
        if (!savedApiUrl && defaultApiUrl) {
            persistenceManager.updatePreference('tts', `${this.id}_api_url`, defaultApiUrl);
        }
        
        this.reportProgress(30, `${this.name} API configuration loaded`);
        
        // Set up event listeners for API key and URL changes
        document.addEventListener('tts:api:keyChanged', this.handleApiKeyChanged);
        document.addEventListener('tts:api:urlChanged', this.handleApiUrlChanged);
        this.addEventListener(document, 'preference-updated', (event) => {
            const { category, key } = event.detail || {};
            if (category !== 'audio') {
                return;
            }

            if (['masterVolume', 'ttsVolume', 'masterVolumeEnabled', 'ttsVolumeEnabled', 'master_volume', 'tts_volume'].includes(key)) {
                this.applyCurrentVolume();
            }
        });
        
        // Load voices
        await this.loadVoices();
        this.reportProgress(50, `${this.name} voices loaded`);
        
        // Set up voice from preferences
        await this.setupVoiceFromPreferences();
        this.reportProgress(70, `${this.name} voice preferences configured`);
        
        // Check if we have an API key
        this.isReady = !!this.apiKey;
        
        if (!this.isReady) {
            console.info(`${this.name}: API key not configured; provider unavailable until configured`);
            this.reportProgress(100, `${this.name} not configured`);
            return true;
        }
        
        // Only mark as complete if we have an API key
        this.reportProgress(100, `${this.name} initialization complete`);
        
        return true;
    }
    
    /**
     * Get the default API base URL for this provider
     * @returns {string} - Default API base URL
     */
    getDefaultApiBaseUrl() {
        // To be implemented by subclasses
        return '';
    }
    
    /**
     * 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) {
            console.error(`${this.name}: Required dependencies not found`);
            return false;
        }
        
        // Get preferred voice ID from preferences
        const preferredVoiceId = persistenceManager.getPreference('tts', `${this.id}_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 = this.voices.find(v => v.id === preferredVoiceId);
            return true;
        }
        
        // Otherwise, select voice based on locale
        if (currentLocale) {
            return this.selectVoiceForLocale(currentLocale);
        }
        
        // Fall back to default voice
        return this.selectDefaultVoice();
    }
    
    /**
     * Load available voices from API
     * @returns {Promise<boolean>} - Resolves with success status
     */
    async loadVoices() {
        // To be implemented by subclasses
        this.voices = [];
        return true;
    }
    
    /**
     * Select a voice for the given locale
     * @param {string} locale - Locale code
     * @returns {boolean} - Success status
     */
    selectVoiceForLocale(locale) {
        // To be implemented by subclasses
        return this.selectDefaultVoice();
    }
    
    /**
     * Select a default voice
     * @returns {boolean} - Success status
     */
    selectDefaultVoice() {
        if (this.voices.length > 0) {
            this.voiceOptions.voice = this.voices[0];
            return true;
        }
        return false;
    }
    
    /**
     * Generate speech audio blob for the given text using the API.
     * @param {string} text - The text to synthesize.
     * @returns {Promise<Object>} - A promise that resolves with the audio data object.
     */
    async generateSpeechAudio(text, options = {}) {
        // To be implemented by subclasses
        return { success: false, reason: 'not_implemented' };
    }
    
    /**
     * Speak preloaded audio data
     * @param {Object} preloadData - Preloaded audio data
     * @param {Function} callback - Callback for when speech completes
     * @returns {Promise<Object>} - Resolves when audio finishes playing
     */
    async speakPreloaded(preloadData, callback = null) {
        const completionCallback = typeof callback === 'function' ? callback : null;
        if (!preloadData || !preloadData.audioData) {
            console.error(`${this.name}: Invalid preloaded data`);
            const result = { success: false, reason: 'invalid_data' };
            if (completionCallback) completionCallback(result);
            return result;
        }

        return new Promise((resolve) => {
            // Create an audio element to play the audio
            const audioBlob = new Blob([preloadData.audioData], { type: 'audio/mp3' });
            const audioUrl = URL.createObjectURL(audioBlob);
            const audio = new Audio(audioUrl);
            let settled = false;

            audio.volume = this.getPlaybackVolume();
            console.log(`${this.name}: Playback volume set to ${audio.volume.toFixed(2)}`);

            // Set up state
            this.isSpeaking = true;
            this.currentAudio = audio;

            const finish = (result) => {
                if (settled) {
                    return;
                }

                settled = true;
                this.isSpeaking = false;
                if (this.currentAudio === audio) {
                    this.currentAudio = null;
                }
                if (this.currentPlaybackFinish === finish) {
                    this.currentPlaybackFinish = null;
                }
                URL.revokeObjectURL(audioUrl);

                if (completionCallback) completionCallback(result);
                resolve(result);
            };
            this.currentPlaybackFinish = finish;

            // Set up event handlers
            audio.onended = () => {
                finish({ success: true });
            };

            audio.onerror = (error) => {
                console.error(`${this.name}: Audio playback error:`, error);
                finish({ success: false, reason: 'playback_error', error });
            };

            // Play the audio
            audio.play().then(() => {
                document.dispatchEvent(new CustomEvent('tts:audio-started', {
                    detail: { provider: this.id || this.name }
                }));
            }).catch(error => {
                console.error(`${this.name}: Failed to play audio:`, error);
                finish({ success: false, reason: 'playback_error', error });
            });
        });
    }

    /**
     * Get the current effective TTS playback volume.
     * @returns {number} Volume from 0 to 1.
     */
    getPlaybackVolume() {
        const persistenceManager = this.getModule('persistence-manager');
        if (!persistenceManager) {
            return 1.0;
        }

        const masterVolume = persistenceManager.getPreference(
            'audio',
            'masterVolume',
            persistenceManager.getPreference('audio', 'master_volume', 1.0)
        );
        const masterEnabled = persistenceManager.getPreference('audio', 'masterVolumeEnabled', true) !== false;
        const ttsVolume = persistenceManager.getPreference(
            'audio',
            'ttsVolume',
            persistenceManager.getPreference('audio', 'tts_volume', 1.0)
        );
        const ttsEnabled = persistenceManager.getPreference('audio', 'ttsVolumeEnabled', true) !== false;

        return Math.max(0, Math.min(1, (masterEnabled ? masterVolume : 0) * (ttsEnabled ? ttsVolume : 0)));
    }

    /**
     * Apply updated volume settings to currently playing audio.
     */
    applyCurrentVolume() {
        if (!this.currentAudio) {
            return;
        }

        this.currentAudio.volume = this.getPlaybackVolume();
        console.log(`${this.name}: Updated current playback volume to ${this.currentAudio.volume.toFixed(2)}`);
    }
    
    /**
     * Stop speaking
     * @returns {boolean} - Success status
     */
    stop() {
        if (this.currentAudio) {
            try {
                // Stop current audio
                this.currentAudio.pause();
                this.currentAudio.currentTime = 0;
                if (this.currentPlaybackFinish) {
                    this.currentPlaybackFinish({ success: false, reason: 'stopped' });
                }
                
                // Clean up
                this.isSpeaking = false;
                this.currentAudio = null;
                
                return true;
            } catch (error) {
                console.error(`${this.name}: Error stopping audio:`, error);
                return false;
            }
        }
        return true; // Already stopped
    }

    fadeOutCurrentAudio(duration = 1000) {
        if (!this.currentAudio) {
            return Promise.resolve(true);
        }

        const audio = this.currentAudio;
        const startVolume = audio.volume;
        const startedAt = performance.now();

        return new Promise((resolve) => {
            const tick = () => {
                const progress = Math.min(1, (performance.now() - startedAt) / duration);
                audio.volume = startVolume * (1 - progress);

                if (progress >= 1) {
                    this.stop();
                    resolve(true);
                    return;
                }

                requestAnimationFrame(tick);
            };

            tick();
        });
    }
    
    /**
     * 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) {
            if (callback) {
                callback({ success: false, reason: 'not_ready' });
            }
            return false;
        }
        
        // Generate and play speech
        this.generateSpeechAudio(text).then(result => {
            if (result.success && result.audioData) {
                // Create audio from blob and play it
                this.speakPreloaded({ audioData: result.audioData }, callback);
            } else if (callback) {
                callback({ success: false, reason: 'generation_failed' });
            }
        }).catch(error => {
            if (callback) {
                callback({ success: false, reason: 'generation_error', error });
            }
        });
        
        return true;
    }
    
    /**
     * Calculate audio duration from audio buffer
     * @param {ArrayBuffer} audioData - Audio data buffer
     * @returns {Promise<number>} - Duration in milliseconds
     */
    async calculateAudioDuration(audioData) {
        try {
            // Use Web Audio API to decode audio and get duration
            const audioContext = new (window.AudioContext || window.webkitAudioContext)();
            const audioBuffer = await audioContext.decodeAudioData(audioData.slice(0));
            const durationMs = audioBuffer.duration * 1000;

            // Close the audio context to free resources
            await audioContext.close();

            console.log(`${this.name}: Calculated audio duration: ${durationMs.toFixed(0)}ms`);
            return durationMs;
        } catch (error) {
            console.warn(`${this.name}: Failed to calculate audio duration:`, error);
            return 0;
        }
    }

    /**
     * Preload speech for later playback
     * @param {string} text - Text to preload
     * @returns {Promise<Object>} - Preloaded speech data
     */
    async preloadSpeech(text, options = {}) {
        if (!this.isReady) {
            return { success: false, reason: 'not_ready' };
        }

        try {
            // Generate speech
            const result = await this.generateSpeechAudio(text, options);

            if (!result.success) {
                return { success: false, reason: 'generation_failed' };
            }

            // Calculate actual audio duration if not provided
            let duration = result.duration || 0;
            if (duration === 0 && result.audioData) {
                duration = await this.calculateAudioDuration(result.audioData);
            }

            return {
                success: true,
                audioData: result.audioData,
                text,
                duration: duration
            };
        } catch (error) {
            return { success: false, reason: 'generation_error', error };
        }
    }
    
    /**
     * 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 += '.';
        }
        
        return processed;
    }
    
    /**
     * Handle API key change event
     * @param {Event} event - Event object
     */
    handleApiKeyChanged(event) {
        if (event && event.detail && event.detail.provider === this.id) {
            const newKey = event.detail.key || '';
            const oldKey = this.apiKey;
            
            // Security check - never use a URL as an API key
            if (newKey && newKey.startsWith('http')) {
                console.error(`${this.name}: Received URL instead of API key, ignoring it`);
                return;
            }
            
            // Update API key
            this.apiKey = newKey;
            
            // Save to preferences
            const persistenceManager = this.getModule('persistence-manager');
            if (persistenceManager && oldKey !== newKey) {
                persistenceManager.updatePreference('tts', `${this.id}_api_key`, newKey);
            }
            
            // Update ready state
            const wasReady = this.isReady;
            this.isReady = !!this.apiKey;
            
            // If state changed (now ready/not-ready), notify the TTS factory
            if (wasReady !== this.isReady) {
                console.log(`${this.name}: TTS ready state changed to ${this.isReady ? 'ready' : 'not ready'} after API key change`);
                
                // Find and notify the TTS factory
                const ttsFactory = this.getModule('tts-factory');
                if (ttsFactory) {
                    // If we have a key now (and didn't before), try initializing voices
                    if (this.isReady && !wasReady) {
                        // Reload voices with the new API key
                        this.loadVoices().then((voicesLoaded) => {
                            this.isReady = voicesLoaded !== false && !!this.apiKey;
                            // Then set up voice from preferences
                            this.setupVoiceFromPreferences().then(() => {
                                console.log(`${this.name}: API key status: ${this.isReady ? 'ready' : 'not ready'}`);
                                // Notify the factory of our readiness change
                                ttsFactory.updateTTSAvailability();
                                document.dispatchEvent(new CustomEvent('tts:status:updated', {
                                    detail: { provider: this.id, ready: this.isReady }
                                }));
                            });
                        });
                    } else {
                        // Just update the availability
                        ttsFactory.updateTTSAvailability();
                    }
                }
            }
        }
    }
    
    /**
     * Handle API URL change event
     * @param {Event} event - Event object
     */
    handleApiUrlChanged(event) {
        if (event && event.detail && event.detail.provider === this.id) {
            const oldUrl = this.apiBaseUrl;
            const newUrl = event.detail.url || this.getDefaultApiBaseUrl();
            
            // Update API URL
            this.apiBaseUrl = newUrl;
            
            // Save to preferences
            const persistenceManager = this.getModule('persistence-manager');
            if (persistenceManager && oldUrl !== newUrl) {
                persistenceManager.updatePreference('tts', `${this.id}_api_url`, newUrl);
            }
            
            // Only reinitialize if the URL actually changed and we have an API key
            if (oldUrl !== newUrl && this.isReady) {
                console.log(`${this.name}: API URL changed, reinitializing`);
                
                // Reload voices with the new API URL if we're ready
                this.loadVoices().then((voicesLoaded) => {
                    this.isReady = voicesLoaded !== false && !!this.apiKey;
                    // Then set up voice from preferences
                    this.setupVoiceFromPreferences().then(() => {
                        console.log(`${this.name}: API URL status: ${this.isReady ? 'ready' : 'not ready'}`);
                        
                        // Notify the TTS factory
                        const ttsFactory = this.getModule('tts-factory');
                        if (ttsFactory) {
                            ttsFactory.updateTTSAvailability();
                        }
                        document.dispatchEvent(new CustomEvent('tts:status:updated', {
                            detail: { provider: this.id, ready: this.isReady }
                        }));
                    });
                });
            }
        }
    }
}
