// Ambient sound mixer — WebAudio based. Generates looping pad/noise/drone
// textures procedurally so the prototype has REAL audio without shipping
// audio files. In production (Capacitor) these become .mp3/.ogg loops
// played via @capacitor-community/native-audio for background + lock-screen
// controls.

// Ambient library entries. `label`/`desc` are the English fallback; `labelKey`
// and `descKey` route through BFI18n so picker entries follow the UI locale.
const BF_AMBIENTS = [
  { id: 'silence', labelKey: 'ambient.silence', descKey: 'ambient.silence_sub', label: 'Silence',       icon: 'volumeOff', desc: 'No sound' },
  { id: 'rain',    labelKey: 'ambient.rain',    descKey: 'ambient.rain_sub',    label: 'Rain',          icon: 'wind',      desc: 'Soft rainfall' },
  { id: 'ocean',   labelKey: 'ambient.ocean',   descKey: 'ambient.ocean_sub',   label: 'Ocean',         icon: 'wind',      desc: 'Waves · low swell' },
  { id: 'forest',  labelKey: 'ambient.forest',  descKey: 'ambient.forest_sub',  label: 'Forest',        icon: 'flow',      desc: 'Wind through leaves' },
  { id: 'drone',   labelKey: 'ambient.drone',   descKey: 'ambient.drone_sub',   label: 'Drone',         icon: 'sparkle',   desc: 'Tonal pad · 110 Hz' },
  { id: 'bowls',   labelKey: 'ambient.bowls',   descKey: 'ambient.bowls_sub',   label: 'Singing bowls', icon: 'circle',    desc: 'Tibetan bowls' },
  { id: 'brown',   labelKey: 'ambient.brown',   descKey: 'ambient.brown_sub',   label: 'Brown noise',   icon: 'volume',    desc: 'Deep low-end hiss' },
];

class BFAmbientEngine {
  constructor() {
    this.ctx = null;
    this.master = null;
    this.current = 'silence';
    this.nodes = [];
    this.volume = 0.35;
  }
  _ensure() {
    if (this.ctx) return;
    this.ctx = new (window.AudioContext || window.webkitAudioContext)();
    this.master = this.ctx.createGain();
    this.master.gain.value = this.volume;
    this.master.connect(this.ctx.destination);
  }
  _stopAll() {
    this.nodes.forEach(n => { try { n.stop?.(); n.disconnect?.(); } catch(e){} });
    this.nodes = [];
  }
  setVolume(v) {
    this.volume = v;
    if (this.master) this.master.gain.linearRampToValueAtTime(v, this.ctx.currentTime + 0.15);
  }
  play(id) {
    this._ensure();
    if (this.ctx.state === 'suspended') this.ctx.resume();
    this._stopAll();
    this.current = id;
    if (id === 'silence') return;
    const ctx = this.ctx, master = this.master;
    const map = { rain: 'noise-rain', ocean: 'noise-ocean', forest: 'noise-forest', drone: 'tone-drone', bowls: 'tone-bowls', brown: 'noise-brown' };
    const kind = map[id];
    if (!kind) return;
    if (kind.startsWith('noise-')) this._makeNoise(kind.split('-')[1]);
    if (kind.startsWith('tone-'))  this._makeTone(kind.split('-')[1]);
  }
  _makeNoiseBuffer(type) {
    const ctx = this.ctx;
    const len = ctx.sampleRate * 4;
    const buf = ctx.createBuffer(1, len, ctx.sampleRate);
    const d = buf.getChannelData(0);
    let last = 0;
    for (let i = 0; i < len; i++) {
      const white = Math.random() * 2 - 1;
      if (type === 'brown') { last = (last + 0.02 * white) / 1.02; d[i] = last * 3.5; }
      else if (type === 'rain') { last = 0.95 * last + 0.05 * white; d[i] = last * 0.8 + white * 0.1; }
      else if (type === 'ocean') { last = 0.98 * last + 0.02 * white; d[i] = last; }
      else d[i] = white;
    }
    return buf;
  }
  _makeNoise(type) {
    const ctx = this.ctx;
    const src = ctx.createBufferSource();
    src.buffer = this._makeNoiseBuffer(type);
    src.loop = true;
    const filter = ctx.createBiquadFilter();
    filter.type = 'lowpass';
    filter.frequency.value = type === 'rain' ? 2400 : type === 'ocean' ? 600 : type === 'forest' ? 1800 : 500;
    const g = ctx.createGain(); g.gain.value = type === 'brown' ? 0.5 : 0.8;
    src.connect(filter); filter.connect(g); g.connect(this.master);
    src.start();
    this.nodes.push(src, filter, g);

    // slow LFO on volume for "breathing" water/wind motion
    if (type === 'ocean' || type === 'forest' || type === 'rain') {
      const lfo = ctx.createOscillator(); lfo.frequency.value = type === 'ocean' ? 0.1 : 0.3;
      const lfoGain = ctx.createGain(); lfoGain.gain.value = 0.3;
      lfo.connect(lfoGain); lfoGain.connect(g.gain);
      lfo.start(); this.nodes.push(lfo, lfoGain);
    }
  }
  _makeTone(type) {
    const ctx = this.ctx;
    const freqs = type === 'drone' ? [55, 110, 165] : [196, 293.66, 440, 587.33]; // drone / singing bowls (G3, D4, A4, D5)
    freqs.forEach((f, i) => {
      const osc = ctx.createOscillator();
      osc.type = type === 'drone' ? 'sine' : 'triangle';
      osc.frequency.value = f + (Math.random() - 0.5) * 0.4;
      const g = ctx.createGain(); g.gain.value = 0;
      g.gain.linearRampToValueAtTime(type === 'drone' ? 0.12 : 0.08, ctx.currentTime + 2 + i * 0.8);
      osc.connect(g); g.connect(this.master);
      osc.start();
      this.nodes.push(osc, g);
      if (type === 'bowls') {
        const lfo = ctx.createOscillator(); lfo.frequency.value = 0.05 + i * 0.02;
        const lfoG = ctx.createGain(); lfoG.gain.value = 0.04;
        lfo.connect(lfoG); lfoG.connect(g.gain);
        lfo.start(); this.nodes.push(lfo, lfoG);
      }
    });
  }
  stop() { this._stopAll(); this.current = 'silence'; }
}

