import { Injectable, signal } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class AudioEngineService {
  public isListening = signal<boolean>(false);
  public wordsDetected = signal<number>(0);
  public linesDetected = signal<number>(0);
  public energyLevel = signal<number>(0); // For visual feedback
  public searchTranscript = signal<string>('');
  public isSearching = signal<boolean>(false);
  public sensitivity = signal<number>(75); // Default sensitivity (50-100)
  public speechRecognitionActive = signal<boolean>(false);
  
  // Real-time background continuous transcript and word buffer for reliability checks
  public backgroundTranscript = signal<string>('');
  private backgroundRecognition: any = null;
  private recentWordsBuffer: { word: string, timestamp: number }[] = [];
  
  public clearSearchTranscript() {
    this.searchTranscript.set('');
  }
  
  private audioContext: AudioContext | null = null;
  private highpassFilter: BiquadFilterNode | null = null;
  private lowpassFilter: BiquadFilterNode | null = null;
  private vocalBandpass: BiquadFilterNode | null = null;
  private analyser: AnalyserNode | null = null;
  private stream: MediaStream | null = null;
  private animationFrame: number | null = null;
  private recognition: any = null;

  // Detection logic state
  private isSpeaking: boolean = false;
  private lastSilenceTime: number = Date.now();
  private lastWordTime: number = 0;
  
  // Constants for tuning - Optimized for close proximity (singer/guitarist)
  private readonly SILENCE_GAP = 100; // ms
  private readonly LINE_SILENCE_GAP = 600; // ms
  private readonly COOLDOWN = 1000; // ms
  private peakEnergy: number = 0;
  private lineStarted: boolean = false;
  private noiseFloor: number = 30; // Adaptive noise floor starting point

  constructor() {
    this.initSpeechRecognition();
    this.initBackgroundRecognition();
  }

  private initBackgroundRecognition() {
    const SpeechRecognition = (window as any).SpeechRecognition || (window as any).webkitSpeechRecognition;
    if (SpeechRecognition) {
      this.backgroundRecognition = new SpeechRecognition();
      this.backgroundRecognition.continuous = true;
      this.backgroundRecognition.interimResults = true;
      this.backgroundRecognition.lang = 'it-IT';

      this.backgroundRecognition.onstart = () => {
        console.log('Background speech recognition started successfully');
        this.speechRecognitionActive.set(true);
      };

      this.backgroundRecognition.onresult = (event: any) => {
        let interimTranscript = '';
        let finalTranscript = '';
        for (let i = event.resultIndex; i < event.results.length; ++i) {
          if (event.results[i].isFinal) {
            finalTranscript += event.results[i][0].transcript;
          } else {
            interimTranscript += event.results[i][0].transcript;
          }
        }
        const fullTranscript = (finalTranscript + ' ' + interimTranscript).trim().toLowerCase();
        if (fullTranscript) {
          this.backgroundTranscript.set(fullTranscript);
          this.updateRecentWords(fullTranscript);
        }
      };

      this.backgroundRecognition.onerror = (err: any) => {
        console.error('Background recognition error:', err);
        this.speechRecognitionActive.set(false);
        if (this.isListening() && err.error !== 'aborted' && err.error !== 'not-allowed') {
          setTimeout(() => this.startBackgroundRecognition(), 1000);
        }
      };

      this.backgroundRecognition.onend = () => {
        console.log('Background speech recognition ended');
        if (this.isListening()) {
          this.startBackgroundRecognition();
        } else {
          this.speechRecognitionActive.set(false);
        }
      };
    }
  }

  private startBackgroundRecognition() {
    if (!this.backgroundRecognition) return;
    try {
      this.backgroundRecognition.start();
    } catch (e) {
      // Recognition already started or running
    }
  }

  private stopBackgroundRecognition() {
    if (!this.backgroundRecognition) return;
    try {
      this.backgroundRecognition.stop();
    } catch (e) {
      // Recognition already stopped
    }
  }

  private updateRecentWords(transcript: string) {
    const words = transcript.split(/\s+/);
    const now = Date.now();
    words.forEach(w => {
      // Strip special characters and keep alphanumeric Italian letters
      const cleaned = w.replace(/[^a-zA-Z0-9àèìòùáéíóú]/g, '').toLowerCase();
      if (cleaned.length >= 3) {
        // Avoid adding duplicate words within the last 3 seconds
        const existing = this.recentWordsBuffer.find(item => item.word === cleaned && now - item.timestamp < 3000);
        if (!existing) {
          this.recentWordsBuffer.push({ word: cleaned, timestamp: now });
        }
      }
    });

    // Keep only words from the last 10 seconds to make it highly responsive
    this.recentWordsBuffer = this.recentWordsBuffer.filter(item => now - item.timestamp < 10000);
  }

  public getRecentWords(): string[] {
    const now = Date.now();
    this.recentWordsBuffer = this.recentWordsBuffer.filter(item => now - item.timestamp < 10000);
    return this.recentWordsBuffer.map(item => item.word);
  }

  public getRecentWordsSince(timestamp: number): string[] {
    const now = Date.now();
    // Consente una sovrapposizione di 1.5 secondi per coprire la latenza di SpeechRecognition
    const cutoff = Math.max(timestamp - 1500, now - 10000);
    this.recentWordsBuffer = this.recentWordsBuffer.filter(item => now - item.timestamp < 10000);
    return this.recentWordsBuffer
      .filter(item => item.timestamp > cutoff)
      .map(item => item.word);
  }

  public clearRecentWords() {
    this.recentWordsBuffer = [];
    this.backgroundTranscript.set('');
  }

  private initSpeechRecognition() {
    const SpeechRecognition = (window as any).SpeechRecognition || (window as any).webkitSpeechRecognition;
    if (SpeechRecognition) {
      this.recognition = new SpeechRecognition();
      this.recognition.continuous = false; // Stop after one phrase for search
      this.recognition.interimResults = true;
      this.recognition.lang = 'it-IT';

      this.recognition.onresult = (event: any) => {
        const text = event.results[0][0].transcript;
        this.searchTranscript.set(text);
      };

      this.recognition.onend = () => {
        this.isSearching.set(false);
      };

      this.recognition.onerror = (err: any) => {
        console.error('Search recognition error:', err);
        this.isSearching.set(false);
      };
    }
  }

  startSearchRecognition() {
    if (!this.recognition) {
      alert('Il riconoscimento vocale non è supportato in questo browser.');
      return;
    }
    this.searchTranscript.set('');
    this.isSearching.set(true);
    try {
      this.recognition.start();
    } catch (e) {
      console.warn('Recognition already started', e);
    }
  }

  stopSearchRecognition() {
    this.recognition?.stop();
    this.isSearching.set(false);
  }

  async startListening() {
    if (this.isListening()) return;

    try {
      this.stream = await navigator.mediaDevices.getUserMedia({ 
        audio: {
          echoCancellation: true,
          noiseSuppression: true,
          autoGainControl: false // Impedisce il boost automatico del rumore di fondo nel silenzio
        } 
      });
      this.audioContext = new AudioContext();
      const source = this.audioContext.createMediaStreamSource(this.stream);
      
      // Catena di filtri in cascata per isolamento vocale
      this.highpassFilter = this.audioContext.createBiquadFilter();
      this.highpassFilter.type = 'highpass';
      this.highpassFilter.frequency.value = 150; // Taglia i bassi e rombi di fondo

      this.lowpassFilter = this.audioContext.createBiquadFilter();
      this.lowpassFilter.type = 'lowpass';
      this.lowpassFilter.frequency.value = 2000; // Taglia sibili e armonici acuti di chitarre

      this.vocalBandpass = this.audioContext.createBiquadFilter();
      this.vocalBandpass.type = 'bandpass';
      this.vocalBandpass.frequency.value = 800; // Centro della gamma vocale primaria
      this.vocalBandpass.Q.value = 1.2;         // Filtro stretto focalizzato sulla voce
      
      this.analyser = this.audioContext.createAnalyser();
      this.analyser.fftSize = 512;
      
      // Connessione in cascata: source -> highpass -> lowpass -> bandpass -> analyser
      source.connect(this.highpassFilter);
      this.highpassFilter.connect(this.lowpassFilter);
      this.lowpassFilter.connect(this.vocalBandpass);
      this.vocalBandpass.connect(this.analyser);
      
      this.isListening.set(true);
      this.clearRecentWords();
      this.startBackgroundRecognition();
      this.processAudio();
    } catch (err) {
      console.error('Error accessing microphone', err);
      alert('Errore microfono: assicurati di usare HTTPS e di aver dato i permessi.');
    }
  }

  stopListening() {
    if (this.animationFrame) cancelAnimationFrame(this.animationFrame);
    this.stream?.getTracks().forEach(track => track.stop());
    this.audioContext?.close();
    this.isListening.set(false);
    this.energyLevel.set(0);
    this.stopBackgroundRecognition();
    this.clearRecentWords();
    this.highpassFilter = null;
    this.lowpassFilter = null;
    this.vocalBandpass = null;
    this.speechRecognitionActive.set(false);
  }

  private processAudio() {
    if (!this.analyser) return;

    const bufferLength = this.analyser.frequencyBinCount;
    const dataArray = new Uint8Array(bufferLength);
    
    // Con la catena di filtri in cascata, l'energia vocale è centrata tra 200 Hz e 2000 Hz.
    const sampleRate = this.audioContext ? this.audioContext.sampleRate : 44100;
    const binWidth = sampleRate / 512;
    const startBin = Math.max(0, Math.floor(200 / binWidth));
    const endBin = Math.min(bufferLength - 1, Math.floor(2000 / binWidth));
    
    const analyze = () => {
      if (!this.analyser) return;
      this.analyser.getByteFrequencyData(dataArray);
      
      // Calcola l'energia media ESCLUSIVAMENTE nella gamma di frequenza della voce
      let sum = 0;
      for (let i = startBin; i <= endBin; i++) {
        sum += dataArray[i];
      }
      const avgEnergy = sum / (endBin - startBin + 1);
      this.energyLevel.set(avgEnergy);

      const now = Date.now();
      
      // Tracciamento adattivo del rumore di fondo
      if (avgEnergy < this.noiseFloor) {
        this.noiseFloor = this.noiseFloor * 0.95 + avgEnergy * 0.05;
      } else {
        this.noiseFloor = this.noiseFloor * 0.998 + avgEnergy * 0.002;
      }

      // Soglia di sensibilità definita dall'utente
      const userThreshold = 220 - (this.sensitivity() * 2.0); 
      // La soglia effettiva si adatta al rumore di fondo per evitare falsi avanzamenti
      const currentThreshold = Math.max(userThreshold, this.noiseFloor + 12);

      if (avgEnergy > currentThreshold) {
        if (avgEnergy > this.peakEnergy) {
          this.peakEnergy = avgEnergy;
        }

        if (!this.isSpeaking) {
          this.isSpeaking = true;
          this.peakEnergy = avgEnergy; 
          this.lastSilenceTime = now;
        }

        // Rilevamento della caduta di energia relativa per identificare stacchi tra sillabe/parole (legato acustico)
        const dropRatio = (this.peakEnergy - avgEnergy) / this.peakEnergy;
        if (this.isSpeaking && dropRatio > 0.20 && this.peakEnergy > currentThreshold * 1.1) {
          if (now - this.lastWordTime > this.COOLDOWN) {
            this.linesDetected.update(v => v + 1);
            this.lastWordTime = now;
            this.peakEnergy = avgEnergy; // Reset peak
            this.isSpeaking = false; 
            console.log('Line advanced - relative drop detected (highly sensitive)', { dropRatio, avgEnergy, peak: this.peakEnergy });
          }
        }
        
        this.lastSilenceTime = now;
      } else {
        // Sotto soglia (silenzio)
        if (this.isSpeaking && (now - this.lastSilenceTime > 180)) {
          if (now - this.lastWordTime > this.COOLDOWN) {
            this.linesDetected.update(v => v + 1);
            this.lastWordTime = now;
            console.log('Line advanced - silence detected');
          }
          this.isSpeaking = false;
          this.peakEnergy = 0;
        }
      }

      this.animationFrame = requestAnimationFrame(analyze);
    };

    analyze();
  }

  resetWordCount() {
    this.wordsDetected.set(0);
    this.linesDetected.set(0);
    this.lineStarted = false;
  }
}
