/**
 * TTS Factory for AI Interactive Fiction
 * Manages different TTS implementations with a common interface
 */
class TTSFactory {
  constructor() {
    this.ttsHandler = null;
    this.handlers = {};
    this.initializationAttempted = false;
    this.initializationPromise = null;
    this.ttsEnabled = true;
    this.progressCallback = null;
    this.persistenceManager = null;
  }

  /**
   * Initialize the TTS Factory - Static method for the module loader
   * @param {Function} reportProgress - Function to report loading progress to the loader
   * @returns {Promise} - Resolves when TTS is initialized
   */
  static async initializeInterface(reportProgress = null) {
    console.log('TTS Factory: Initializing interface');
    
    // Create singleton instance if needed
    if (!window.ttsFactory) {
      window.ttsFactory = new TTSFactory();
    }
    
    // Initialize TTS with the progress callback
    window.ttsFactory.progressCallback = reportProgress;
    
    try {
      // Start initialization process
      await window.ttsFactory.initialize();
      return true;
    } catch (error) {
      console.error('Error initializing TTS Factory:', error);
      return false;
    }
  }

  /**
   * Initialize the TTS Factory
   * This will load and initialize all available TTS handlers
   * @returns {Promise} - Resolves when initialization is complete
   */
  async initialize() {
    if (this.initializationPromise) {
      return this.initializationPromise;
    }

    this.initializationPromise = new Promise(async (resolve) => {
      this.initializationAttempted = true;
      
      const reportProgress = (percent, message) => {
        console.log(`TTS progress: ${percent}% - ${message}`);
        if (this.progressCallback && typeof this.progressCallback === 'function') {
          this.progressCallback(percent, message);
        }
      };

      try {
        // Report starting initialization
        reportProgress(10, 'Loading TTS modules');
        
        // Get persistence manager if available
        if (window.PersistenceManager) {
          this.persistenceManager = window.PersistenceManager;
          reportProgress(15, 'Persistence manager found, loading preferences');
          
          // Load preferences to determine TTS enabled state and preferred provider
          const prefs = this.persistenceManager.getAllPreferences();
          if (prefs && prefs.tts) {
            this.ttsEnabled = prefs.tts.enabled;
            console.log(`TTS Factory: Setting initial TTS enabled state to ${this.ttsEnabled ? 'enabled' : 'disabled'} from preferences`);
          }
        }
        
        // Import needed modules dynamically
        const [{ BrowserTTSHandler }, { KokoroHandler }, { ApiTTSHandler }] = await Promise.all([
          import('./browser-tts-handler.js'),
          import('./kokoro-handler.js'),
          import('./api-tts-handler.js')
        ]);
        
        reportProgress(20, 'TTS modules loaded');

        // Create handlers
        const browserHandler = new BrowserTTSHandler();
        const kokoroHandler = new KokoroHandler();
        const apiHandler = new ApiTTSHandler();
        
        // Store handlers
        this.handlers = {
          browser: browserHandler,
          kokoro: kokoroHandler,
          api: apiHandler
        };

        // Get preferred TTS mode from options
        const preferredTTSMode = this.getPreferredTTSMode();

        // Initialize the preferred handler first
        if (preferredTTSMode === 'browser') {
          // User prefers browser TTS
          await this.initializeBrowserTTS(browserHandler, reportProgress);
        } else if (preferredTTSMode === 'api') {
          // User prefers API TTS
          await this.initializeApiTTS(apiHandler, reportProgress);
          
          // Fallback to browser TTS if API fails
          if (!apiHandler.isAvailable()) {
            await this.initializeBrowserTTS(browserHandler, reportProgress);
          }
        } else {
          // Default flow: prefer Kokoro, with browser as immediate fallback
          // Initialize browser TTS immediately for a responsive experience
          await this.initializeBrowserTTS(browserHandler, reportProgress);
          
          // Then schedule Kokoro loading in the background
          reportProgress(75, 'Scheduling Kokoro TTS initialization');
          this.scheduleKokoroInitialization(kokoroHandler, reportProgress).then((kokoroAvailable) => {
            if (kokoroAvailable) {
              // Switch to Kokoro as it's the best option and set as preferred
              this.ttsHandler = kokoroHandler;
              this.setPreferredTTSMode('kokoro');
              this.dispatchTTSReadyEvent(true, 'kokoro', kokoroHandler);
              reportProgress(100, 'Kokoro TTS ready');
              
              // Apply voice settings from preferences if available
              this.applyVoiceSettingsFromPreferences();
            } else if (!this.getPreferredTTSMode()) {
              // If Kokoro failed and no preference was previously set, 
              // set browser as preferred mode
              this.setPreferredTTSMode('browser');
            }
          });
        }

        // Apply voice settings from preferences for initial handler
        this.applyVoiceSettingsFromPreferences();

        // Resolve initialization even though Kokoro is still loading in background
        reportProgress(80, 'TTS interface ready' + 
          (preferredTTSMode !== 'kokoro' ? '' : ' (Kokoro loading in background)'));
        resolve(true);
        
      } catch (error) {
        console.error('Error initializing TTS Factory:', error);
        
        // If we have any handler working, consider initialization successful
        if (this.ttsHandler) {
          reportProgress(100, `Using ${this.ttsHandler.getId()} TTS (fallback)`);
          resolve(true);
        } else {
          this.dispatchTTSReadyEvent(false);
          reportProgress(100, 'TTS initialization failed');
          resolve(false);
        }
      }
    });

    return this.initializationPromise;
  }

