// Free Browser-based TTS Service using Web Speech API

class BrowserTTSService {
  constructor() {
    this.synth = window.speechSynthesis;
    this.voices = [];
    this.isInitialized = false;
    this.isSpeaking = false;
    this.isPaused = false;
    this.currentUtterance = null;
    this.onSegmentEnd = null;
    this.onComplete = null;
    this.onError = null;
    
    // Cache for voice selections
    this.maleVoice = null;
    this.femaleVoice = null;
    this.narratorMaleVoice = null;
    this.narratorFemaleVoice = null;
    
    // Load voices
    this.loadVoices();
    
    // Chrome loads voices asynchronously
    if (speechSynthesis.onvoiceschanged !== undefined) {
      speechSynthesis.onvoiceschanged = () => this.loadVoices();
    }
  }

  loadVoices() {
    this.voices = this.synth.getVoices();
    console.log('[BrowserTTS] All voices loaded:', this.voices.length);
    this.voices.forEach(v => {
      console.log('  -', v.name, v.lang, v.default ? '(default)' : '');
    });
    
    // Filter to English voices for better compatibility
    const englishVoices = this.voices.filter(voice => 
      voice.lang.startsWith('en')
    );
    
    // Find best male and female voices
    this.maleVoice = this.findBestVoice(['male', 'man', 'boy', 'david', 'james', 'daniel', 'george', 'lee', 'mark', 'steven', 'paul', 'eric', 'oliver', 'william', 'benjamin', 'lucas', 'henry', 'marcus', 'arthur']);
    this.femaleVoice = this.findBestVoice(['female', 'woman', 'girl', 'fiona', 'samantha', 'victoria', 'zira', 'hazel', 'susan', 'karen', 'linda', 'catherine', 'martha', 'alice', 'emma', 'olivia', 'ava', 'mia', 'sophia', 'isabella', 'charlotte', 'amelia', 'harper', 'emily', 'jessica', 'hannah']);
    
    // Narrator voices (prefer deeper, clearer voices)
    this.narratorMaleVoice = this.findBestVoice(['google us english', 'microsoft david', 'daniel', 'james', 'george', 'oliver', 'william']);
    this.narratorFemaleVoice = this.findBestVoice(['google us english', 'microsoft zira', 'samantha', 'victoria', 'emma', 'olivia', 'sophia']);
    
    // Fallbacks
    if (!this.maleVoice) this.maleVoice = englishVoices[0] || this.voices[0];
    if (!this.femaleVoice) this.femaleVoice = englishVoices[0] || this.voices[0];
    if (!this.narratorMaleVoice) this.narratorMaleVoice = this.maleVoice;
    if (!this.narratorFemaleVoice) this.narratorFemaleVoice = this.femaleVoice;
    
    console.log('[BrowserTTS] Selected voices:');
    console.log('  Male:', this.maleVoice?.name);
    console.log('  Female:', this.femaleVoice?.name);
    console.log('  Narrator Male:', this.narratorMaleVoice?.name);
    console.log('  Narrator Female:', this.narratorFemaleVoice?.name);
    
    this.isInitialized = true;
    
    // Trigger voices changed event for components
    if (this.onVoicesChanged) {
      this.onVoicesChanged();
    }
  }

  findBestVoice(indicators) {
    // First try to find Google or Microsoft voices (better quality)
    for (const indicator of indicators) {
      const voice = this.voices.find(v => 
        v.name.toLowerCase().includes(indicator) && 
        (v.name.includes('Google') || v.name.includes('Microsoft'))
      );
      if (voice) return voice;
    }
    
    // Then try any voice with the indicator
    for (const indicator of indicators) {
      const voice = this.voices.find(v => 
        v.name.toLowerCase().includes(indicator)
      );
      if (voice) return voice;
    }
    
    return null;
  }

  /**
   * Get available voices categorized by gender
   */
  getVoices() {
    return this.voices.map(voice => ({
      name: voice.name,
      displayName: voice.name,
      lang: voice.lang,
      gender: this.detectGender(voice.name),
      local: true,
      default: voice.default
    }));
  }

  /**
   * Simple heuristic to detect voice gender from name
   */
  detectGender(voiceName) {
    const name = voiceName.toLowerCase();
    
    // Female indicators
    const femaleIndicators = [
      'female', 'woman', 'girl', 'fiona', 'samantha', 'victoria', 
      'zira', 'hazel', 'susan', 'karen', 'linda', 'catherine',
      'martha', 'alice', 'emma', 'olivia', 'ava', 'mia',
      'sophia', 'isabella', 'charlotte', 'amelia', 'harper', 'emily', 'jessica'
    ];
    
    // Male indicators
    const maleIndicators = [
      'male', 'man', 'boy', 'david', 'james', 'daniel',
      'george', 'lee', 'mark', 'steven', 'paul', 'eric',
      'oliver', 'william', 'benjamin', 'lucas', 'henry', 'marcus'
    ];
    
    for (const indicator of femaleIndicators) {
      if (name.includes(indicator)) return 'Female';
    }
    
    for (const indicator of maleIndicators) {
      if (name.includes(indicator)) return 'Male';
    }
    
    // Default to unknown
    return 'Unknown';
  }

