let audioCtx = null;

function getCtx() {
  if (audioCtx === null || audioCtx === undefined) audioCtx = new (window.AudioContext || window.webkitAudioContext)();
  return audioCtx;
}

function applySoundUpgrade(osc, now) {
  osc.frequency.setValueAtTime(659, now);
  osc.frequency.setValueAtTime(784, now + 0.06);
}

function applySoundHatch(osc, now) {
  osc.type = "sine";
  osc.frequency.setValueAtTime(392, now);
  osc.frequency.setValueAtTime(523, now + 0.08);
  osc.frequency.setValueAtTime(659, now + 0.16);
}

function applySoundPlace(osc, now) {
  osc.type = "sine";
  osc.frequency.setValueAtTime(659, now);
  osc.frequency.setValueAtTime(523, now + 0.05);
  osc.frequency.setValueAtTime(784, now + 0.1);
}

function applySoundBuy(osc, now) {
  osc.type = "sine";
  osc.frequency.setValueAtTime(523, now);
  osc.frequency.setValueAtTime(659, now + 0.05);
  osc.frequency.setValueAtTime(784, now + 0.1);
}

function applySoundSell(osc, now) {
  osc.type = "triangle";
  osc.frequency.setValueAtTime(440, now);
  osc.frequency.setValueAtTime(349, now + 0.06);
  osc.frequency.setValueAtTime(262, now + 0.12);
}

function applySoundPlotUpgrade(osc, now) {
  osc.type = "sine";
  osc.frequency.setValueAtTime(330, now);
  osc.frequency.setValueAtTime(440, now + 0.06);
  osc.frequency.setValueAtTime(554, now + 0.12);
}

function applySoundTruckUpgrade(osc, now) {
  osc.type = "square";
  osc.frequency.setValueAtTime(220, now);
  osc.frequency.setValueAtTime(277, now + 0.05);
  osc.frequency.setValueAtTime(330, now + 0.1);
}

function applySoundSchoolUpgrade(osc, now) {
  osc.type = "sine";
  osc.frequency.setValueAtTime(523, now);
  osc.frequency.setValueAtTime(659, now + 0.05);
  osc.frequency.setValueAtTime(784, now + 0.1);
  osc.frequency.setValueAtTime(1047, now + 0.15);
}

function applySoundWorldMapUpgrade(osc, now) {
  osc.type = "sine";
  osc.frequency.setValueAtTime(440, now);
  osc.frequency.setValueAtTime(554, now + 0.06);
  osc.frequency.setValueAtTime(698, now + 0.12);
}

function applySoundQuest(osc, now) {
  osc.frequency.setValueAtTime(659, now);
  osc.frequency.setValueAtTime(784, now + 0.06);
}

function applySoundError(osc, now) {
  osc.frequency.setValueAtTime(200, now);
  osc.frequency.setValueAtTime(180, now + 0.08);
}

function applySoundDefault(osc, now) {
  osc.frequency.setValueAtTime(440, now);
}

const SOUND_APPLIERS = {
  upgrade: applySoundUpgrade,
  hatch: applySoundHatch,
  place: applySoundPlace,
  buy: applySoundBuy,
  sell: applySoundSell,
  plotUpgrade: applySoundPlotUpgrade,
  truckUpgrade: applySoundTruckUpgrade,
  schoolUpgrade: applySoundSchoolUpgrade,
  worldMapUpgrade: applySoundWorldMapUpgrade,
  quest: applySoundQuest,
  error: applySoundError,
};

/**
 * @param {string} type One of: hatch, place, buy, sell, plotUpgrade, truckUpgrade, schoolUpgrade, worldMapUpgrade, upgrade, quest, error
 * @returns {void}
 */
export function playSound(type) {
  try {
    const ctx = getCtx();
    const now = ctx.currentTime;
    const osc = ctx.createOscillator();
    const gain = ctx.createGain();
    osc.connect(gain);
    gain.connect(ctx.destination);
    gain.gain.setValueAtTime(0.15, now);
    gain.gain.exponentialRampToValueAtTime(0.01, now + 0.15);
    osc.start(now);
    osc.stop(now + 0.15);
    const applier = SOUND_APPLIERS[type] || applySoundDefault;
    applier(osc, now);
  } catch (e) {
    console.warn("playSound failed", e);
  }
}