  /**
   * Apply stored voice settings from preferences
   * @private
   */
  applyVoiceSettingsFromPreferences() {
    if (!this.ttsHandler || !this.persistenceManager) return;
    
    const prefs = this.persistenceManager.getAllPreferences();
    if (prefs && prefs.tts) {
      if (prefs.tts.voice) {
        console.log(`TTS Factory: Setting voice to ${prefs.tts.voice} from preferences`);
        // Check if setVoice exists, otherwise try setting through voiceOptions
        if (typeof this.ttsHandler.setVoice === 'function') {
          this.ttsHandler.setVoice(prefs.tts.voice);
        } else if (typeof this.ttsHandler.setVoiceOptions === 'function') {
          this.ttsHandler.setVoiceOptions({ voice: prefs.tts.voice });
        }
      }
      
      if (prefs.tts.rate !== undefined) {
        console.log(`TTS Factory: Setting speech rate to ${prefs.tts.rate} from preferences`);
        // Check if setSpeed exists, otherwise try setting through voiceOptions
        if (typeof this.ttsHandler.setSpeed === 'function') {
          this.ttsHandler.setSpeed(prefs.tts.rate);
        } else if (typeof this.ttsHandler.setVoiceOptions === 'function') {
          this.ttsHandler.setVoiceOptions({ rate: prefs.tts.rate });
        }
      }
      
      if (prefs.tts.volume !== undefined && typeof this.ttsHandler.setVolume === 'function') {
        console.log(`TTS Factory: Setting volume to ${prefs.tts.volume} from preferences`);
        this.ttsHandler.setVolume(prefs.tts.volume);
      }
    }
  }

  /**
   * Initialize browser TTS
   * @param {BrowserTTSHandler} handler - The browser TTS handler
   * @param {Function} reportProgress - Progress reporting function
   * @returns {Promise<boolean>} - Resolves with availability status
   */
  async initializeBrowserTTS(handler, reportProgress) {
    reportProgress(30, 'Initializing browser TTS');
    const browserAvailable = await handler.initialize();
    
    if (browserAvailable) {
      this.ttsHandler = handler;
      this.dispatchTTSReadyEvent(true, 'browser', handler);
      reportProgress(40, 'Browser TTS ready');
    } else {
      reportProgress(40, 'Browser TTS not available');
    }
    
    return browserAvailable;
  }

  /**
   * Initialize API TTS
   * @param {ApiTTSHandler} handler - The API TTS handler
   * @param {Function} reportProgress - Progress reporting function
   * @returns {Promise<boolean>} - Resolves with availability status
   */
  async initializeApiTTS(handler, reportProgress) {
    reportProgress(50, 'Initializing API TTS');
    const apiAvailable = await handler.initialize();
    
    if (apiAvailable) {
      this.ttsHandler = handler;
      this.dispatchTTSReadyEvent(true, 'api', handler);
      reportProgress(70, 'API TTS ready');
    }
    
    return apiAvailable;
  }
  
  /**
   * Get preferred TTS mode from storage
   * @returns {string|null} - Preferred TTS mode or null if not set
   */
  getPreferredTTSMode() {
    // First check persistent settings if available
    if (this.persistenceManager) {
      const prefs = this.persistenceManager.getAllPreferences();
      if (prefs && prefs.tts && prefs.tts.provider) {
        console.log(`TTS Factory: Using preferred TTS mode '${prefs.tts.provider}' from persistence manager`);
        return prefs.tts.provider;
      }
    }
    
    // Fallback to localStorage if persistence manager is not available
    try {
      const savedMode = localStorage.getItem('preferred-tts-mode');
      if (savedMode) {
        console.log(`TTS Factory: Using preferred TTS mode '${savedMode}' from localStorage`);
        return savedMode;
      }
    } catch (e) {
      console.warn('Could not read TTS preference from localStorage');
    }
    
    // Default to Kokoro if no preference is found
    return "kokoro";
  }
  
