// System-audio loopback. Under Electron this is renderer-driven: we
// ask main for the primary screen's capturer id, then call
// getUserMedia with Chromium's `chromeMediaSource: 'desktop'`
// constraint to obtain the OS-mixer MediaStream directly. The whole
// Tauri WASAPI + AudioWorklet base64 pipeline is gone.
//
// Windows-only in practice (loopback audio is a Windows feature of
// Chromium's desktop source). On other platforms `startSystemAudioCapture`
// throws `SystemAudioUnavailable`; callers are expected to fall back
// to the standard getDisplayMedia flow.

import { isTauriRuntime } from './globalShortcut';

export interface SystemAudioHandle {
  /** Monotonic id, used by callers to correlate stop() with start. */
  captureId: number;
  /** MediaStream with a single audio track carrying the OS mixer. */
  stream: MediaStream;
  /** Teardown — stops the MediaStreamTrack. Idempotent. */
  stop: () => Promise<void>;
}

export class SystemAudioUnavailable extends Error {
  constructor(reason: string) {
    super('system audio unavailable: ' + reason);
    this.name = 'SystemAudioUnavailable';
  }
}

interface ChromiumAudioConstraint {
  mandatory: {
    chromeMediaSource: 'desktop';
    chromeMediaSourceId: string;
  };
}

export async function startSystemAudioCapture(): Promise<SystemAudioHandle> {
  if (!isTauriRuntime()) {
    throw new SystemAudioUnavailable('not an electron runtime');
  }
  if (typeof navigator === 'undefined' || !navigator.mediaDevices) {
    throw new SystemAudioUnavailable('mediaDevices unavailable');
  }

  const resolved = await window.electronAPI.resolveLoopbackSource();
  if (!resolved) {
    throw new SystemAudioUnavailable('no screen source available');
  }

  const audioConstraint: ChromiumAudioConstraint = {
    mandatory: {
      chromeMediaSource: 'desktop',
      chromeMediaSourceId: resolved.sourceId,
    },
  };

  let stream: MediaStream;
  try {
    // Cast: the Chromium `mandatory` constraint is non-standard and
    // not covered by lib.dom.d.ts typings.
    stream = await navigator.mediaDevices.getUserMedia({
      audio: audioConstraint as unknown as MediaTrackConstraints,
      video: false,
    });
  } catch (err: unknown) {
    throw new SystemAudioUnavailable(
      err instanceof Error ? err.message : String(err),
    );
  }

  const tracks = stream.getAudioTracks();
  if (tracks.length === 0) {
    for (const t of stream.getTracks()) t.stop();
    throw new SystemAudioUnavailable('no audio track in returned stream');
  }

  const captureId = Date.now();
  let stopped = false;
  const stop = async (): Promise<void> => {
    if (stopped) return;
    stopped = true;
    for (const track of stream.getTracks()) {
      try {
        track.stop();
      } catch {
        /* already stopped */
      }
    }
  };

  return { captureId, stream, stop };
}
