// RoomMic bridge — sortie audio vers BlackHole via ffmpeg (CoreAudio)
//
// Pourquoi ffmpeg et pas naudiodon : naudiodon 2.3.6 (2022) segfault dans
// getDevices() avec les runtimes modernes (Electron 43 / Node 26, ABI 148).
// ffmpeg est un binaire externe maintenu — zéro module natif à compiler,
// zéro rebuild ABI. On lui envoie le PCM (Float32 stéréo) via stdin.
const { spawn } = require('child_process');
const fs = require('fs');

const DEFAULT_DEVICE = 'BlackHole 2ch';
let ff = null;

// ffmpeg peut être hors PATH quand Electron est lancé hors du shell
// (Finder/Dock) → on préfère les chemins absolus Homebrew, sinon le PATH.
function resolveFfmpeg() {
  for (const c of ['/opt/homebrew/bin/ffmpeg', '/usr/local/bin/ffmpeg']) {
    try { if (fs.existsSync(c)) return c; } catch {}
  }
  return 'ffmpeg';
}

// Liste les devices CoreAudio contenant "BlackHole" via ffmpeg.
function listBlackHoleDevices() {
  return new Promise((resolve) => {
    let proc;
    try {
      proc = spawn(resolveFfmpeg(), ['-hide_banner', '-f', 'coreaudio', '-list_devices', 'true', '-i', '']);
    } catch {
      resolve([]);
      return;
    }
    let out = '';
    proc.stderr.on('data', (d) => { out += d; });
    proc.on('error', (e) => console.error('[audio] ffmpeg scan:', e.message));
    proc.on('close', () => {
      const names = [...out.matchAll(/\[\d+\]\s*(.*)/g)]
        .map((m) => m[1].trim())
        .filter((n) => /blackhole/i.test(n));
      resolve([...new Set(names)]);
    });
    // filet de sécurité : ffmpeg peut rester bloqué sur -list_devices
    setTimeout(() => {
      try { proc.kill(); } catch {}
      resolve([DEFAULT_DEVICE]); // valeur par défaut si le scan échoue
    }, 3000);
  });
}

// Ouvre un flux ffmpeg qui écrit dans le device CoreAudio ciblé.
function openOutput(deviceName, sampleRate) {
  close();
  const name = deviceName || DEFAULT_DEVICE;
  ff = spawn(resolveFfmpeg(), [
    '-hide_banner', '-loglevel', 'error',
    '-f', 'f32le', '-ar', String(sampleRate || 48000), '-ac', '2',
    '-i', 'pipe:0',
    '-f', 'coreaudio', name,
  ]);
  ff.on('error', (e) => console.error('[audio] ffmpeg:', e.message));
  ff.stderr.on('data', (d) => {
    const s = String(d).trim();
    if (s) console.error('[audio]', s);
  });
  return ff;
}

// mono Float32Array → stéréo, écrit dans le stdin de ffmpeg.
function writeChunk(mono) {
  if (!ff || !ff.stdin || ff.stdin.destroyed) return;
  const stereo = new Float32Array(mono.length * 2);
  for (let i = 0; i < mono.length; i++) {
    stereo[i * 2] = mono[i];
    stereo[i * 2 + 1] = mono[i];
  }
  ff.stdin.write(Buffer.from(stereo.buffer));
}

function close() {
  if (ff) {
    try { ff.stdin.end(); } catch {}
    try { ff.kill(); } catch {}
    ff = null;
  }
}

module.exports = { listBlackHoleDevices, openOutput, writeChunk, close, DEFAULT_DEVICE };
