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

@Injectable({
  providedIn: 'root'
})
export class SortingAudioService {
  private audioContext: AudioContext | null = null;

  private ensureContext(): AudioContext {
    this.audioContext ??= new AudioContext();
    if (this.audioContext.state === 'suspended') {
      this.audioContext.resume();
    }
    return this.audioContext;
  }

  // Call this on the user gesture (button click) so the AudioContext can be created/resumed.
  initOnUserGesture(): void {
    this.ensureContext();
  }

  playTone(value: number, maxValue: number, animationSpeedMs: number): void {
    const ctx = this.ensureContext();
    const frequency = this.valueToFrequency(value, maxValue);

    // Keep tone duration slightly shorter than the animation frame to avoid overlap
    const duration = Math.min(0.1, (animationSpeedMs * 0.75) / 1000);

    const oscillator = ctx.createOscillator();
    const gainNode = ctx.createGain();

    oscillator.connect(gainNode);
    gainNode.connect(ctx.destination);

    oscillator.type = 'sawtooth';
    oscillator.frequency.setValueAtTime(frequency, ctx.currentTime);

    gainNode.gain.setValueAtTime(0.15, ctx.currentTime);
    gainNode.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + duration);

    oscillator.start(ctx.currentTime);
    oscillator.stop(ctx.currentTime + duration);
  }

  playSortedSweep(sortedValues: number[], maxValue: number): void {
    const ctx = this.ensureContext();
    // Play a quick ascending sweep through all sorted bar values
    const step = Math.ceil(sortedValues.length / 40);
    sortedValues.forEach((value, i) => {
      if (i % step !== 0) {
        return;
      }
      const delayMs = (i / step) * 18;
      setTimeout(() => {
        const frequency = this.valueToFrequency(value, maxValue);
        const oscillator = ctx.createOscillator();
        const gainNode = ctx.createGain();

        oscillator.connect(gainNode);
        gainNode.connect(ctx.destination);

        oscillator.type = 'sine';
        oscillator.frequency.setValueAtTime(frequency, ctx.currentTime);

        gainNode.gain.setValueAtTime(0.15, ctx.currentTime);
        gainNode.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.06);

        oscillator.start(ctx.currentTime);
        oscillator.stop(ctx.currentTime + 0.06);
      }, delayMs);
    });
  }

  // Maps a bar value linearly to the frequency range 180–1100 Hz (roughly 3 octaves)
  private valueToFrequency(value: number, maxValue: number): number {
    const minFreq = 400;
    const maxFreq = 800;
    return minFreq + (value / maxValue) * (maxFreq - minFreq);
  }
}
