/**
 * Mindavio Sound System
 * All sounds are generated programmatically via Web Audio API —
 * no external audio files needed. Volume is kept gentle (~0.09–0.13 per oscillator).
 */

import { useSoundStore } from "@/stores/sound-store";

let sharedAudioContext: AudioContext | null = null;
let unlockInitialized = false;

/* ── Public sound functions ──────────────────────────────────────────────── */

export function initSoundUnlock() {
  if (typeof window === "undefined" || unlockInitialized) return;

  unlockInitialized = true;

  const unlock = () => {
    const ctx = getAudioContext();
    if (!ctx) return;

    void ctx.resume().then(() => {
      // A silent tap during the gesture unlocks later timer-triggered sounds.
      const osc = ctx.createOscillator();
      const gain = ctx.createGain();
      gain.gain.value = 0;
      osc.connect(gain);
      gain.connect(ctx.destination);
      osc.start();
      osc.stop(ctx.currentTime + 0.01);
    }).catch(() => {});
  };

  window.addEventListener("pointerdown", unlock, { once: true, passive: true });
  window.addEventListener("keydown", unlock, { once: true });
  window.addEventListener("touchstart", unlock, { once: true, passive: true });
}

/** Short single-note ping. Used when a new DB notification arrives. */
export function playNotificationPing() {
  if (!can("notification")) return;
  tone([
    { freq: 1174.7, vol: 0.08, delay: 0, dur: 0.40 }, // D6
  ]);
}

/** Two-note upward chime. Used for smart/proactive info alerts. */
export function playSmartAlert() {
  if (!can("notification")) return;
  tone([
    { freq: 784,  vol: 0.09, delay: 0,    dur: 0.45 }, // G5
    { freq: 987.8,vol: 0.09, delay: 0.20, dur: 0.45 }, // B5
  ]);
}

/** Two-note descending minor. Used for overdue / urgent deadlines. */
export function playUrgentPing() {
  if (!can("notification")) return;
  tone([
    { freq: 659.3, vol: 0.10, delay: 0,    dur: 0.30, type: "triangle" }, // E5
    { freq: 523.3, vol: 0.10, delay: 0.16, dur: 0.35, type: "triangle" }, // C5
  ]);
}

/** Ascending 5-note fanfare. Used when an achievement is completed. */
export function playAchievementUnlock() {
  if (!can("achievement")) return;
  tone([
    { freq: 523.25, vol: 0.11, delay: 0,    dur: 0.70 }, // C5
    { freq: 659.25, vol: 0.11, delay: 0.12, dur: 0.70 }, // E5
    { freq: 783.99, vol: 0.11, delay: 0.24, dur: 0.70 }, // G5
    { freq: 987.77, vol: 0.11, delay: 0.36, dur: 0.70 }, // B5
    { freq: 1046.5, vol: 0.14, delay: 0.50, dur: 1.00 }, // C6 — sustained finish
  ]);
}

/** Triumphant chord-based fanfare. Used when claiming a reward. */
export function playRewardClaim() {
  if (!can("achievement")) return;
  tone([
    // Beat 1 — root chord C5+E5
    { freq: 523.25, vol: 0.09, delay: 0,    dur: 0.90 },
    { freq: 659.25, vol: 0.09, delay: 0,    dur: 0.90 },
    // Beat 2 — G5+C6 open fifth
    { freq: 783.99, vol: 0.10, delay: 0.20, dur: 0.90 },
    { freq: 1046.5, vol: 0.10, delay: 0.20, dur: 0.90 },
    // Beat 3 — E6 sparkle
    { freq: 1318.5, vol: 0.09, delay: 0.42, dur: 1.10 },
    // Beat 4 — G6 high resolution
    { freq: 1568.0, vol: 0.08, delay: 0.62, dur: 1.30 },
  ]);
}

/** Crisp two-note "done". Used when a task is completed. */
export function playTaskDone() {
  if (!can("task")) return;
  tone([
    { freq: 783.99, vol: 0.10, delay: 0,    dur: 0.30 }, // G5
    { freq: 1046.5, vol: 0.10, delay: 0.12, dur: 0.42 }, // C6
  ]);
}

/** Clean glassy resolve. Used when today's journal entry is marked complete. */
export function playJournalComplete() {
  if (!can("notification")) return;
  tone([
    { freq: 587.33, vol: 0.075, delay: 0,    dur: 0.34, type: "triangle" }, // D5
    { freq: 880.0,  vol: 0.08,  delay: 0.12, dur: 0.42, type: "sine" }, // A5
    { freq: 1174.7, vol: 0.07,  delay: 0.28, dur: 0.62, type: "sine" }, // D6
  ]);
}

/** Soft snap-pop. Used when a habit is checked off. */
export function playHabitComplete() {
  if (!can("task")) return;
  tone([
    { freq: 698.46, vol: 0.07, delay: 0,    dur: 0.18, type: "triangle" }, // F5
    { freq: 1046.5, vol: 0.08, delay: 0.08, dur: 0.30, type: "sine" }, // C6
  ]);
}