  /**
   * Set preferred TTS mode in storage
   * @param {string} mode - The TTS mode to save as preferred
   */
  setPreferredTTSMode(mode) {
    // Update in persistence manager if available
    if (this.persistenceManager) {
      this.persistenceManager.updatePreference('tts', 'provider', mode);
      console.log(`TTS Factory: Saved preferred TTS mode '${mode}' to persistence manager`);
    }
    
    // Also save to localStorage as backup
    try {
      localStorage.setItem('preferred-tts-mode', mode);
    } catch (e) {
      console.warn('Could not save TTS preference to localStorage');
    }
  }

  /**
   * Schedule Kokoro initialization during idle time
   * @param {Object} kokoroHandler - The Kokoro handler instance
   * @param {Function} reportProgress - Progress reporting function
   * @returns {Promise<boolean>} - Resolves with success status
   */
  scheduleKokoroInitialization(kokoroHandler, reportProgress) {
    // Immediately dispatch the loading started event so tts-player can catch it
    window.dispatchEvent(new CustomEvent('kokoro-loading-started'));
    
    return new Promise((resolve) => {
        // Create the initialization function
        const startKokoroInit = async () => {
            try {
                // Initialize Kokoro with progress callback
                const kokoroAvailable = await kokoroHandler.initialize((percent, message) => {
                    // Scale progress to 80-95% range for the TTS module's overall progress
                    const scaledProgress = 80 + Math.floor(percent * 0.15);
                    reportProgress(scaledProgress, message || `Loading Kokoro TTS: ${percent}%`);
                });
                
                // Mark completion
                if (kokoroAvailable) {
                    reportProgress(95, "Kokoro TTS initialized successfully");
                } else {
                    reportProgress(95, "Kokoro TTS unavailable - using fallback");
                }
                
                // Always dispatch event to indicate completion status
                window.dispatchEvent(new CustomEvent('kokoro-loading-complete', {
                    detail: { success: kokoroAvailable }
                }));
                
                resolve(kokoroAvailable);
            } catch (error) {
                console.error('Error initializing Kokoro:', error);
                reportProgress(95, 'Kokoro TTS failed to initialize - using fallback');
                
                // Dispatch completion event with error information
                window.dispatchEvent(new CustomEvent('kokoro-loading-complete', {
                    detail: { success: false, error: error.message }
                }));
                
                resolve(false);
            }
        };

        // Add timeout protection with a reasonable timeout (30 seconds for resource-intensive operations)
        const timeoutId = setTimeout(() => {
            reportProgress(95, 'Kokoro initialization timed out - using fallback');
            window.dispatchEvent(new CustomEvent('kokoro-loading-complete', {
                detail: { success: false, error: "Timeout" }
            }));
            resolve(false);
        }, 30000); // Increased timeout to 30 seconds since model loading is resource intensive

        // Use requestIdleCallback to start initialization during idle time
        if (window.requestIdleCallback) {
            reportProgress(75, 'Scheduling Kokoro TTS for background loading');
            
            window.requestIdleCallback(() => {
                startKokoroInit().then(() => clearTimeout(timeoutId));
            }, { timeout: 10000 });
        } else {
            reportProgress(75, 'Background loading not available, loading Kokoro normally');
            
            // Use a microtask to avoid blocking the UI thread
            Promise.resolve().then(() => startKokoroInit().then(() => clearTimeout(timeoutId)));
        }
    });
  }

  /**
   * Dispatch a custom event when TTS is ready
   * @param {boolean} available - Whether TTS is available
   * @param {string} type - The type of TTS
   * @param {Object} handler - The TTS handler object
   */
  dispatchTTSReadyEvent(available, type = null, handler = null) {
    const event = new CustomEvent('tts-ready', {
      detail: {
        available,
        type,
        handler,
        enabled: this.ttsEnabled
      }
    });
    window.dispatchEvent(event);
  }