  /**
   * Find a voice by name
   */
  findVoice(voiceName) {
    if (!voiceName) return this.narratorMaleVoice;
    
    const exactMatch = this.voices.find(v => v.name === voiceName);
    if (exactMatch) return exactMatch;
    
    const partialMatch = this.voices.find(v => v.name.toLowerCase().includes(voiceName.toLowerCase()));
    if (partialMatch) return partialMatch;
    
    return this.narratorMaleVoice;
  }

  /**
   * Get voice by gender
   */
  getVoiceByGender(gender) {
    if (gender === 'Female') {
      return this.femaleVoice || this.voices[0];
    }
    return this.maleVoice || this.voices[0];
  }

  /**
   * Get narrator voice by gender
   */
  getNarratorVoice(gender = 'Male') {
    if (gender === 'Female') {
      return this.narratorFemaleVoice || this.femaleVoice || this.voices[0];
    }
    return this.narratorMaleVoice || this.maleVoice || this.voices[0];
  }

  /**
   * Get character voice based on character properties
   */
  getCharacterVoice(character) {
    if (!character) return this.maleVoice;
    
    // If character has explicit voice assigned
    if (character.voice?.name) {
      return this.findVoice(character.voice.name);
    }
    
    // Detect gender from character
    const traits = (character.traits || []).map(t => t.toLowerCase());
    const description = (character.description || '').toLowerCase();
    const name = character.name || '';
    
    // Check for female indicators
    const isFemale = 
      traits.some(t => t.includes('female') || t.includes('woman') || t.includes('girl')) ||
      description.includes('she ') || description.includes('her ') ||
      ['she', 'her', 'lady', 'queen', 'princess', 'woman', 'girl', 'mother', 'daughter', 'goddess', 'witch', 'sorceress'].some(w => 
        name.toLowerCase().includes(w) || description.includes(w)
      );
    
    // Check for male indicators
    const isMale =
      traits.some(t => t.includes('male') || t.includes('man') || t.includes('boy')) ||
      description.includes('he ') || description.includes('his ') ||
      ['he', 'his', 'lord', 'king', 'prince', 'man', 'boy', 'father', 'son', 'god', 'wizard'].some(w => 
        name.toLowerCase().includes(w) || description.includes(w)
      );
    
    if (isFemale) {
      console.log('[BrowserTTS] Character', character.name, 'detected as Female');
      return this.femaleVoice;
    }
    
    if (isMale) {
      console.log('[BrowserTTS] Character', character.name, 'detected as Male');
      return this.maleVoice;
    }
    
    // Default to male for unknown
    console.log('[BrowserTTS] Character', character.name, 'gender unknown, using male voice');
    return this.maleVoice;
  }

  /**
   * Speak text with options
   */
  speak(text, options = {}) {
    const {
      voice,
      pitch = 1,
      rate = 1,
      volume = 1,
      onEnd,
      onError,
      onBoundary
    } = options;

    return new Promise((resolve, reject) => {
      if (!this.synth) {
        const error = new Error('Speech Synthesis not supported');
        reject(error);
        if (onError) onError(error);
        return;
      }

      // Cancel any ongoing speech
      this.synth.cancel();

      const utterance = new SpeechSynthesisUtterance(text);
      
      if (voice) {
        utterance.voice = typeof voice === 'string' ? this.findVoice(voice) : voice;
      }
      
      utterance.pitch = pitch;
      utterance.rate = rate;
      utterance.volume = volume;

      utterance.onend = () => {
        this.isSpeaking = false;
        this.isPaused = false;
        this.currentUtterance = null;
        if (onEnd) onEnd();
        resolve();
      };

      utterance.onerror = (event) => {
        this.isSpeaking = false;
        this.isPaused = false;
        this.currentUtterance = null;
        const error = new Error(event.error);
        if (onError) onError(error);
        reject(error);
      };

      utterance.onboundary = (event) => {
        if (onBoundary) onBoundary(event);
      };

      this.currentUtterance = utterance;
      this.isSpeaking = true;
      this.isPaused = false;
      
      console.log('[BrowserTTS] Speaking:', text.substring(0, 50) + '...', 'Voice:', utterance.voice?.name);
      
      this.synth.speak(utterance);
    });
  }

