/* global React */
// =========================================================================
// AMBIENCE — scene picker + procedural WebAudio for all 8 scenes
// The selected scene becomes the desktop wallpaper (see app.jsx).
// =========================================================================

const { useState: useAmState, useEffect: useAmEffect } = React;

// ─── Shared audio context ────────────────────────────────────────────────
window.__somnia_audio = window.__somnia_audio || { ctx: null, master: null, currentStop: null, masterVol: 0.2 };

function ensureCtx() {
  const A = window.__somnia_audio;
  if (!A.ctx) {
    A.ctx = new (window.AudioContext || window.webkitAudioContext)();
    A.master = A.ctx.createGain();
    A.master.gain.value = A.masterVol;
    A.master.connect(A.ctx.destination);
  }
  return A;
}

/** Call synchronously from a click/tap handler — browsers require a user gesture to start audio. */
function unlockAudio() {
  const A = ensureCtx();
  if (A.ctx.state === 'suspended') void A.ctx.resume();
  return A;
}

async function resumeCtx() {
  const A = ensureCtx();
  if (A.ctx.state === 'suspended') await A.ctx.resume();
  return A;
}

function setMasterVolume(v) {
  const A = window.__somnia_audio;
  A.masterVol = v;
  if (A.master && A.ctx) {
    A.master.gain.cancelScheduledValues(A.ctx.currentTime);
    A.master.gain.linearRampToValueAtTime(v, A.ctx.currentTime + 0.05);
  }
}

function makeNoise(ctx, sec = 4) {
  const buf = ctx.createBuffer(1, ctx.sampleRate * sec, ctx.sampleRate);
  const d = buf.getChannelData(0);
  for (let i = 0; i < d.length; i++) d[i] = Math.random() * 2 - 1;
  return buf;
}

function noiseSrc(ctx) {
  const n = ctx.createBufferSource();
  n.buffer = makeNoise(ctx, 4);
  n.loop = true;
  return n;
}