  /**
   * Get information about the active TTS system
   * @returns {Object} - TTS system info
   */
  getActiveTTSInfo() {
    if (!this.ttsHandler) {
      return { available: false, type: 'none', name: 'None' };
    }

    const id = this.ttsHandler.getId();
    const name = {
      'browser': 'Browser TTS',
      'kokoro': 'Kokoro Neural TTS',
      'api': 'ElevenLabs API TTS'
    }[id] || 'Unknown TTS';

    return {
      available: true,
      type: id,
      name: name
    };
  }

  /**
   * Switch to a specific TTS handler
   * @param {string} type - The handler ID to use
   * @returns {boolean} - Success status
   */
  switchTTS(type) {
    if (!this.handlers[type] || !this.handlers[type].isAvailable()) {
      return false;
    }
    
    this.ttsHandler = this.handlers[type];
    this.dispatchTTSReadyEvent(true, type, this.ttsHandler);
    
    // Update preferred TTS mode
    this.setPreferredTTSMode(type);
    
    return true;
  }

  /**
   * Speak text using the active TTS handler
   * @param {string} text - Text to speak
   * @param {Function} callback - Called when speech completes
   * @returns {boolean} - True if speech started successfully
   */
  speak(text, callback = null) {
    if (!this.ttsEnabled || !this.ttsHandler) {
      console.warn("TTSFactory: No active TTS handler available or TTS disabled");
      if (callback) callback("No TTS handler");
      return false;
    }
    
    const handlerType = this.ttsHandler.getId();
    console.log(`TTSFactory: Using ${handlerType} handler to speak "${text}"`);
    
    try {
      this.ttsHandler.speak(text, (result) => {
        console.log(`TTSFactory: Speech completed using ${handlerType}`, result);
        if (callback) callback(result);
      });
      return true;
    } catch (error) {
      console.error('Error speaking:', error);
      if (callback) callback(error);
      return false;
    }
  }

  /**
   * Stop any ongoing speech
   */
  stop() {
    if (this.ttsHandler) {
      this.ttsHandler.stop();
    }
  }

  /**
   * Set voice options for the active handler
   * @param {Object} options - Voice options
   */
  setVoiceOptions(options = {}) {
    if (this.ttsHandler && typeof this.ttsHandler.setVoiceOptions === 'function') {
      this.ttsHandler.setVoiceOptions(options);
      
      // Save settings to persistence manager if available
      if (this.persistenceManager) {
        if (options.voice !== undefined) {
          this.persistenceManager.updatePreference('tts', 'voice', options.voice, false);
        }
        if (options.rate !== undefined) {
          this.persistenceManager.updatePreference('tts', 'rate', options.rate, false);
        }
        if (options.volume !== undefined) {
          this.persistenceManager.updatePreference('tts', 'volume', options.volume, false);
        }
        // Save all changes at once
        this.persistenceManager.savePreferences();
      }
    }
  }

  /**
   * Toggle TTS on/off
   * @returns {boolean} - New TTS enabled state
   */
  toggle() {
    this.ttsEnabled = !this.ttsEnabled;
    console.log(`TTS Factory: Toggling TTS to ${this.ttsEnabled ? 'enabled' : 'disabled'}`);
    
    if (!this.ttsEnabled && this.ttsHandler) {
      this.ttsHandler.stop();
    }
    
    // Save the new state to preferences if persistence manager is available
    if (this.persistenceManager) {
      this.persistenceManager.updatePreference('tts', 'enabled', this.ttsEnabled);
      console.log(`TTS Factory: Saved enabled state (${this.ttsEnabled}) to persistence manager`);
    }
    
    return this.ttsEnabled;
  }

  /**
   * Check if TTS is enabled
   * @returns {boolean} - Current TTS enabled state
   */
  isEnabled() {
    return this.ttsEnabled;
  }

  /**
   * Get available handlers
   * @returns {Object} - Map of available handlers
   */
  getAvailableHandlers() {
    const available = {};
    
    Object.entries(this.handlers).forEach(([id, handler]) => {
      if (handler.isAvailable()) {
        available[id] = handler;
      }
    });
    
    return available;
  }

  /**
   * Get available voices from active handler
   * @returns {Promise<Array>} - Array of available voices
   */
  async getVoices() {
    if (!this.ttsHandler || typeof this.ttsHandler.getVoices !== 'function') {
      return [];
    }
    
    try {
      return await this.ttsHandler.getVoices();
    } catch (error) {
      console.error('Error getting voices:', error);
      return [];
    }
  }
}

// Create singleton instance
const ttsFactory = new TTSFactory();

// Export the factory
export { ttsFactory };

// Keep global reference
window.ttsFactory = ttsFactory;