  /**
   * Speak multiple segments sequentially with different voices
   */
  speakBatch(segments, options = {}) {
    console.log('[BrowserTTS] speakBatch called with', segments.length, 'segments');
    
    return new Promise(async (resolve, reject) => {
      const {
        pitch = 1,
        rate = 1,
        volume = 1,
        narratorGender = 'Male',
        onSegmentEnd,
        onComplete,
        onError
      } = options;

      const narratorVoice = this.getNarratorVoice(narratorGender);
      
      console.log('[BrowserTTS] Narrator voice:', narratorVoice?.name);
      console.log('[BrowserTTS] Available voices - Male:', this.maleVoice?.name, 'Female:', this.femaleVoice?.name);

      try {
        for (let i = 0; i < segments.length; i++) {
          const segment = segments[i];
          
          console.log('[BrowserTTS] Processing segment', i + 1, ':', {
            type: segment.type,
            textLength: segment.text?.length,
            speaker: segment.speaker,
            hasCharacterVoice: !!segment.characterVoice,
            speakerGender: segment.speakerGender
          });

          // Check if cancelled
          if (!this.isSpeaking) {
            console.log('[BrowserTTS] Stopped - isSpeaking is false');
            resolve();
            return;
          }

          // Pause if needed
          while (this.isPaused) {
            await new Promise(r => setTimeout(r, 100));
            if (!this.isSpeaking) {
              resolve();
              return;
            }
          }

          // Determine voice for this segment
          let voiceToUse = narratorVoice;

          if (segment.type === 'dialogue' && segment.speaker) {
            // Use character's voice for dialogue
            if (segment.characterVoice) {
              voiceToUse = segment.characterVoice;
            } else if (segment.speakerGender === 'Female') {
              voiceToUse = this.femaleVoice;
            } else if (segment.speakerGender === 'Male') {
              voiceToUse = this.maleVoice;
            }
            console.log('[BrowserTTS] Segment', i + 1, 'dialogue by', segment.speaker, 'using', voiceToUse?.name);
          } else {
            console.log('[BrowserTTS] Segment', i + 1, 'narration using', narratorVoice?.name);
          }

          // Skip empty text
          if (!segment.text || segment.text.trim().length === 0) {
            console.log('[BrowserTTS] Skipping empty segment', i + 1);
            if (onSegmentEnd) onSegmentEnd(i, segment);
            continue;
          }

          await this.speak(segment.text, {
            voice: voiceToUse,
            pitch: segment.type === 'dialogue' ? pitch * 1.1 : pitch, // Slightly higher pitch for dialogue
            rate,
            volume,
            onEnd: () => {
              console.log('[BrowserTTS] Segment', i + 1, 'completed');
              if (onSegmentEnd) onSegmentEnd(i, segment);
            },
            onError: (err) => {
              console.error('[BrowserTTS] Segment', i + 1, 'error:', err);
              if (onError) onError(err, i, segment);
            }
          });
        }

        console.log('[BrowserTTS] All segments completed');
        if (onComplete) onComplete();
        resolve();
      } catch (error) {
        console.error('[BrowserTTS] speakBatch error:', error);
        reject(error);
      }
    });
  }

  /**
   * Pause current speech
   */
  pause() {
    if (this.isSpeaking && !this.isPaused && this.synth.speaking) {
      this.synth.pause();
      this.isPaused = true;
      console.log('[BrowserTTS] Paused');
    }
  }

  /**
   * Resume paused speech
   */
  resume() {
    if (this.isSpeaking && this.isPaused && this.synth.paused) {
      this.synth.resume();
      this.isPaused = false;
      console.log('[BrowserTTS] Resumed');
    }
  }

  /**
   * Stop all speech
   */
  stop() {
    this.isSpeaking = false;
    this.isPaused = false;
    this.synth.cancel();
    console.log('[BrowserTTS] Stopped');
  }

  /**
   * Check if speech synthesis is supported
   */
  isSupported() {
    return 'speechSynthesis' in window;
  }

  /**
   * Get service health status
   */
  healthCheck() {
    return {
      healthy: this.isSupported(),
      reason: this.isSupported() ? 'OK' : 'Browser does not support Speech Synthesis',
      voiceCount: this.voices.length,
      maleVoice: this.maleVoice?.name,
      femaleVoice: this.femaleVoice?.name
    };
  }
}

// Singleton instance
let browserTTSService = null;

export function getBrowserTTS() {
  if (!browserTTSService) {
    browserTTSService = new BrowserTTSService();
  }
  return browserTTSService;
}

export default BrowserTTSService;
