/**
 * ApiTTSHandler for AI Interactive Fiction
 * Implementation using external TTS APIs like ElevenLabs
 */
import { TTSHandler } from './tts-handler.js';

export class ApiTTSHandler extends TTSHandler {
  constructor() {
    super(); // Initialize the base TTSHandler
    this.isReady = false;
    this.enabled = false; // Disabled by default until options panel is implemented
    this.audioElement = null;
    // Set voice options through base class
    this.voiceOptions = {
      voice: '8JNqTOY3RaSYcHTVJZ0G', // Default ElevenLabs voice ID
      model: 'eleven_multilingual_v1',
      stability: 0,
      similarityBoost: 0,
      style: 0.5,
      useSpeakerBoost: true
    };
    this.apiKey = 'd191e27c2e5b07573b39fe70f0783f48'; // From speech.js
    this.apiUrl = 'https://api.elevenlabs.io/v1/text-to-speech';
    this.voicesApiUrl = 'https://api.elevenlabs.io/v1/voices'; // Separate URL for voices endpoint
    this.cache = new Map();
    this.currentCallback = null;
  }

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

  /**
   * Initialize the API TTS system
   * @param {Function} progressCallback - Optional callback for progress updates
   * @returns {Promise<boolean>} - Resolves to true if initialization was successful
   */
  async initialize(progressCallback = null) {
    try {
      if (progressCallback) progressCallback(20, 'Setting up API TTS');

      // Create audio element for playback
      this.audioElement = new Audio();
      
      // Set up audio event listeners
      this.audioElement.onended = () => {
        if (this.currentCallback) {
          const callback = this.currentCallback;
          this.currentCallback = null;
          callback();
        }
      };
      
      this.audioElement.onerror = (error) => {
        console.error('Audio playback error:', error);
        if (this.currentCallback) {
          const callback = this.currentCallback;
          this.currentCallback = null;
          callback();
        }
      };
      
      if (progressCallback) progressCallback(80, 'API TTS ready');
      
      // Only check API if enabled
      if (this.enabled) {
        // Check if the API is reachable with a simple request
        try {
          const testResponse = await fetch(this.voicesApiUrl, {
            method: 'GET',
            headers: {
              'xi-api-key': this.apiKey
            }
          });
          
          if (testResponse.ok) {
            this.isReady = true;
            console.log('API TTS initialized successfully');
          } else {
            console.warn('API TTS initialized but API may not be accessible');
          }
        } catch (apiError) {
          console.warn('Could not verify API access, but continuing:', apiError);
          // We'll still mark as ready and try when speak is called
          this.isReady = true;
        }
      } else {
        console.log('API TTS is disabled by default. Enable via options panel when implemented.');
      }
      
      if (progressCallback) progressCallback(100, 'API TTS initialization complete');
      
      return this.isReady;
    } catch (error) {
      console.error('Error initializing API TTS:', error);
      return false;
    }
  }

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

  /**
   * Generate an MD5 hash for text caching
   * @param {string} text - Text to hash
   * @returns {string} - MD5 hash
   */
  generateHash(text) {
    // Simple hash function for client-side use
    // For production, consider using a proper hashing library
    let hash = 0;
    if (text.length === 0) return hash.toString();
    
    for (let i = 0; i < text.length; i++) {
      const char = text.charCodeAt(i);
      hash = ((hash << 5) - hash) + char;
      hash = hash & hash; // Convert to 32bit integer
    }
    
    return Math.abs(hash).toString(16);
  }

  /**
   * Convert text to speech via API and play it
   * @param {string} text - Text to speak
   * @param {Function} callback - Called when speech completes
   */
  async speak(text, callback = null) {
    if (!this.isAvailable() || !text) {
      if (callback) callback();
      return;
    }

    // Stop any current speech
    this.stop();
    
    // Set new callback
    this.currentCallback = callback;
    
    try {
      // Check cache first
      const cacheKey = this.generateHash(text + JSON.stringify(this.voiceOptions));
      let audioUrl = this.cache.get(cacheKey);
      
      if (!audioUrl) {
        // Make API request to get audio
        const response = await fetch(`${this.apiUrl}/${this.voiceOptions.voice}`, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'xi-api-key': this.apiKey
          },
          body: JSON.stringify({
            text: text,
            model_id: this.voiceOptions.model,
            voice_settings: {
              stability: this.voiceOptions.stability,
              similarity_boost: this.voiceOptions.similarityBoost,
              style: this.voiceOptions.style,
              use_speaker_boost: this.voiceOptions.useSpeakerBoost
            }
          })
        });
        
        if (!response.ok) {
          throw new Error(`API returned ${response.status}: ${response.statusText}`);
        }
        
        // Get the audio data as blob
        const audioBlob = await response.blob();
        audioUrl = URL.createObjectURL(audioBlob);
        
        // Store in cache
        this.cache.set(cacheKey, audioUrl);
      }
      
      // Play the audio
      this.audioElement.src = audioUrl;
      await this.audioElement.play();
      
    } catch (error) {
      console.error('Error speaking with API TTS:', error);
      if (this.currentCallback) {
        const callback = this.currentCallback;
        this.currentCallback = null;
        callback();
      }
    }
  }

  /**
   * Stop any ongoing speech
   */
  stop() {
    if (this.audioElement) {
      this.audioElement.pause();
      this.audioElement.currentTime = 0;
    }
    
    if (this.currentCallback) {
      const callback = this.currentCallback;
      this.currentCallback = null;
      callback();
    }
  }

  /**
   * Set voice options
   * @param {Object} options - Voice options
   */
  setVoiceOptions(options = {}) {
    if (options.voice !== undefined) this.voiceOptions.voice = options.voice;
    if (options.model !== undefined) this.voiceOptions.model = options.model;
    if (options.stability !== undefined) this.voiceOptions.stability = options.stability;
    if (options.similarityBoost !== undefined) this.voiceOptions.similarityBoost = options.similarityBoost;
    if (options.style !== undefined) this.voiceOptions.style = options.style;
    if (options.useSpeakerBoost !== undefined) this.voiceOptions.useSpeakerBoost = options.useSpeakerBoost;
  }

  /**
   * Get available voices from the API
   * @returns {Promise<Array>} - Array of available voices
   */
  async getVoices() {
    if (!this.enabled) {
      return [];
    }
    
    try {
      const response = await fetch(this.voicesApiUrl, {
        method: 'GET',
        headers: {
          'xi-api-key': this.apiKey
        }
      });
      
      if (!response.ok) {
        throw new Error(`API returned ${response.status}: ${response.statusText}`);
      }
      
      const data = await response.json();
      return data.voices || [];
      
    } catch (error) {
      console.error('Error getting voices from API:', error);
      return [];
    }
  }
  
  /**
   * Enable or disable the API TTS
   * @param {boolean} enabled - Whether the API TTS should be enabled
   */
  setEnabled(enabled) {
    this.enabled = enabled;
    if (enabled && !this.isReady) {
      // Re-initialize if enabled
      this.initialize();
    }
  }

  /**
   * Check if speech is currently playing
   * @returns {boolean} - True if speaking
   */
  isSpeaking() {
    return this.audioElement !== null && 
           !this.audioElement.paused && 
           !this.audioElement.ended;
  }
}