let musicEnabled = false;
let musicIntervalId = null;
let musicGetState = () => null;
const MUSIC_BEAT_MS = 400;

/**
 * Register state getter so music can switch by day/night/truck.
 * @param {() => { timeOfDay?: number, truckSale?: { startAt: number } }} getState
 */
export function setMusicGetState(getState) {
  musicGetState = getState || (() => null);
}

function isNight(timeOfDay) {
  const t = (timeOfDay ?? 6) % 24;
  return t < 6 || t >= 20;
}

function isTruckMoving(state) {
  const sale = state.truckSale;
  if (!sale || !sale.startAt) return false;
  const truckMs = 2500;
  return (Date.now() - sale.startAt) < truckMs;
}

function playMusicVoliere() {
  if (!musicEnabled || (audioCtx === null || audioCtx === undefined)) return;
  try {
    const ctx = getCtx();
    const now = ctx.currentTime;
    const gain = ctx.createGain();
    gain.connect(ctx.destination);
    gain.gain.setValueAtTime(0, now);
    gain.gain.linearRampToValueAtTime(0.04, now + 0.02);
    gain.gain.exponentialRampToValueAtTime(0.004, now + 0.25);
    const freq = 600 + Math.random() * 800;
    const osc = ctx.createOscillator();
    osc.type = "sine";
    osc.frequency.setValueAtTime(freq, now);
    osc.connect(gain);
    osc.start(now);
    osc.stop(now + 0.25);
  } catch (e) {
    console.warn("playMusicVoliere failed", e);
  }
}

function playMusicBrahms() {
  if (!musicEnabled || (audioCtx === null || audioCtx === undefined)) return;
  try {
    const ctx = getCtx();
    const now = ctx.currentTime;
    const gain = ctx.createGain();
    gain.connect(ctx.destination);
    gain.gain.setValueAtTime(0, now);
    gain.gain.linearRampToValueAtTime(0.05, now + 0.03);
    gain.gain.exponentialRampToValueAtTime(0.005, now + 0.5);
    const waltz = [415.3, 523.25, 622.25];
    waltz.forEach((freq, i) => {
      const osc = ctx.createOscillator();
      osc.type = "sine";
      osc.frequency.setValueAtTime(freq, now + i * 0.15);
      osc.connect(gain);
      osc.start(now + i * 0.15);
      osc.stop(now + 0.5);
    });
  } catch (e) {
    console.warn("playMusicBrahms failed", e);
  }
}

function playMusicTrepak() {
  if (!musicEnabled || (audioCtx === null || audioCtx === undefined)) return;
  try {
    const ctx = getCtx();
    const now = ctx.currentTime;
    const gain = ctx.createGain();
    gain.connect(ctx.destination);
    gain.gain.setValueAtTime(0, now);
    gain.gain.linearRampToValueAtTime(0.08, now + 0.02);
    gain.gain.exponentialRampToValueAtTime(0.006, now + 0.12);
    const osc = ctx.createOscillator();
    osc.type = "triangle";
    osc.frequency.setValueAtTime(280, now);
    osc.frequency.setValueAtTime(350, now + 0.06);
    osc.connect(gain);
    osc.start(now);
    osc.stop(now + 0.12);
  } catch (e) {
    console.warn("playMusicTrepak failed", e);
  }
}

function playMusicTick() {
  if (!musicEnabled || (audioCtx === null || audioCtx === undefined)) return;
  const state = musicGetState();
  if (state && isTruckMoving(state)) {
    playMusicTrepak();
    return;
  }
  const timeOfDay = state && state.timeOfDay !== null && state.timeOfDay !== undefined ? state.timeOfDay : 6;
  if (isNight(timeOfDay)) {
    playMusicBrahms();
  } else {
    playMusicVoliere();
  }
}

/** Call to enable/disable background music (procedural: Volière / Brahms / Trepak). */
export function setMusicEnabled(enabled) {
  if (musicIntervalId !== null && musicIntervalId !== undefined) {
    clearInterval(musicIntervalId);
    musicIntervalId = null;
  }
  musicEnabled = Boolean(enabled);
  if (musicEnabled) {
    getCtx();
    playMusicTick();
    musicIntervalId = setInterval(playMusicTick, MUSIC_BEAT_MS);
  }
}

export function isMusicEnabled() {
  return musicEnabled;
}