// ─── Engines (one per scene) ─────────────────────────────────────────────
const ENGINES = {
  // Faint low drone + occasional crickets
  night: (ctx, out) => {
    const osc = ctx.createOscillator();
    osc.type = 'sine';
    osc.frequency.value = 55;
    const g = ctx.createGain();
    g.gain.value = 0.15;
    osc.connect(g); g.connect(out);
    osc.start();
    // High soft hiss
    const noise = noiseSrc(ctx);
    const hp = ctx.createBiquadFilter();
    hp.type = 'highpass'; hp.frequency.value = 6000;
    const ng = ctx.createGain(); ng.gain.value = 0.04;
    noise.connect(hp); hp.connect(ng); ng.connect(out);
    noise.start();
    // crickets
    const timers = [];
    const cricket = () => {
      const c = ctx.createOscillator();
      const cg = ctx.createGain();
      c.frequency.value = 4200 + Math.random() * 800;
      c.type = 'sine';
      const t = ctx.currentTime;
      for (let i = 0; i < 4; i++) {
        cg.gain.setValueAtTime(0, t + i * 0.08);
        cg.gain.linearRampToValueAtTime(0.08, t + i * 0.08 + 0.005);
        cg.gain.exponentialRampToValueAtTime(0.001, t + i * 0.08 + 0.04);
      }
      c.connect(cg); cg.connect(out);
      c.start(); c.stop(t + 0.5);
      timers.push(setTimeout(cricket, 3000 + Math.random() * 6000));
    };
    timers.push(setTimeout(cricket, 1500));
    return () => { try { osc.stop(); noise.stop(); } catch(e){} timers.forEach(clearTimeout); };
  },

  // Soft wind, gentle bird coda for dusk
  sunset: (ctx, out) => {
    const noise = noiseSrc(ctx);
    const lp = ctx.createBiquadFilter();
    lp.type = 'lowpass'; lp.frequency.value = 700;
    const ng = ctx.createGain(); ng.gain.value = 0.18;
    const lfo = ctx.createOscillator(); lfo.frequency.value = 0.2;
    const lfoAmp = ctx.createGain(); lfoAmp.gain.value = 0.08;
    lfo.connect(lfoAmp); lfoAmp.connect(ng.gain);
    noise.connect(lp); lp.connect(ng); ng.connect(out);
    noise.start(); lfo.start();
    // distant bird coda
    const timers = [];
    const coda = () => {
      const notes = [800, 1000, 850, 900];
      const t0 = ctx.currentTime;
      notes.forEach((f, i) => {
        const o = ctx.createOscillator();
        const og = ctx.createGain();
        o.type = 'sine';
        o.frequency.setValueAtTime(f, t0 + i * 0.18);
        og.gain.setValueAtTime(0, t0 + i * 0.18);
        og.gain.linearRampToValueAtTime(0.06, t0 + i * 0.18 + 0.02);
        og.gain.exponentialRampToValueAtTime(0.001, t0 + i * 0.18 + 0.16);
        o.connect(og); og.connect(out);
        o.start(t0 + i * 0.18); o.stop(t0 + i * 0.18 + 0.2);
      });
      timers.push(setTimeout(coda, 8000 + Math.random() * 10000));
    };
    timers.push(setTimeout(coda, 3000));
    return () => { try { noise.stop(); lfo.stop(); } catch(e){} timers.forEach(clearTimeout); };
  },

  // Wind + frequent chirps + faint leaf rustle
  forest: (ctx, out) => {
    const noise = noiseSrc(ctx);
    const lp = ctx.createBiquadFilter();
    lp.type = 'lowpass'; lp.frequency.value = 900;
    const ng = ctx.createGain(); ng.gain.value = 0.22;
    noise.connect(lp); lp.connect(ng); ng.connect(out);
    noise.start();
    // high crinkle (leaves)
    const noise2 = noiseSrc(ctx);
    const hp = ctx.createBiquadFilter();
    hp.type = 'highpass'; hp.frequency.value = 4000;
    const ng2 = ctx.createGain(); ng2.gain.value = 0.04;
    noise2.connect(hp); hp.connect(ng2); ng2.connect(out);
    noise2.start();
    const timers = [];
    const chirp = () => {
      const o = ctx.createOscillator();
      const og = ctx.createGain();
      const base = 1600 + Math.random() * 2400;
      const t = ctx.currentTime;
      o.type = 'sine';
      o.frequency.setValueAtTime(base, t);
      o.frequency.linearRampToValueAtTime(base + 600, t + 0.04);
      o.frequency.linearRampToValueAtTime(base - 300, t + 0.12);
      og.gain.setValueAtTime(0, t);
      og.gain.linearRampToValueAtTime(0.1, t + 0.02);
      og.gain.exponentialRampToValueAtTime(0.001, t + 0.18);
      o.connect(og); og.connect(out);
      o.start(); o.stop(t + 0.22);
      timers.push(setTimeout(chirp, 1200 + Math.random() * 3500));
    };
    timers.push(setTimeout(chirp, 800));
    return () => { try { noise.stop(); noise2.stop(); } catch(e){} timers.forEach(clearTimeout); };
  },

  // Quiet very-low-frequency pad + occasional wind gust
  snow: (ctx, out) => {
    const noise = noiseSrc(ctx);
    const lp = ctx.createBiquadFilter();
    lp.type = 'lowpass'; lp.frequency.value = 400;
    const ng = ctx.createGain(); ng.gain.value = 0.16;
    noise.connect(lp); lp.connect(ng); ng.connect(out);
    noise.start();
    // very-low pad
    const o1 = ctx.createOscillator(); o1.type = 'sine'; o1.frequency.value = 110;
    const o2 = ctx.createOscillator(); o2.type = 'sine'; o2.frequency.value = 165;
    const og = ctx.createGain(); og.gain.value = 0.04;
    o1.connect(og); o2.connect(og); og.connect(out);
    o1.start(); o2.start();
    // gusts via LFO on gain
    const lfo = ctx.createOscillator(); lfo.frequency.value = 0.07;
    const lfoAmp = ctx.createGain(); lfoAmp.gain.value = 0.1;
    lfo.connect(lfoAmp); lfoAmp.connect(ng.gain);
    lfo.start();
    return () => { try { noise.stop(); o1.stop(); o2.stop(); lfo.stop(); } catch(e){} };
  },

  // Bandpass noise (hiss) + crackles + low fire rumble
  fire: (ctx, out) => {
    const noise = noiseSrc(ctx);
    const bp = ctx.createBiquadFilter();
    bp.type = 'bandpass'; bp.frequency.value = 380; bp.Q.value = 0.6;
    const ng = ctx.createGain(); ng.gain.value = 0.3;
    noise.connect(bp); bp.connect(ng); ng.connect(out);
    noise.start();
    // low rumble
    const rumble = ctx.createOscillator(); rumble.type = 'sine'; rumble.frequency.value = 70;
    const rg = ctx.createGain(); rg.gain.value = 0.06;
    rumble.connect(rg); rg.connect(out);
    rumble.start();
    const timers = [];
    const crackle = () => {
      const o = ctx.createOscillator();
      const og = ctx.createGain();
      o.frequency.value = 180 + Math.random() * 700;
      const t = ctx.currentTime;
      og.gain.setValueAtTime(0, t);
      og.gain.linearRampToValueAtTime(0.12 + Math.random() * 0.12, t + 0.004);
      og.gain.exponentialRampToValueAtTime(0.001, t + 0.06 + Math.random() * 0.05);
      o.connect(og); og.connect(out);
      o.start(); o.stop(t + 0.12);
      timers.push(setTimeout(crackle, 40 + Math.random() * 400));
    };
    crackle();
    return () => { try { noise.stop(); rumble.stop(); } catch(e){} timers.forEach(clearTimeout); };
  },

  // Light rain on a window + low café hum
  coffee: (ctx, out) => {
    // rain (filtered, soft)
    const noise = noiseSrc(ctx);
    const bp = ctx.createBiquadFilter();
    bp.type = 'bandpass'; bp.frequency.value = 2000; bp.Q.value = 0.5;
    const ng = ctx.createGain(); ng.gain.value = 0.22;
    noise.connect(bp); bp.connect(ng); ng.connect(out);
    noise.start();
    // low café hum
    const hum = ctx.createOscillator(); hum.type = 'sawtooth'; hum.frequency.value = 90;
    const humLp = ctx.createBiquadFilter(); humLp.type = 'lowpass'; humLp.frequency.value = 200;
    const humG = ctx.createGain(); humG.gain.value = 0.04;
    hum.connect(humLp); humLp.connect(humG); humG.connect(out);
    hum.start();
    // occasional cup clink
    const timers = [];
    const clink = () => {
      const o = ctx.createOscillator();
      const og = ctx.createGain();
      o.frequency.value = 2200 + Math.random() * 1200;
      o.type = 'triangle';
      const t = ctx.currentTime;
      og.gain.setValueAtTime(0.0, t);
      og.gain.linearRampToValueAtTime(0.05, t + 0.005);
      og.gain.exponentialRampToValueAtTime(0.001, t + 0.4);
      o.connect(og); og.connect(out);
      o.start(); o.stop(t + 0.5);
      timers.push(setTimeout(clink, 12000 + Math.random() * 18000));
    };
    timers.push(setTimeout(clink, 4000));
    return () => { try { noise.stop(); hum.stop(); } catch(e){} timers.forEach(clearTimeout); };
  },

  // Heavy rain + distant thunder rumble
  rain: (ctx, out) => {
    const noise = noiseSrc(ctx);
    const hp = ctx.createBiquadFilter();
    hp.type = 'highpass'; hp.frequency.value = 800;
    const lp = ctx.createBiquadFilter();
    lp.type = 'lowpass'; lp.frequency.value = 6000;
    const ng = ctx.createGain(); ng.gain.value = 0.4;
    noise.connect(hp); hp.connect(lp); lp.connect(ng); ng.connect(out);
    noise.start();
    // distant thunder rumble
    const timers = [];
    const thunder = () => {
      const t = ctx.currentTime;
      const r = ctx.createOscillator(); r.type = 'sine'; r.frequency.value = 50;
      const rg = ctx.createGain();
      rg.gain.setValueAtTime(0, t);
      rg.gain.linearRampToValueAtTime(0.4, t + 0.6);
      rg.gain.exponentialRampToValueAtTime(0.001, t + 3);
      const rn = noiseSrc(ctx);
      const rlp = ctx.createBiquadFilter(); rlp.type = 'lowpass'; rlp.frequency.value = 120;
      const rng = ctx.createGain();
      rng.gain.setValueAtTime(0, t);
      rng.gain.linearRampToValueAtTime(0.3, t + 0.4);
      rng.gain.exponentialRampToValueAtTime(0.001, t + 2.5);
      r.connect(rg); rg.connect(out);
      rn.connect(rlp); rlp.connect(rng); rng.connect(out);
      r.start(t); r.stop(t + 3.2);
      rn.start(t); rn.stop(t + 2.7);
      timers.push(setTimeout(thunder, 14000 + Math.random() * 14000));
    };
    timers.push(setTimeout(thunder, 6000));
    return () => { try { noise.stop(); } catch(e){} timers.forEach(clearTimeout); };
  },

  // Detuned drone + sweep tones for cosmic feel
  space: (ctx, out) => {
    const make = (freq, gain) => {
      const o = ctx.createOscillator();
      o.type = 'sine';
      o.frequency.value = freq;
      const g = ctx.createGain(); g.gain.value = gain;
      o.connect(g); g.connect(out);
      o.start();
      return o;
    };
    const o1 = make(82, 0.07);
    const o2 = make(83.2, 0.05);    // detune for shimmer
    const o3 = make(165, 0.04);
    const o4 = make(247, 0.03);
    // slow LFO on filter for movement
    const noise = noiseSrc(ctx);
    const bp = ctx.createBiquadFilter();
    bp.type = 'bandpass'; bp.frequency.value = 1200; bp.Q.value = 8;
    const ng = ctx.createGain(); ng.gain.value = 0.1;
    const lfo = ctx.createOscillator(); lfo.frequency.value = 0.05;
    const lfoAmp = ctx.createGain(); lfoAmp.gain.value = 700;
    lfo.connect(lfoAmp); lfoAmp.connect(bp.frequency);
    noise.connect(bp); bp.connect(ng); ng.connect(out);
    noise.start(); lfo.start();
    // occasional radio beep
    const timers = [];
    const beep = () => {
      const o = ctx.createOscillator();
      const og = ctx.createGain();
      o.frequency.value = 1400 + Math.random() * 800;
      o.type = 'square';
      const t = ctx.currentTime;
      og.gain.setValueAtTime(0, t);
      og.gain.linearRampToValueAtTime(0.04, t + 0.01);
      og.gain.exponentialRampToValueAtTime(0.001, t + 0.25);
      o.connect(og); og.connect(out);
      o.start(); o.stop(t + 0.3);
      timers.push(setTimeout(beep, 8000 + Math.random() * 12000));
    };
    timers.push(setTimeout(beep, 4500));
    return () => { try { o1.stop(); o2.stop(); o3.stop(); o4.stop(); noise.stop(); lfo.stop(); } catch(e){} timers.forEach(clearTimeout); };
  },
};