const bfAmbient = new BFAmbientEngine();

// Haptics — navigator.vibrate works on Android browsers; iOS Safari ignores
// silently, which matches our UI note. Capacitor Haptics plugin handles both
// platforms natively in the shipped app.
const bfHaptic = {
  light: () => navigator.vibrate?.(10),
  medium: () => navigator.vibrate?.(25),
  heavy: () => navigator.vibrate?.(50),
  success: () => navigator.vibrate?.([15, 40, 15]),
  breathe: (phase) => {
    if (phase === 'inhale')  navigator.vibrate?.(8);
    if (phase === 'exhale')  navigator.vibrate?.([4, 30, 4]);
  },
};

// Ambient picker screen
function BFAmbientPicker({ theme, accentH, current, onPick, volume, onVolume }) {
  // Resolve strings via BFI18n so the picker re-paints when the UI locale
  // flips. `labelKey` falls back to the English `label` if the catalog is
  // missing a key (e.g. during early boot before i18n.js has hydrated).
  const t = (k, fb) => (window.BFI18n ? window.BFI18n.t(k) : fb);
  return (
    <BFCard theme={theme} style={{ padding: 0, overflow: 'hidden', marginBottom: 18 }}>
      <div style={{ padding: '14px 16px', borderBottom: `1px solid ${theme.line}`, display: 'flex', alignItems: 'center', gap: 12 }}>
        <div style={{ flex: 1, fontSize: 13, color: theme.fgMuted }}>{t('settings.volume', 'Volume')}</div>
        <input type="range" min="0" max="1" step="0.01" value={volume} onChange={(e) => onVolume(parseFloat(e.target.value))} style={{ flex: 2, accentColor: bfAccentSolid(accentH) }} />
      </div>
      {BF_AMBIENTS.map((a, i) => {
        const active = current === a.id;
        return (
          <button key={a.id} onClick={() => onPick(a.id)} style={{
            width: '100%', display: 'flex', alignItems: 'center', gap: 14, padding: '12px 16px',
            background: active ? bfAccentTint(theme, accentH, 0.08) : 'transparent',
            border: 'none', borderBottom: i < BF_AMBIENTS.length - 1 ? `1px solid ${theme.line}` : 'none',
            cursor: 'pointer', textAlign: 'left', color: theme.fg, fontFamily: BF_FONTS.sans,
          }}>
            <div style={{
              width: 32, height: 32, borderRadius: 999,
              background: active ? bfAccentTint(theme, accentH, 0.2) : theme.cardSoft,
              color: active ? bfAccentText(theme, accentH) : theme.fgFaint,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
            }}>
              <BFIcon name={a.icon} size={15} />
            </div>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 14 }}>{t(a.labelKey, a.label)}</div>
              <div style={{ fontSize: 11, color: theme.fgFaint, marginTop: 2 }}>{t(a.descKey, a.desc)}</div>
            </div>
            {active && <div style={{ width: 6, height: 6, borderRadius: 999, background: bfAccentSolid(accentH) }} />}
          </button>
        );
      })}
    </BFCard>
  );
}

Object.assign(window, { BF_AMBIENTS, bfAmbient, bfHaptic, BFAmbientPicker });
