// Synthesized ambient audio — no external files.
// A low hum (two detuned sine oscillators + slow LFO on filter) and click ticks
// (band-passed noise impulses). All gated by a master gain the UI controls.

function useAudio(enabled) {
  const ctxRef = React.useRef(null);
  const masterRef = React.useRef(null);
  const humStopRef = React.useRef(null);
  const trackElRef = React.useRef(null);
  const trackNodeRef = React.useRef(null);
  const trackGainRef = React.useRef(null);

  // Lazy-init AudioContext on first enable (browsers require user gesture).
  const ensure = React.useCallback(() => {
    if (ctxRef.current) return ctxRef.current;
    const Ctx = window.AudioContext || window.webkitAudioContext;
    if (!Ctx) return null;
    const ctx = new Ctx();
    const master = ctx.createGain();
    master.gain.value = 0;
    master.connect(ctx.destination);
    ctxRef.current = ctx;
    masterRef.current = master;
    return ctx;
  }, []);

  // Background music — Scott Buckley "Nightfall". Looped through a low-gain
  // node so the synth hum + ticks + stings still sit on top of it.
  const startTrack = React.useCallback(() => {
    const ctx = ensure();
    if (!ctx || trackNodeRef.current) return;
    let el = trackElRef.current;
    if (!el) {
      el = new Audio('assets/nightfall.mp3');
      el.loop = true;
      el.crossOrigin = 'anonymous';
      el.preload = 'auto';
      trackElRef.current = el;
    }
    const node = ctx.createMediaElementSource(el);
    const g = ctx.createGain();
    g.gain.value = 0.34; // closer balance — SFX still cut through
    node.connect(g).connect(masterRef.current);
    trackNodeRef.current = node;
    trackGainRef.current = g;
    el.play().catch(() => { /* autoplay blocked until first gesture */ });
  }, [ensure]);

  const stopTrack = React.useCallback(() => {
    const el = trackElRef.current;
    if (el) { try { el.pause(); } catch(e) {} }
  }, []);

  const startHum = React.useCallback(() => {
    const ctx = ensure();
    if (!ctx || humStopRef.current) return;
    // Two slightly detuned low sines + a sub.
    const o1 = ctx.createOscillator(); o1.type = 'sine'; o1.frequency.value = 55;
    const o2 = ctx.createOscillator(); o2.type = 'sine'; o2.frequency.value = 55.4;
    const o3 = ctx.createOscillator(); o3.type = 'sine'; o3.frequency.value = 32.7;
    const filt = ctx.createBiquadFilter();
    filt.type = 'lowpass'; filt.frequency.value = 220; filt.Q.value = 0.7;
    // LFO on filter cutoff for slow breathing.
    const lfo = ctx.createOscillator(); lfo.type = 'sine'; lfo.frequency.value = 0.07;
    const lfoGain = ctx.createGain(); lfoGain.gain.value = 80;
    lfo.connect(lfoGain).connect(filt.frequency);
    const g = ctx.createGain(); g.gain.value = 0.18;
    o1.connect(filt); o2.connect(filt); o3.connect(g);
    filt.connect(g);
    g.connect(masterRef.current);
    o1.start(); o2.start(); o3.start(); lfo.start();
    humStopRef.current = () => {
      try { o1.stop(); o2.stop(); o3.stop(); lfo.stop(); } catch(e) {}
      try { g.disconnect(); filt.disconnect(); } catch(e) {}
    };
  }, [ensure]);

  const stopHum = React.useCallback(() => {
    if (humStopRef.current) { humStopRef.current(); humStopRef.current = null; }
  }, []);

  const tick = React.useCallback((variant = 'tick') => {
    const ctx = ensure();
    if (!ctx || !masterRef.current) return;
    if (ctx.state === 'suspended') ctx.resume();
    // Browsers block autoplay until a user gesture; ride the first click.
    const tEl = trackElRef.current;
    if (tEl && tEl.paused) { tEl.play().catch(() => {}); }
    if (masterRef.current.gain.value < 0.001) return; // muted
    const now = ctx.currentTime;
    // Short noise burst through bandpass.
    const buf = ctx.createBuffer(1, 1024, ctx.sampleRate);
    const d = buf.getChannelData(0);
    for (let i = 0; i < d.length; i++) d[i] = (Math.random() * 2 - 1) * Math.exp(-i / 80);
    const src = ctx.createBufferSource(); src.buffer = buf;
    const bp = ctx.createBiquadFilter();
    bp.type = 'bandpass';
    bp.frequency.value = variant === 'thud' ? 180 : variant === 'whisper' ? 2200 : 1100;
    bp.Q.value = variant === 'whisper' ? 0.6 : 4;
    const g = ctx.createGain();
    g.gain.setValueAtTime(variant === 'thud' ? 0.5 : 0.22, now);
    g.gain.exponentialRampToValueAtTime(0.001, now + (variant === 'thud' ? 0.45 : 0.12));
    src.connect(bp).connect(g).connect(masterRef.current);
    src.start(now);
    src.stop(now + 0.5);
  }, [ensure]);

  // Wind gust — used when entering a new room/poem. Brown noise through a
  // breathy bandpass that sweeps slowly, with a softer high-frequency hiss
  // riding on top. ~3s, swells then fades.
  const sting = React.useCallback(() => {
    const ctx = ensure();
    if (!ctx || !masterRef.current) return;
    if (masterRef.current.gain.value < 0.001) return;
    const now = ctx.currentTime;
    const dur = 1.0;

    // Brown-noise buffer (low-frequency-rich), 1s.
    const buf = ctx.createBuffer(1, Math.floor(ctx.sampleRate * dur), ctx.sampleRate);
    const d = buf.getChannelData(0);
    let last = 0;
    for (let i = 0; i < d.length; i++) {
      const w = Math.random() * 2 - 1;
      last = (last + 0.02 * w) / 1.02;
      d[i] = last * 3.5;
    }

    // Body: brown noise through a fast-sweeping bandpass — bold whoosh.
    const body = ctx.createBufferSource(); body.buffer = buf;
    const bp = ctx.createBiquadFilter();
    bp.type = 'bandpass';
    bp.frequency.setValueAtTime(180, now);
    bp.frequency.exponentialRampToValueAtTime(900, now + dur * 0.45);
    bp.frequency.exponentialRampToValueAtTime(220, now + dur);
    bp.Q.value = 1.2;
    const bodyG = ctx.createGain();
    bodyG.gain.setValueAtTime(0.0001, now);
    bodyG.gain.linearRampToValueAtTime(0.85, now + dur * 0.35);
    bodyG.gain.exponentialRampToValueAtTime(0.0001, now + dur);
    body.connect(bp).connect(bodyG).connect(masterRef.current);
    body.start(now); body.stop(now + dur + 0.05);

    // Hiss: same noise, highpassed — punchier air-past-the-ear texture.
    const hiss = ctx.createBufferSource(); hiss.buffer = buf;
    const hp = ctx.createBiquadFilter();
    hp.type = 'highpass';
    hp.frequency.setValueAtTime(2000, now);
    hp.frequency.linearRampToValueAtTime(3600, now + dur * 0.4);
    hp.frequency.linearRampToValueAtTime(1800, now + dur);
    const hissG = ctx.createGain();
    hissG.gain.setValueAtTime(0.0001, now);
    hissG.gain.linearRampToValueAtTime(0.22, now + dur * 0.4);
    hissG.gain.exponentialRampToValueAtTime(0.0001, now + dur);
    hiss.connect(hp).connect(hissG).connect(masterRef.current);
    hiss.start(now); hiss.stop(now + dur + 0.05);
  }, [ensure]);

  // Master fade follows `enabled`.
  React.useEffect(() => {
    const ctx = ensure();
    if (!ctx || !masterRef.current) return;
    if (ctx.state === 'suspended' && enabled) ctx.resume();
    const target = enabled ? 0.6 : 0;
    masterRef.current.gain.cancelScheduledValues(ctx.currentTime);
    masterRef.current.gain.linearRampToValueAtTime(target, ctx.currentTime + 0.8);
    if (enabled) { startHum(); startTrack(); } else { stopTrack(); }
    // Don't kill oscillators on disable — the gain ramp silences them, and
    // restarting them every toggle creates audible pops.
  }, [enabled, ensure, startHum]);

  return { tick, sting };
}

window.useAudio = useAudio;
