// Auto-recovery for transient network failures during playback.
// Listens for stalled / waiting / network-error events and forces a
// reload-from-current-position when the audio element doesn't recover
// on its own within `delayMs`. Bounded retry count prevents tight
// loops on persistent failures (the existing onerror handler still
// reports to the store after retries exhaust).
//
// Returns a teardown function — call from $effect cleanup.

export type StallRetryOptions = {
  maxRetries?: number;
  delayMs?: number;
};

export function attachStallRetry(
  el: HTMLAudioElement,
  opts: StallRetryOptions = {}
): () => void {
  const maxRetries = opts.maxRetries ?? 3;
  const delayMs = opts.delayMs ?? 3000;

  let timer: ReturnType<typeof setTimeout> | null = null;
  let retries = 0;

  function clear() {
    if (timer) {
      clearTimeout(timer);
      timer = null;
    }
  }

  function attemptRecovery() {
    if (retries >= maxRetries) return;
    retries++;
    const lastPos = el.currentTime;
    const wasPlaying = !el.paused;
    el.load();
    el.currentTime = lastPos;
    if (wasPlaying) el.play().catch(() => { /* swallowed; store sees the event */ });
  }

  function onStallish() {
    if (timer) return;
    timer = setTimeout(() => {
      timer = null;
      // readyState >= 3 (HAVE_FUTURE_DATA) means the buffer recovered.
      if (el.readyState >= 3) return;
      attemptRecovery();
    }, delayMs);
  }

  function onError() {
    // MediaError.MEDIA_ERR_NETWORK === 2 → transient; worth retrying.
    // Other codes (DECODE, SRC_NOT_SUPPORTED) are deterministic; skip.
    if (!el.error) return;
    if (el.error.code !== 2) return;
    attemptRecovery();
  }

  function onRecovered() {
    retries = 0;
    clear();
  }

  el.addEventListener('stalled', onStallish);
  el.addEventListener('waiting', onStallish);
  el.addEventListener('error', onError);
  el.addEventListener('canplay', onRecovered);
  el.addEventListener('playing', onRecovered);

  return () => {
    el.removeEventListener('stalled', onStallish);
    el.removeEventListener('waiting', onStallish);
    el.removeEventListener('error', onError);
    el.removeEventListener('canplay', onRecovered);
    el.removeEventListener('playing', onRecovered);
    clear();
  };
}
