/**
 * BrowserTTSHandler for AI Interactive Fiction
 * Implementation using the browser's Web Speech API
 */
import { TTSHandler } from './tts-handler.js';

export class BrowserTTSHandler extends TTSHandler {
  constructor() {
    super(); // Initialize the base TTSHandler
    this.synth = window.speechSynthesis;
    this.utterance = null;
    this.voices = [];
    this.isReady = false;
    // Initialize voice options through base class
    this.voiceOptions = {
      voice: '',
      rate: 1.0,
      pitch: 1.0,
      volume: 1.0
    };
  }

  /**
   * Check if speech is currently playing
   * @returns {boolean} - True if speaking
   */
  isSpeaking() {
    return this.synth && this.synth.speaking;
  }

  /**
   * Get the ID of this provider
   * @returns {string} - Provider ID
   */
  getId() {
    return 'browser';
  }

  /**
   * Initialize the browser's speech synthesis
   * @param {Function} progressCallback - Optional callback for progress updates
   * @returns {Promise<boolean>} - Resolves to true if initialization was successful
   */
  async initialize(progressCallback = null) {
    if (!this.synth) {
      console.warn('Web Speech API not supported in this browser');
      return false;
    }

    try {
      if (progressCallback) progressCallback(20, 'Loading speech synthesis');
      
      // Get available voices
      this.voices = await this.getVoices();
      
      if (progressCallback) progressCallback(80, 'Speech synthesis loaded');
      
      // If we have voices, we're ready
      this.isReady = this.voices && this.voices.length > 0;
      
      if (this.isReady) {
        console.log('Browser TTS initialized with', this.voices.length, 'voices');
      } else {
        console.warn('Browser TTS initialized but no voices available');
      }
      
      if (progressCallback) progressCallback(100, 'Browser TTS ready');
      
      return this.isReady;
    } catch (error) {
      console.error('Error initializing browser TTS:', error);
      return false;
    }
  }

  /**
   * Get available voices
   * @returns {Promise<Array>} - Array of available voices
   */
  async getVoices() {
    return new Promise((resolve) => {
      // Some browsers get voices immediately, others need an event
      const voices = this.synth.getVoices();
      
      if (voices && voices.length > 0) {
        resolve(voices);
      } else {
        // Wait for voiceschanged event
        const voicesChangedHandler = () => {
          this.synth.removeEventListener('voiceschanged', voicesChangedHandler);
          resolve(this.synth.getVoices());
        };
        
        this.synth.addEventListener('voiceschanged', voicesChangedHandler);
        
        // Safety mechanism: if after 3 seconds we still have no voices and no event,
        // resolve with whatever we have (or empty array)
        // This is not a setTimeout for synchronization, but a safety fallback
        const safetyCheckVoices = () => {
          const currentVoices = this.synth.getVoices() || [];
          console.log(`Safety check: Found ${currentVoices.length} voices`);
          resolve(currentVoices);
        };
        
        // Use requestIdleCallback if available, otherwise requestAnimationFrame
        if (window.requestIdleCallback) {
          window.requestIdleCallback(safetyCheckVoices, { timeout: 3000 });
        } else {
          // Schedule for next frame, but with longer delay
          setTimeout(safetyCheckVoices, 3000);
        }
      }
    });
  }

  /**
   * Check if browser TTS is available
   * @returns {boolean} - True if browser TTS is ready to use
   */
  isAvailable() {
    return this.isReady && this.synth;
  }

  /**
   * Speak text using browser TTS
   * @param {string} text - The text to speak
   * @param {Function} callback - Called when speech completes
   */
  speak(text, callback = null) {
    if (!this.isAvailable() || !text) {
      if (callback) callback();
      return;
    }

    // Stop any current speech
    this.stop();

    try {
      // Create a new utterance
      this.utterance = new SpeechSynthesisUtterance(text);
      
      // Apply voice options
      if (this.voiceOptions.voice) {
        // Find the voice by name or URI
        const selectedVoice = this.voices.find(v => 
          v.name === this.voiceOptions.voice || 
          v.voiceURI === this.voiceOptions.voice
        );
        if (selectedVoice) {
          this.utterance.voice = selectedVoice;
        }
      }
      
      // Apply other options
      this.utterance.rate = this.voiceOptions.rate;
      this.utterance.pitch = this.voiceOptions.pitch;
      this.utterance.volume = this.voiceOptions.volume;
      
      // Handle end of speech
      this.utterance.onend = () => {
        if (callback) callback();
      };
      
      // Handle errors
      this.utterance.onerror = (e) => {
        console.error('Speech synthesis error:', e);
        if (callback) callback();
      };
      
      // Start speaking
      this.synth.speak(this.utterance);
    } catch (error) {
      console.error('Error speaking with browser TTS:', error);
      if (callback) callback();
    }
  }

  /**
   * Stop any ongoing speech
   */
  stop() {
    if (this.synth) {
      this.synth.cancel();
      this.utterance = null;
    }
  }

  /**
   * Set voice options
   * @param {Object} options - Voice options
   */
  setVoiceOptions(options = {}) {
    if (options.voice !== undefined) this.voiceOptions.voice = options.voice;
    if (options.rate !== undefined) this.voiceOptions.rate = options.rate;
    if (options.pitch !== undefined) this.voiceOptions.pitch = options.pitch;
    if (options.volume !== undefined) this.voiceOptions.volume = options.volume;
  }
}