// ─── Scene metadata ──────────────────────────────────────────────────────
const SCENES = [
  { id: 'night',  label: 'Quiet Night',  desc: 'Crickets, distant hum. The default desktop.', engine: 'night' },
  { id: 'sunset', label: 'Sunset Peaks', desc: 'Soft wind, birds calling at dusk.',          engine: 'sunset' },
  { id: 'forest', label: 'Pine Forest',  desc: 'Breeze through needles + birdsong.',         engine: 'forest' },
  { id: 'snow',   label: 'Snowfall',     desc: 'Hushed wind over white peaks.',              engine: 'snow' },
  { id: 'fire',   label: 'Cabin Fire',   desc: 'Crackling logs, low rumble, warm windows.',  engine: 'fire' },
  { id: 'coffee', label: 'Morning Cup',  desc: 'Rain on the window, distant café hum.',      engine: 'coffee' },
  { id: 'rain',   label: 'Storm',        desc: 'Steady rain with distant thunder.',          engine: 'rain' },
  { id: 'space',  label: 'Deep Space',   desc: 'Detuned drone + cosmic radio.',              engine: 'space' },
];

// ─── Audio controller (called from app.jsx) ──────────────────────────────
function playSceneAudio(sceneId) {
  const A = window.__somnia_audio;
  A._playGen = (A._playGen || 0) + 1;
  if (A.currentStop) { A.currentStop(); A.currentStop = null; }
  if (!sceneId || !ENGINES[sceneId]) return;

  const playId = A._playGen;
  void resumeCtx().then((audio) => {
    if (playId !== A._playGen) return;
    const ctx = audio.ctx;
    const sceneGain = ctx.createGain();
    sceneGain.gain.value = 0;
    sceneGain.connect(A.master);
    sceneGain.gain.linearRampToValueAtTime(1, ctx.currentTime + 0.8);
    const stop = ENGINES[sceneId](ctx, sceneGain);
    A.currentStop = () => {
      sceneGain.gain.cancelScheduledValues(ctx.currentTime);
      sceneGain.gain.linearRampToValueAtTime(0, ctx.currentTime + 0.4);
      setTimeout(() => { try { stop(); sceneGain.disconnect(); } catch (e) {} }, 500);
    };
  });
}