/** Slightly richer completion shimmer. Used when a goal hits 100%. */
export function playGoalComplete() {
  if (!can("achievement")) return;
  tone([
    { freq: 493.88, vol: 0.08, delay: 0,    dur: 0.42, type: "triangle" }, // B4
    { freq: 739.99, vol: 0.08, delay: 0.12, dur: 0.46, type: "sine" }, // F#5
    { freq: 987.77, vol: 0.08, delay: 0.26, dur: 0.60, type: "sine" }, // B5
    { freq: 1479.98,vol: 0.04, delay: 0.36, dur: 0.45, type: "sine" }, // F#6
  ]);
}

/** Gentle triple bell. Used for journal reminders. */
export function playJournalReminder() {
  if (!can("notification")) return;
  tone([
    { freq: 659.25, vol: 0.09, delay: 0,    dur: 0.60 }, // E5
    { freq: 659.25, vol: 0.08, delay: 0.30, dur: 0.55 }, // E5 echo
    { freq: 783.99, vol: 0.09, delay: 0.55, dur: 0.70 }, // G5 resolve
  ]);
}

/** Tiny bell tap. Used when a reminder becomes due. */
export function playReminderDue() {
  if (!can("notification")) return;
  tone([
    { freq: 1568.0, vol: 0.07, delay: 0, dur: 0.13, type: "triangle" }, // G6
    { freq: 3136.0, vol: 0.025, delay: 0, dur: 0.09, type: "sine" }, // small bell overtone
  ]);
}

/** Ascending event chime (C5→E5→G5). Used for upcoming calendar events. */
export function playEventAlert() {
  if (!can("notification")) return;
  tone([
    { freq: 523.25, vol: 0.09, delay: 0,    dur: 0.50 }, // C5
    { freq: 659.25, vol: 0.09, delay: 0.16, dur: 0.50 }, // E5
    { freq: 783.99, vol: 0.10, delay: 0.32, dur: 0.60 }, // G5
  ]);
}

/** Soft two-note ascending tap. Used when the Pomodoro timer starts. */
export function playTimerStart() {
  if (!canTimer()) return;
  tone([
    { freq: 523.25, vol: 0.09, delay: 0,    dur: 0.28 }, // C5
    { freq: 783.99, vol: 0.10, delay: 0.14, dur: 0.38 }, // G5
  ]);
}

/** Three-note descending bell. Used when a Pomodoro session completes. */
export function playTimerComplete() {
  if (!canTimer()) return;
  tone([
    { freq: 783.99, vol: 0.13, delay: 0,    dur: 0.90 }, // G5
    { freq: 659.25, vol: 0.13, delay: 0.22, dur: 0.85 }, // E5
    { freq: 523.25, vol: 0.15, delay: 0.44, dur: 1.20 }, // C5 — sustained
  ]);
}

/* ── Internal helpers ────────────────────────────────────────────────────── */

type SoundCategory = "notification" | "achievement" | "task";

function canTimer(): boolean {
  try { return useSoundStore.getState().soundsEnabled; } catch { return false; }
}

function can(category: SoundCategory): boolean {
  try {
    const s = useSoundStore.getState();
    if (!s.soundsEnabled) return false;
    if (category === "notification" && !s.notificationSounds)  return false;
    if (category === "achievement"  && !s.achievementSounds)   return false;
    if (category === "task"         && !s.taskSounds)          return false;
    return true;
  } catch {
    return false;
  }
}

interface Note {
  freq:   number;
  vol:    number;
  delay:  number;
  dur:    number;
  type?:  OscillatorType;
}

function tone(notes: Note[]) {
  try {
    const masterVol = useSoundStore.getState().volume ?? 0.8;
    const ctx = getAudioContext();
    if (!ctx) return;

    const play = () => {
      for (const { freq, vol, delay, dur, type = "sine" } of notes) {
        const osc  = ctx.createOscillator();
        const gain = ctx.createGain();
        osc.connect(gain);
        gain.connect(ctx.destination);
        osc.type = type;
        osc.frequency.value = freq;
        const t = ctx.currentTime + delay + 0.01;
        gain.gain.setValueAtTime(0.0001, t);
        gain.gain.linearRampToValueAtTime(vol * masterVol, t + 0.015);
        gain.gain.exponentialRampToValueAtTime(0.0001, t + dur);
        osc.start(t);
        osc.stop(t + dur + 0.04);
      }
    };

    if (ctx.state === "suspended") {
      void ctx.resume().then(play).catch(() => {});
      return;
    }

    play();
  } catch { /* AudioContext blocked by browser policy — silently skip */ }
}

function getAudioContext() {
  if (typeof window === "undefined") return null;

  const win = window as Window &
    typeof globalThis & { webkitAudioContext?: typeof AudioContext };
  const AudioContextCtor = win.AudioContext || win.webkitAudioContext;
  if (!AudioContextCtor) return null;

  sharedAudioContext ??= new AudioContextCtor();
  return sharedAudioContext;
}
