/**
 * Recorder — AudioWorklet-based replacement for Recorderjs
 * https://github.com/mattdiamond/Recorderjs
 *
 * AudioWorkletNode заменяет устаревший ScriptProcessorNode.
 * WAV-кодирование остаётся в отдельном Worker (recorderWorker.js).
 */

export class Recorder {
  private node: AudioWorkletNode;
  private worker: Worker;
  private currCallback: ((blob: Blob) => void) | null = null;

  private constructor(node: AudioWorkletNode, worker: Worker) {
    this.node = node;
    this.worker = worker;

    // Пробрасываем PCM-чанки из воркл'ета в WAV-воркер
    node.port.onmessage = (e: MessageEvent<Float32Array[]>) => {
      this.worker.postMessage({ command: 'record', buffer: e.data });
    };

    this.worker.onmessage = (e: MessageEvent<{ command: string; data: Blob }>) => {
      if (e.data.command === 'exportWAV' && this.currCallback) {
        this.currCallback(e.data.data);
        this.currCallback = null;
      }
    };
  }

  /** Фабричный метод — async из-за audioWorklet.addModule */
  static async create(
    source: MediaStreamAudioSourceNode,
    cfg?: { numChannels?: number; workletPath?: string; workerPath?: string },
  ): Promise<Recorder> {
    const context = source.context as AudioContext;
    const numChannels = cfg?.numChannels ?? 1;

    await context.audioWorklet.addModule(cfg?.workletPath ?? '/recorderWorklet.js');

    const node = new AudioWorkletNode(context, 'recorder-processor', {
      numberOfInputs: 1,
      numberOfOutputs: 0,
      channelCount: numChannels,
    });

    const worker = new Worker(cfg?.workerPath ?? '/recorderWorker.js');
    worker.postMessage({
      command: 'init',
      config: { sampleRate: context.sampleRate, numChannels },
    });

    source.connect(node);

    return new Recorder(node, worker);
  }

  record() { /* данные уже идут через node.port.onmessage */ }

  stop() {
    // Сообщаем воркл'ету завершить process() и отключиться
    this.node.port.postMessage('stop');
  }

  clear() {
    this.worker.postMessage({ command: 'clear' });
  }

  exportWAV(cb: (blob: Blob) => void, type = 'audio/wav') {
    this.currCallback = cb;
    this.worker.postMessage({ command: 'exportWAV', type });
  }

  destroy() {
    this.node.disconnect();
    this.worker.terminate();
  }
}
