/** Tiny Web Audio synth — soft cozy blips, no samples. */

let ctx: AudioContext | null = null;
let master: GainNode | null = null;

export function initAudio(): void {
  if (ctx) return;
  try {
    ctx = new AudioContext();
    master = ctx.createGain();
    master.gain.value = 0.35;
    master.connect(ctx.destination);
  } catch {
    ctx = null;
  }
}

function now(): number {
  return ctx ? ctx.currentTime : 0;
}

/** small random detune so repeated tool hits don't sound machine-stamped */
function jitter(freq: number): number {
  return freq * (0.95 + Math.random() * 0.1);
}

// ---------------------------------------------------------------------------
// ambient bed: soft wind loop + day birds / night crickets
// ---------------------------------------------------------------------------

let windGain: GainNode | null = null;
let chirpTimer = 4;
let cricketTimer = 2;

function startWind(): void {
  if (!ctx || !master || windGain) return;
  const len = ctx.sampleRate * 4;
  const buf = ctx.createBuffer(1, len, ctx.sampleRate);
  const ch = buf.getChannelData(0);
  let last = 0;
  for (let i = 0; i < len; i++) {
    // brown-ish noise: gentle rumble rather than hiss
    last = (last + (Math.random() * 2 - 1) * 0.04) * 0.985;
    ch[i] = last * 6;
  }
  const src = ctx.createBufferSource();
  src.buffer = buf;
  src.loop = true;
  const lp = ctx.createBiquadFilter();
  lp.type = 'lowpass';
  lp.frequency.value = 420;
  // slow swell so the wind breathes
  const lfo = ctx.createOscillator();
  lfo.frequency.value = 0.07;
  const lfoGain = ctx.createGain();
  lfoGain.gain.value = 0.025;
  windGain = ctx.createGain();
  windGain.gain.value = 0.05;
  lfo.connect(lfoGain).connect(windGain.gain);
  src.connect(lp).connect(windGain).connect(master);
  src.start();
  lfo.start();
}

/** call every frame during play; schedules drifting ambience */
export function updateAmbience(time: number, dt: number): void {
  if (!ctx || !master) return;
  startWind();
  const night = time > 0.74;

  chirpTimer -= dt;
  if (!night && chirpTimer <= 0) {
    chirpTimer = 5 + Math.random() * 9;
    // tiny descending birdsong figure
    const base = 1900 + Math.random() * 900;
    const n = 2 + Math.floor(Math.random() * 3);
    for (let i = 0; i < n; i++) {
      note(base * (1 - i * 0.12), 0.09, 'sine', 0.045, i * 0.13 + Math.random() * 0.04, -180);
    }
  }

  cricketTimer -= dt;
  if (night && cricketTimer <= 0) {
    cricketTimer = 1.6 + Math.random() * 1.8;
    for (let i = 0; i < 3; i++) note(4200 + Math.random() * 300, 0.045, 'sine', 0.02, i * 0.09);
  }
}

/** one soft enveloped oscillator note */
function note(freq: number, dur: number, type: OscillatorType, vol: number, when = 0, slide = 0): void {
  if (!ctx || !master) return;
  if (ctx.state === 'suspended') void ctx.resume();
  const t0 = now() + when;
  const osc = ctx.createOscillator();
  const g = ctx.createGain();
  osc.type = type;
  osc.frequency.setValueAtTime(freq, t0);
  if (slide) osc.frequency.exponentialRampToValueAtTime(Math.max(40, freq + slide), t0 + dur);
  g.gain.setValueAtTime(0.0001, t0);
  g.gain.exponentialRampToValueAtTime(vol, t0 + 0.012);
  g.gain.exponentialRampToValueAtTime(0.0001, t0 + dur);
  osc.connect(g).connect(master);
  osc.start(t0);
  osc.stop(t0 + dur + 0.05);
}

/** filtered noise puff (thuds, splashes) */
function puff(dur: number, freq: number, vol: number, q = 1, when = 0): void {
  if (!ctx || !master) return;
  if (ctx.state === 'suspended') void ctx.resume();
  const t0 = now() + when;
  const len = Math.ceil(ctx.sampleRate * dur);
  const buf = ctx.createBuffer(1, len, ctx.sampleRate);
  const ch = buf.getChannelData(0);
  for (let i = 0; i < len; i++) ch[i] = (Math.random() * 2 - 1) * (1 - i / len);
  const src = ctx.createBufferSource();
  src.buffer = buf;
  const f = ctx.createBiquadFilter();
  f.type = 'bandpass';
  f.frequency.value = freq;
  f.Q.value = q;
  const g = ctx.createGain();
  g.gain.setValueAtTime(vol, t0);
  g.gain.exponentialRampToValueAtTime(0.0001, t0 + dur);
  src.connect(f).connect(g).connect(master);
  src.start(t0);
}

export const sfx = {
  /** hoe: earthy thud + tink */
  till(): void {
    puff(0.12, jitter(220), 0.5, 0.8);
    note(jitter(196), 0.1, 'triangle', 0.25, 0.01, -60);
    note(jitter(1320), 0.05, 'sine', 0.08, 0.03); // little metal tink
  },
  /** watering can: soft whoosh */
  water(): void {
    puff(0.3, jitter(900), 0.3, 0.6);
    puff(0.22, jitter(1800), 0.15, 1, 0.05);
  },
  /** seed pop */
  plant(): void {
    note(jitter(520), 0.07, 'sine', 0.3, 0, 140);
    puff(0.06, jitter(500), 0.2, 1.5, 0.02);
  },
  /** harvest: pluck + sparkle */
  harvest(): void {
    note(jitter(392), 0.12, 'triangle', 0.3);
    note(jitter(587), 0.12, 'triangle', 0.25, 0.07);
    note(jitter(784), 0.18, 'sine', 0.2, 0.14);
  },
  /** coins at shipping */
  coins(): void {
    note(880, 0.09, 'square', 0.12);
    note(1175, 0.14, 'square', 0.1, 0.08);
  },
  /** tool hotbar tick */
  tick(): void {
    note(660, 0.04, 'sine', 0.12);
  },
  /** swing that hit nothing */
  whiff(): void {
    puff(0.08, 600, 0.12, 0.7);
  },
  /** out of energy */
  deny(): void {
    note(160, 0.18, 'sawtooth', 0.12, 0, -40);
  },
  /** drifting off */
  sleep(): void {
    note(523, 0.3, 'sine', 0.2);
    note(392, 0.35, 'sine', 0.2, 0.2);
    note(262, 0.5, 'sine', 0.2, 0.4);
  },
  /** rise and shine */
  morning(): void {
    note(392, 0.15, 'triangle', 0.2);
    note(523, 0.15, 'triangle', 0.2, 0.12);
    note(659, 0.25, 'triangle', 0.22, 0.24);
  },
};
