import { extendMicRelease, waitForMicRelease } from './micSession';
import { mapMicError } from './micCapture';

export class AudioAnalyser {
  private stream: MediaStream | null = null;
  private context: AudioContext | null = null;
  private analyser: AnalyserNode | null = null;
  private source: MediaStreamAudioSourceNode | null = null;

  get node() {
    return this.analyser;
  }

  async start() {
    if (this.analyser) return this.analyser;
    await waitForMicRelease();

    this.stream = await navigator.mediaDevices.getUserMedia({
      audio: {
        echoCancellation: true,
        noiseSuppression: true,
        autoGainControl: true,
      },
    });

    this.context = new AudioContext();
    this.analyser = this.context.createAnalyser();
    this.analyser.fftSize = 256;
    this.analyser.smoothingTimeConstant = 0.75;
    this.source = this.context.createMediaStreamSource(this.stream);
    this.source.connect(this.analyser);
    if (this.context.state === 'suspended') {
      await this.context.resume();
    }
    return this.analyser;
  }

  stop() {
    const disposePromise = this.dispose();
    extendMicRelease(disposePromise);
    void disposePromise;
  }

  private async dispose() {
    this.source?.disconnect();
    this.source = null;
    this.analyser = null;
    const context = this.context;
    this.context = null;
    if (context) {
      try {
        await context.close();
      } catch {
        // ignore
      }
    }
    this.stream?.getTracks().forEach((track) => track.stop());
    this.stream = null;
  }
}

export { mapMicError };