// ─── The Ambience Window ─────────────────────────────────────────────────
function AmbienceWindow({ activeScene, onPick, soundOn, onSoundToggle, volume, onVolume, animOn }) {
  return (
    <div className="window-inner">
      <h1>AMBIENCE</h1>
      <p className="muted" style={{margin: '8px 0 16px'}}>
        Pick a scene. The whole desktop becomes that environment, with a procedural soundscape generated live in the browser — no audio files.
      </p>

      <div className="pixel-card ambience-controls" style={{display: 'flex', alignItems: 'center', gap: 14, marginBottom: 16}}>
        <button className={`pbtn ${soundOn ? 'primary' : 'ghost'}`} onClick={onSoundToggle}>
          {soundOn ? '◼ MUTE' : '▶ SOUND ON'}
        </button>
        <span className="tag moon">VOL</span>
        <input
          type="range" min="0" max="1" step="0.05" value={volume}
          onPointerDown={() => window.unlockAudio()}
          onChange={(e) => onVolume(parseFloat(e.target.value))}
          style={{flex: 1, accentColor: 'var(--moon)'}}
        />
        <span style={{fontFamily: 'var(--font-pixel)', fontSize: 10, color: 'var(--moon)', minWidth: 44}}>
          {Math.round(volume * 100)}%
        </span>
      </div>

      <div className="scene-grid">
        {SCENES.map(s => {
          const isActive = activeScene === s.id;
          return (
            <button key={s.id}
              className={`scene-card ${isActive ? 'active' : ''}`}
              onClick={() => onPick(s.id)}>
              <div className="scene">
                <window.MountainScene variant={s.id} animOn={animOn && isActive} />
              </div>
              <div className="scene-label">
                <span>{s.label}</span>
                <span className={isActive ? 'accent-moon' : 'muted'} style={{fontSize: 8}}>
                  {isActive ? '▶ ACTIVE' : '○ PICK'}
                </span>
              </div>
              <div style={{fontFamily: 'var(--font-mono)', fontSize: 15, color: 'var(--ink-dim)', marginTop: 4, lineHeight: 1.25}}>
                {s.desc}
              </div>
            </button>
          );
        })}
      </div>

      <div className="pixel-divider" />
      <div className="pixel-card" style={{background: 'var(--bg-2)'}}>
        <h4 className="accent-cyan">SLEEP SCIENCE</h4>
        <p style={{fontSize: 18, marginTop: 6}}>
          Steady broadband sound (rain, ocean, low-frequency drone) masks unpredictable noises and reduces cortical arousal — the same reason hospital sleep clinics use white-noise generators. Avoid lyrics or musical melody close to sleep onset: the brain tracks them.
        </p>
      </div>
    </div>
  );
}

Object.assign(window, { AmbienceWindow, SCENES, playSceneAudio, setMasterVolume, ensureCtx, unlockAudio });
