// LearnSpace — Sound System
// All sounds synthesized via Web Audio API. No external files needed.
// Usage: LS.sfx.play('achievement')  LS.sfx.setVolume(0.5)  LS.sfx.setEnabled(false)

(function () {
    'use strict';

    // ── AudioContext (lazy, respects autoplay policy) ──────────────────────────
    let _ctx = null;
    function _getCtx() {
        if (!_ctx) {
            _ctx = new (window.AudioContext || window.webkitAudioContext)();
        }
        if (_ctx.state === 'suspended') _ctx.resume();
        return _ctx;
    }

    // ── Master volume node ─────────────────────────────────────────────────────
    let _masterGain = null;
    function _getMaster() {
        if (!_masterGain) {
            _masterGain = _getCtx().createGain();
            _masterGain.connect(_getCtx().destination);
        }
        return _masterGain;
    }

    // ── Low-level synth: single tone with ADSR envelope ───────────────────────
    // freq      — Hz
    // dur       — total duration ms
    // type      — OscillatorType: 'sine'|'square'|'sawtooth'|'triangle'
    // vol       — 0–1 (before master)
    // attack    — ms
    // release   — ms
    function _tone(freq, dur, type, vol, attack, release) {
        type    = type    || 'sine';
        vol     = vol     || 0.18;
        attack  = attack  || 8;
        release = release || Math.min(80, dur * 0.4);

        const ctx  = _getCtx();
        const osc  = ctx.createOscillator();
        const gain = ctx.createGain();

        osc.connect(gain);
        gain.connect(_getMaster());

        osc.type            = type;
        osc.frequency.value = freq;

        const now = ctx.currentTime;
        const durS = dur / 1000;
        const atkS = attack / 1000;
        const relS = release / 1000;

        gain.gain.setValueAtTime(0, now);
        gain.gain.linearRampToValueAtTime(vol, now + atkS);
        if (durS - atkS - relS > 0) {
            gain.gain.setValueAtTime(vol, now + durS - relS);
        }
        gain.gain.linearRampToValueAtTime(0, now + durS);

        osc.start(now);
        osc.stop(now + durS + 0.02);
    }

    // ── Sequence: play notes with delay offsets ────────────────────────────────
    // notes: [ [freq, dur, delayMs, type?, vol?], ... ]
    function _seq(notes, defaultType, defaultVol) {
        notes.forEach(function (n) {
            setTimeout(function () {
                _tone(n[0], n[1], n[2] || defaultType || 'sine', n[3] !== undefined ? n[3] : (defaultVol || 0.15));
            }, n[4] || 0);
        });
    }

    // ── Noise burst (for 'error' thud) ────────────────────────────────────────
    function _noise(dur, vol) {
        const ctx         = _getCtx();
        const bufSize     = ctx.sampleRate * (dur / 1000);
        const buf         = ctx.createBuffer(1, bufSize, ctx.sampleRate);
        const data        = buf.getChannelData(0);
        for (let i = 0; i < bufSize; i++) data[i] = Math.random() * 2 - 1;
        const src  = ctx.createBufferSource();
        const gain = ctx.createGain();
        const filt = ctx.createBiquadFilter();
        src.buffer = buf;
        filt.type  = 'lowpass';
        filt.frequency.value = 400;
        src.connect(filt); filt.connect(gain); gain.connect(_getMaster());
        const now  = ctx.currentTime;
        const durS = dur / 1000;
        gain.gain.setValueAtTime(vol || 0.12, now);
        gain.gain.linearRampToValueAtTime(0, now + durS);
        src.start(now);
        src.stop(now + durS + 0.02);
    }

    // ── Sweep (pitch glide) ────────────────────────────────────────────────────
    function _sweep(f1, f2, dur, type, vol) {
        const ctx  = _getCtx();
        const osc  = ctx.createOscillator();
        const gain = ctx.createGain();
        osc.connect(gain); gain.connect(_getMaster());
        osc.type = type || 'sine';
        const now  = ctx.currentTime;
        const durS = dur / 1000;
        osc.frequency.setValueAtTime(f1, now);
        osc.frequency.linearRampToValueAtTime(f2, now + durS);
        gain.gain.setValueAtTime(vol || 0.14, now);
        gain.gain.linearRampToValueAtTime(0, now + durS);
        osc.start(now);
        osc.stop(now + durS + 0.02);
    }

    // ── Sound definitions ──────────────────────────────────────────────────────
    const _sounds = {

        // ── UI ────────────────────────────────────────────────────────────────
        click: function () {
            _tone(1200, 40, 'sine', 0.10, 2, 30);
        },
        success: function () {
            _tone(523, 100, 'sine', 0.14, 8, 60);
            setTimeout(function () { _tone(659, 120, 'sine', 0.16, 8, 70); }, 90);
            setTimeout(function () { _tone(784, 180, 'sine', 0.18, 8, 100); }, 190);
        },
        error: function () {
            _tone(200, 180, 'sawtooth', 0.14, 5, 120);
            _noise(180, 0.06);
            setTimeout(function () { _tone(150, 200, 'sawtooth', 0.12, 5, 150); }, 150);
        },
        notification: function () {
            _tone(880, 80, 'sine', 0.13, 5, 50);
            setTimeout(function () { _tone(1108, 120, 'sine', 0.15, 5, 80); }, 90);
        },
        modal_open: function () {
            // Soft sweep in — airy whoosh up
            _sweep(300, 520, 180, 'sine', 0.09);
            setTimeout(function () { _tone(660, 100, 'sine', 0.08, 5, 70); }, 160);
        },
        modal_close: function () {
            // Sweep down — gentle dismiss
            _sweep(520, 300, 150, 'sine', 0.08);
        },
        tab_switch: function () {
            // Double-click feel
            _tone(900, 35, 'sine', 0.09, 2, 25);
            setTimeout(function () { _tone(1100, 35, 'sine', 0.09, 2, 25); }, 45);
        },
        delete: function () {
            // Descending soft thud
            _sweep(400, 180, 120, 'triangle', 0.10);
            _noise(80, 0.04);
        },

        // ── Navigation ────────────────────────────────────────────────────────
        page_enter: function () {
            // Subtle arrival chime
            _tone(784, 80, 'sine', 0.08, 5, 55);
            setTimeout(function () { _tone(1047, 120, 'sine', 0.07, 5, 80); }, 75);
        },
        section_reveal: function () {
            // Very quiet rising whoosh
            _sweep(250, 420, 200, 'sine', 0.07);
        },

        // ── Classroom ─────────────────────────────────────────────────────────
        hand_raise: function () {
            _tone(660, 100, 'sine', 0.14, 8, 60);
            setTimeout(function () { _tone(880, 140, 'sine', 0.17, 8, 90); }, 110);
        },
        chat_message: function () {
            _tone(880, 90, 'triangle', 0.13, 5, 60);
        },
        user_joined: function () {
            _sweep(392, 523, 140, 'sine', 0.13);
            setTimeout(function () { _tone(659, 120, 'sine', 0.12, 5, 80); }, 150);
        },
        user_left: function () {
            _sweep(523, 392, 140, 'sine', 0.11);
            setTimeout(function () { _tone(330, 120, 'sine', 0.09, 5, 80); }, 150);
        },
        muted: function () {
            _tone(300, 80, 'triangle', 0.13, 5, 60);
            _noise(100, 0.05);
        },
        draw_permitted: function () {
            _tone(784, 80, 'sine', 0.13, 5, 50);
            setTimeout(function () { _tone(1047, 80, 'sine', 0.14, 5, 50); }, 85);
            setTimeout(function () { _tone(1319, 140, 'sine', 0.16, 5, 90); }, 170);
        },
        lesson_start: function () {
            // C4-E4-G4 major chord arpeggio up
            _tone(261, 160, 'sine', 0.14, 10, 100);
            setTimeout(function () { _tone(330, 160, 'sine', 0.14, 10, 100); }, 130);
            setTimeout(function () { _tone(392, 280, 'sine', 0.16, 10, 160); }, 260);
            setTimeout(function () { _tone(523, 350, 'sine', 0.18, 10, 220); }, 390);
        },
        lesson_end: function () {
            // G4-E4-C4 descending
            _tone(392, 160, 'sine', 0.15, 10, 100);
            setTimeout(function () { _tone(330, 160, 'sine', 0.14, 10, 100); }, 140);
            setTimeout(function () { _tone(261, 350, 'sine', 0.13, 10, 220); }, 280);
        },
        timer_warning: function () {
            // Urgent triple beep — last 10 seconds
            _tone(880, 60, 'square', 0.12, 2, 40);
            setTimeout(function () { _tone(880, 60, 'square', 0.13, 2, 40); }, 200);
            setTimeout(function () { _tone(1108, 100, 'square', 0.15, 2, 70); }, 400);
        },
        wb_clear: function () {
            // Erasing noise sweep
            _noise(220, 0.08);
            _sweep(600, 200, 220, 'triangle', 0.06);
        },
        file_shared: function () {
            // Paper rustle + chime
            _noise(80, 0.05);
            setTimeout(function () { _tone(784, 100, 'sine', 0.12, 5, 70); }, 60);
            setTimeout(function () { _tone(1047, 120, 'sine', 0.10, 5, 80); }, 160);
        },

        // ── Gamification ──────────────────────────────────────────────────────
        xp_gain: function () {
            _tone(880, 60, 'sine', 0.10, 3, 40);
        },
        level_up: function () {
            // Fanfare: C4-E4-G4-C5
            var notes = [
                [261, 120, 'sine', 0.15, 0],
                [330, 120, 'sine', 0.15, 110],
                [392, 120, 'sine', 0.15, 220],
                [523, 360, 'sine', 0.20, 330],
            ];
            notes.forEach(function (n) {
                setTimeout(function () { _tone(n[0], n[1], n[2], n[3]); }, n[4]);
            });
            // Harmony on last note
            setTimeout(function () { _tone(659, 360, 'sine', 0.12); }, 330);
            setTimeout(function () { _tone(784, 360, 'sine', 0.10); }, 330);
        },
        achievement: function () {
            // C5-E5-G5-E5-C6 sparkle
            var notes = [
                [523, 100, 0],
                [659, 100, 95],
                [784, 100, 190],
                [659, 100, 285],
                [1047, 300, 380],
            ];
            notes.forEach(function (n) {
                setTimeout(function () { _tone(n[0], n[1], 'sine', 0.16); }, n[2]);
            });
            // Shimmer
            setTimeout(function () { _tone(1319, 200, 'triangle', 0.08); }, 450);
            setTimeout(function () { _tone(1047, 300, 'triangle', 0.06); }, 550);
        },
        coin: function () {
            _tone(1319, 50, 'sine', 0.14, 2, 35);
            setTimeout(function () { _tone(1047, 80, 'triangle', 0.10, 2, 60); }, 55);
        },
        streak: function () {
            _tone(523, 80, 'sine', 0.13, 5, 55);
            setTimeout(function () { _tone(659, 80, 'sine', 0.14, 5, 55); }, 80);
            setTimeout(function () { _tone(784, 160, 'sine', 0.16, 5, 100); }, 160);
        },
        challenge_complete: function () {
            // Heroic: E4-G4-B4-E5 arpeggio
            _tone(330, 140, 'sine', 0.15, 8, 90);
            setTimeout(function () { _tone(392, 140, 'sine', 0.16, 8, 90); }, 130);
            setTimeout(function () { _tone(494, 140, 'sine', 0.17, 8, 90); }, 260);
            setTimeout(function () { _tone(659, 400, 'sine', 0.20, 8, 260); }, 390);
            // Sparkle on top
            setTimeout(function () { _tone(1319, 200, 'triangle', 0.07); }, 450);
        },
        daily_login: function () {
            // Morning chime: G-A-B
            _tone(392, 120, 'sine', 0.12, 8, 80);
            setTimeout(function () { _tone(440, 120, 'sine', 0.13, 8, 80); }, 110);
            setTimeout(function () { _tone(494, 250, 'sine', 0.15, 8, 160); }, 220);
        },

        // ── Quiz ──────────────────────────────────────────────────────────────
        quiz_start: function () {
            // 3-2-1 countdown beeps
            _tone(440, 100, 'sine', 0.15, 5, 70);
            setTimeout(function () { _tone(440, 100, 'sine', 0.15, 5, 70); }, 700);
            setTimeout(function () { _tone(880, 200, 'sine', 0.20, 5, 120); }, 1400);
        },
        quiz_correct: function () {
            _tone(659, 90, 'sine', 0.15, 5, 60);
            setTimeout(function () { _tone(880, 160, 'sine', 0.18, 5, 100); }, 95);
        },
        quiz_wrong: function () {
            _tone(220, 200, 'sawtooth', 0.13, 5, 150);
            setTimeout(function () { _tone(180, 250, 'sawtooth', 0.10, 5, 200); }, 180);
        },
        quiz_end: function () {
            // C major chord burst
            _tone(523, 400, 'sine', 0.14, 10, 250);
            _tone(659, 400, 'sine', 0.12, 10, 250);
            _tone(784, 400, 'sine', 0.10, 10, 250);
            setTimeout(function () { _tone(1047, 500, 'sine', 0.18, 10, 350); }, 350);
        },
        quiz_tick: function () {
            _tone(660, 40, 'square', 0.08, 2, 25);
        },
        time_up: function () {
            // Buzzer — descending sawtooth alarm
            _tone(440, 120, 'sawtooth', 0.14, 3, 80);
            _noise(120, 0.06);
            setTimeout(function () { _tone(330, 160, 'sawtooth', 0.12, 3, 110); }, 110);
            setTimeout(function () { _tone(220, 220, 'sawtooth', 0.10, 3, 160); }, 240);
        },
        quiz_bonus: function () {
            // Golden sparkle — high twinkling
            _tone(1047, 60, 'sine', 0.14, 2, 45);
            setTimeout(function () { _tone(1319, 60, 'sine', 0.16, 2, 45); }, 65);
            setTimeout(function () { _tone(1568, 60, 'sine', 0.17, 2, 45); }, 130);
            setTimeout(function () { _tone(2093, 180, 'sine', 0.18, 2, 120); }, 195);
        },
    };

    // ── Persistence ───────────────────────────────────────────────────────────
    var LS_SFX_KEY = 'ls_sfx';

    function _load() {
        try {
            var raw = localStorage.getItem(LS_SFX_KEY);
            return raw ? JSON.parse(raw) : null;
        } catch (e) { return null; }
    }

    function _save() {
        try {
            localStorage.setItem(LS_SFX_KEY, JSON.stringify({
                enabled : sfx.enabled,
                volume  : sfx.volume,
                prefs   : sfx.prefs,
            }));
        } catch (e) {}
    }

    // ── Public API ─────────────────────────────────────────────────────────────
    var sfx = {
        enabled : true,
        volume  : 0.75,
        prefs   : {
            ui           : true,
            navigation   : true,
            classroom    : true,
            gamification : true,
            quiz         : true,
        },

        // Category map — which prefs key each sound belongs to
        _cats: {
            click: 'ui', success: 'ui', error: 'ui', notification: 'ui',
            modal_open: 'ui', modal_close: 'ui', tab_switch: 'ui', delete: 'ui',
            page_enter: 'navigation', section_reveal: 'navigation',
            hand_raise: 'classroom', chat_message: 'classroom',
            user_joined: 'classroom', user_left: 'classroom',
            muted: 'classroom', draw_permitted: 'classroom',
            lesson_start: 'classroom', lesson_end: 'classroom',
            timer_warning: 'classroom', wb_clear: 'classroom', file_shared: 'classroom',
            xp_gain: 'gamification', level_up: 'gamification',
            achievement: 'gamification', coin: 'gamification', streak: 'gamification',
            challenge_complete: 'gamification', daily_login: 'gamification',
            quiz_start: 'quiz', quiz_correct: 'quiz', quiz_wrong: 'quiz',
            quiz_end: 'quiz', quiz_tick: 'quiz', time_up: 'quiz', quiz_bonus: 'quiz',
        },

        play: function (name) {
            if (!this.enabled) return;
            var cat = this._cats[name];
            if (cat && !this.prefs[cat]) return;
            if (!_sounds[name]) return;
            // Apply volume to master gain
            if (_masterGain) _masterGain.gain.value = this.volume;
            try { _sounds[name](); } catch (e) {}
        },

        setEnabled: function (v) {
            this.enabled = !!v;
            _save();
        },

        setVolume: function (v) {
            this.volume = Math.max(0, Math.min(1, parseFloat(v) || 0));
            if (_masterGain) _masterGain.gain.value = this.volume;
            _save();
        },

        setPref: function (cat, v) {
            if (this.prefs.hasOwnProperty(cat)) {
                this.prefs[cat] = !!v;
                _save();
            }
        },

        init: function () {
            var saved = _load();
            if (!saved) return;
            if (saved.enabled  !== undefined) this.enabled = saved.enabled;
            if (saved.volume   !== undefined) this.volume  = saved.volume;
            if (saved.prefs) {
                var self = this;
                Object.keys(saved.prefs).forEach(function (k) {
                    if (self.prefs.hasOwnProperty(k)) self.prefs[k] = saved.prefs[k];
                });
            }
        },
    };

    // Attach to LS namespace
    window.LS = window.LS || {};
    window.LS.sfx = sfx;

    // Auto-init on load
    sfx.init();

})();
