// ══════════════════════════════════════════════════════════════
//  Audio — процедурные SFX + фоновая музыка (Web Audio API)
// ══════════════════════════════════════════════════════════════
const Audio = {
    ctx: null,
    _master: null,
    _musicGain: null,   // отдельный узел для музыки — глушится при смене темы
    _menuBgm: null,     // HTML-аудио для mainmenu.mp3
    _musicSeqId: 0,
    currentTheme: null,
    muted: false,
    _volume: 0.6,
    _lastStep: 0,

    // ── Инициализация ─────────────────────────────────────────
    init() {
        // Создать HTML-элемент для MP3 меню (не требует AudioContext)
        if (!this._menuBgm) {
            const el = document.createElement('audio');
            el.src    = 'mainmenu.mp3';
            el.loop   = true;
            el.volume = this._volume;
            el.muted  = this.muted;
            this._menuBgm = el;
        }
        if (this.ctx) return;
        try {
            this.ctx = new (window.AudioContext || window.webkitAudioContext)();
            this._master = this.ctx.createGain();
            this._master.gain.value = this._volume;
            this._master.connect(this.ctx.destination);
            // Отдельный гейн для музыкальных нот (мгновенно глушится при смене темы)
            this._musicGain = this.ctx.createGain();
            this._musicGain.gain.value = 1;
            this._musicGain.connect(this._master);
        } catch(e) { console.warn('Web Audio недоступен:', e); }
    },

    toggleMute() {
        if (!this.ctx) return;
        this.muted = !this.muted;
        this._master.gain.value = this.muted ? 0 : this._volume;
        if (this._menuBgm) this._menuBgm.muted = this.muted;
        const btn = document.getElementById('btn-mute');
        if (btn) btn.textContent = this.muted ? '🔇' : '🔊';
    },

    setVolume(v) {
        this._volume = Math.max(0, Math.min(1, v));
        if (this._master && !this.muted) this._master.gain.value = this._volume;
        if (this._menuBgm) this._menuBgm.volume = this._volume;
    },

    // ── Низкоуровневый синтез ──────────────────────────────────
    _note(freq, startTime, dur, type, gainVal, dest, filterFreq) {
        if (!this.ctx || this.muted) return;
        const osc = this.ctx.createOscillator();
        const g   = this.ctx.createGain();
        osc.type = type || 'sine';
        osc.frequency.setValueAtTime(freq, startTime);
        const atk = 0.01;
        const rel = Math.min(dur * 0.4, 0.15);
        g.gain.setValueAtTime(0, startTime);
        g.gain.linearRampToValueAtTime(gainVal, startTime + atk);
        g.gain.setValueAtTime(gainVal, startTime + dur - rel);
        g.gain.exponentialRampToValueAtTime(0.0001, startTime + dur);
        if (filterFreq) {
            const f = this.ctx.createBiquadFilter();
            f.type = 'lowpass';
            f.frequency.value = filterFreq;
            osc.connect(f); f.connect(g);
        } else {
            osc.connect(g);
        }
        g.connect(dest || this._master);
        osc.start(startTime);
        osc.stop(startTime + dur + 0.01);
    },

    _noise(startTime, dur, gainVal, filterFreq) {
        if (!this.ctx || this.muted) return;
        const bufLen = Math.ceil(this.ctx.sampleRate * dur);
        const buf = this.ctx.createBuffer(1, bufLen, this.ctx.sampleRate);
        const data = buf.getChannelData(0);
        for (let i = 0; i < bufLen; i++) data[i] = Math.random() * 2 - 1;
        const src = this.ctx.createBufferSource();
        src.buffer = buf;
        const g = this.ctx.createGain();
        g.gain.setValueAtTime(gainVal, startTime);
        g.gain.exponentialRampToValueAtTime(0.0001, startTime + dur);
        const f = this.ctx.createBiquadFilter();
        f.type = 'bandpass';
        f.frequency.value = filterFreq || 800;
        f.Q.value = 0.5;
        src.connect(f); f.connect(g); g.connect(this._master);
        src.start(startTime); src.stop(startTime + dur + 0.01);
    },

    // ── SFX ───────────────────────────────────────────────────
    playHit(crit) {
        if (!this.ctx) return;
        const t = this.ctx.currentTime;
        const mul = crit ? 1.6 : 1;
        // удар — нисходящий шум + низкий удар
        this._noise(t, 0.08, crit ? 0.3 : 0.18, 1200 * mul);
        this._note(180 * mul, t, 0.1, 'sawtooth', 0.18, null, 400);
        this._note(90,  t + 0.04, 0.12, 'sine', 0.25, null, 300);
        if (crit) {
            // дополнительный хруст для крита
            this._note(440, t, 0.05, 'square', 0.12);
            this._note(330, t + 0.05, 0.08, 'square', 0.1);
        }
    },

    playSpell(type) {
        if (!this.ctx) return;
        const t = this.ctx.currentTime;
        switch (type) {
            case 'fire': {
                // восходящий пламенный свист
                const osc = this.ctx.createOscillator();
                const g   = this.ctx.createGain();
                osc.type = 'sawtooth';
                osc.frequency.setValueAtTime(200, t);
                osc.frequency.exponentialRampToValueAtTime(900, t + 0.35);
                g.gain.setValueAtTime(0.15, t);
                g.gain.exponentialRampToValueAtTime(0.0001, t + 0.35);
                osc.connect(g); g.connect(this._master);
                osc.start(t); osc.stop(t + 0.36);
                this._noise(t, 0.25, 0.1, 1800);
                break;
            }
            case 'ice': {
                // нисходящий кристальный звон
                [1046, 784, 523, 392].forEach((f, i) => {
                    this._note(f, t + i * 0.06, 0.18, 'sine', 0.12);
                });
                break;
            }
            case 'heal': {
                // мажорный аккорд
                [261, 329, 392, 523].forEach((f, i) => {
                    this._note(f, t + i * 0.04, 0.4, 'sine', 0.1);
                });
                break;
            }
            case 'magic':
            default: {
                // арпеджио вверх-вниз
                const notes = [261, 329, 392, 523, 392, 329];
                notes.forEach((f, i) => {
                    this._note(f, t + i * 0.07, 0.1, 'triangle', 0.12);
                });
                break;
            }
        }
    },

    playStep() {
        if (!this.ctx) return;
        const now = Date.now();
        if (now - this._lastStep < 250) return; // дроссель
        this._lastStep = now;
        const t = this.ctx.currentTime;
        this._noise(t, 0.04, 0.04, 300);
        this._note(80, t, 0.04, 'sine', 0.06, null, 200);
    },

    playLevelUp() {
        if (!this.ctx) return;
        const t = this.ctx.currentTime;
        // восходящее арпеджио C-E-G-C'
        [261, 329, 392, 523].forEach((f, i) => {
            this._note(f, t + i * 0.12, 0.2, 'triangle', 0.15);
            this._note(f * 2, t + i * 0.12 + 0.06, 0.1, 'sine', 0.07);
        });
        // завершение — аккорд
        [523, 659, 784].forEach(f => {
            this._note(f, t + 0.6, 0.5, 'sine', 0.1);
        });
    },

    playVictory() {
        if (!this.ctx) return;
        const t = this.ctx.currentTime;
        // небольшая фанфара
        const mel = [392, 392, 392, 523, 392, 523, 659];
        const durs = [0.15, 0.15, 0.15, 0.4, 0.15, 0.15, 0.6];
        let pos = 0;
        mel.forEach((f, i) => {
            this._note(f, t + pos, durs[i] * 0.9, 'triangle', 0.18);
            this._note(f / 2, t + pos, durs[i] * 0.9, 'sine', 0.08);
            pos += durs[i];
        });
    },

    playDeath() {
        if (!this.ctx) return;
        const t = this.ctx.currentTime;
        // нисходящий минорный аккорд
        [220, 261, 311].forEach((f, i) => {
            const osc = this.ctx.createOscillator();
            const g   = this.ctx.createGain();
            osc.type = 'sine';
            osc.frequency.setValueAtTime(f, t);
            osc.frequency.exponentialRampToValueAtTime(f * 0.5, t + 1.2);
            g.gain.setValueAtTime(0.15, t);
            g.gain.exponentialRampToValueAtTime(0.0001, t + 1.2);
            osc.connect(g); g.connect(this._master);
            osc.start(t); osc.stop(t + 1.25);
        });
        this._noise(t, 0.3, 0.08, 200);
    },

    playOpenChest() {
        if (!this.ctx) return;
        const t = this.ctx.currentTime;
        // позвякивание
        [784, 1046, 1318, 1046, 1318, 1568].forEach((f, i) => {
            this._note(f, t + i * 0.07, 0.15, 'triangle', 0.1);
        });
    },

    // ── Музыкальные темы ──────────────────────────────────────
    THEMES: {
        // ── Главное меню: тёмная эпическая баллада, A-минор ──
        menu: {
            bpm: 58,
            notes: [
                // Фраза 1 — вступление (A3 → E4)
                [220, 2,   0.070],  // A3
                [0,   0.5, 0    ],
                [196, 0.5, 0.055],  // G3
                [220, 1,   0.065],  // A3
                [0,   0.5, 0    ],
                [261, 1.5, 0.070],  // C4
                [329, 2,   0.080],  // E4

                // Фраза 2 — подъём к кульминации (G4 → A4)
                [0,   0.5, 0    ],
                [392, 1,   0.075],  // G4
                [440, 1.5, 0.090],  // A4 — кульминация

                // Фраза 3 — спуск (G4 → C4)
                [0,   0.5, 0    ],
                [392, 0.5, 0.065],  // G4
                [349, 0.5, 0.060],  // F4
                [329, 1,   0.070],  // E4
                [293, 0.5, 0.060],  // D4
                [261, 2,   0.070],  // C4

                // Фраза 4 — разрешение (B3 → A3)
                [0,   0.5, 0    ],
                [246, 0.5, 0.055],  // B3
                [220, 3,   0.080],  // A3 — финал
                [0,   2,   0    ],
            ],
            bass: [
                [55, 4, 0.085],  // A1 — тоника
                [65, 4, 0.075],  // C2 — параллельный мажор
                [82, 4, 0.080],  // E2 — доминанта
                [73, 4, 0.075],  // D2 — субдоминанта
                [55, 6, 0.080],  // A1 — разрешение
            ],
        },

        village: {
            bpm: 80,
            notes: [
                // C мажорная пентатоника: C D E G A C'
                [261,1,0.055],[294,0.5,0.045],[329,1,0.055],[0,0.5,0],
                [392,0.5,0.045],[440,1,0.065],[523,1,0.055],[0,0.5,0],
                [440,0.5,0.04],[392,0.5,0.04],[329,1,0.05],[0,0.5,0],
                [294,0.5,0.04],[261,1.5,0.055],[0,2,0],
            ],
            bass: [
                [65,2,0.06],[65,2,0.05],[73,2,0.06],[65,2,0.05],
            ]
        },
        forest: {
            bpm: 100,
            notes: [
                // a минор арпеджио
                [220,0.5,0.06],[261,0.5,0.05],[329,0.5,0.06],[261,0.5,0.05],
                [247,0.5,0.06],[294,0.5,0.05],[370,0.5,0.065],[294,0.5,0.05],
                [220,0.5,0.06],[261,0.5,0.055],[329,1,0.06],[0,1,0],
                [196,0.5,0.055],[220,0.5,0.05],[261,0.5,0.06],[220,0.5,0.05],
                [196,0.5,0.055],[174,0.5,0.05],[220,1.5,0.065],[0,1,0],
            ],
            bass: [
                [55,1,0.07],[55,1,0.06],[62,1,0.07],[55,1,0.06],
                [49,1,0.07],[49,1,0.06],[55,2,0.065],
            ]
        },
        dungeon: {
            bpm: 50,
            notes: [
                // мрачный хроматический дрон
                [130,3,0.07],[0,1,0],[116,2,0.06],[0,2,0],
                [138,3,0.065],[0,1,0],[123,2,0.06],[0,3,0],
                [146,2,0.07],[130,2,0.065],[0,4,0],
            ],
            bass: [
                [32,4,0.09],[32,4,0.08],[36,4,0.09],[32,8,0.07],
            ]
        },
        swamp: {
            bpm: 60,
            notes: [
                // жуткая хроматика, нерегулярный ритм
                [185,0.5,0.05],[196,1,0.06],[0,0.5,0],[174,0.5,0.05],
                [0,1.5,0],[185,0.5,0.065],[207,1,0.055],[185,0.5,0.05],
                [0,2,0],[174,0.5,0.06],[164,2,0.055],[0,2,0],
                [155,0.5,0.05],[0,0.5,0],[164,1.5,0.065],[0,3,0],
            ],
            bass: [
                [46,3,0.08],[0,1,0],[41,3,0.07],[0,2,0],[43,4,0.075],[0,3,0],
            ]
        },
        mountain: {
            bpm: 55,
            notes: [
                // медленный эпичный минор
                [220,2,0.07],[196,1,0.06],[174,1,0.065],[0,1,0],
                [185,2,0.07],[220,2,0.065],[0,2,0],
                [261,1.5,0.075],[246,0.5,0.065],[220,2,0.07],[0,1,0],
                [196,1,0.065],[174,1,0.06],[185,3,0.07],[0,2,0],
            ],
            bass: [
                [55,4,0.09],[46,4,0.085],[49,4,0.09],[55,4,0.085],
            ]
        },
        combat: {
            bpm: 145,
            notes: [
                // быстрый staccato минор
                [220,0.5,0.08],[0,0.5,0],[261,0.5,0.075],[0,0.5,0],
                [196,0.5,0.08],[220,1,0.085],[0,0.5,0],
                [165,0.5,0.075],[185,0.5,0.08],[0,0.5,0],[220,0.5,0.075],
                [0,0.5,0],[246,0.5,0.08],[220,1,0.085],[0,1,0],
                [174,0.5,0.075],[196,0.5,0.08],[0,0.5,0],[220,0.5,0.075],
                [0,0.5,0],[196,0.5,0.08],[174,1.5,0.085],[0,1,0],
            ],
            bass: [
                [55,0.5,0.1],[0,0.5,0],[55,0.5,0.09],[0,0.5,0],
                [55,0.5,0.1],[0,0.5,0],[55,0.5,0.09],[0,0.5,0],
                [49,0.5,0.1],[0,0.5,0],[49,0.5,0.09],[0,0.5,0],
                [49,0.5,0.1],[0,0.5,0],[55,1,0.095],[0,1,0],
            ]
        },
        ruins: {
            bpm: 45,
            notes: [
                // мрачная эолийская гамма, редкие ноты — атмосфера разрушенного замка
                [110,3,0.055],[0,2,0],[98,2,0.05],[0,3,0],
                [116,3,0.06],[110,2,0.05],[0,3,0],
                [92,2,0.055],[0,2,0],[104,4,0.06],[0,4,0],
                [110,2,0.05],[0,2,0],[98,3,0.055],[0,3,0],
            ],
            bass: [
                [27,6,0.075],[0,2,0],[24,6,0.07],[0,4,0],
                [29,6,0.075],[0,2,0],[27,4,0.065],[0,6,0],
            ]
        },
        cave: {
            bpm: 55,
            notes: [
                // тёмная пещерная атмосфера
                [138,3,0.06],[0,2,0],[123,2,0.055],[0,3,0],
                [146,3,0.065],[138,2,0.055],[0,4,0],
                [116,2,0.06],[0,2,0],[130,3,0.06],[0,3,0],
            ],
            bass: [
                [34,5,0.08],[0,3,0],[30,5,0.075],[0,4,0],
                [36,5,0.08],[0,3,0],[34,4,0.07],[0,5,0],
            ]
        },
        abyss: {
            bpm: 40,
            notes: [
                // зловещий хроматический дрейф
                [41,6,0.09],[0,2,0],[37,5,0.08],[0,3,0],
                [44,4,0.07],[0,4,0],[39,6,0.09],[0,2,0],
                [34,5,0.075],[0,3,0],[41,4,0.08],[0,4,0],
            ],
            bass: [
                [20,8,0.1],[0,4,0],[18,8,0.09],[0,4,0],
                [22,8,0.1],[0,4,0],[20,6,0.09],[0,6,0],
            ]
        },
    },

    // ── Воспроизведение музыки ────────────────────────────────
    playTheme(name) {
        if (this.currentTheme === name) return;

        // Тема 'menu' управляется через #menu-bgm в HTML напрямую
        if (name === 'menu') {
            // Остановить процедурную музыку при переходе в меню
            if (this._musicGain) {
                this._musicGain.disconnect();
                if (this.ctx) {
                    this._musicGain = this.ctx.createGain();
                    this._musicGain.gain.value = 1;
                    this._musicGain.connect(this._master);
                }
            }
            this._musicSeqId++;
            this.currentTheme = 'menu';
            return;
        }

        // ── Процедурные темы — требуют AudioContext ─────────────
        if (!this.ctx) return;

        // Остановить MP3 меню
        if (this._menuBgm && !this._menuBgm.paused) {
            this._menuBgm.pause();
            this._menuBgm.currentTime = 0;
        }

        // Отключить старый musicGain — мгновенно глушит все запланированные ноты
        if (this._musicGain) {
            this._musicGain.disconnect();
        }
        this._musicGain = this.ctx.createGain();
        this._musicGain.gain.value = 1;
        this._musicGain.connect(this._master);

        this.currentTheme = name;
        this._musicSeqId++;
        const id = this._musicSeqId;
        const theme = this.THEMES[name];
        if (!theme) return;
        this._scheduleMelody(theme.notes, theme.bpm, id, false);
        if (theme.bass) this._scheduleMelody(theme.bass, theme.bpm, id, true);
    },

    stopMusic() {
        if (this._menuBgm && !this._menuBgm.paused) {
            this._menuBgm.pause();
            this._menuBgm.currentTime = 0;
        }
        if (this._musicGain) {
            this._musicGain.disconnect();
            if (this.ctx) {
                this._musicGain = this.ctx.createGain();
                this._musicGain.gain.value = 1;
                this._musicGain.connect(this._master);
            }
        }
        this._musicSeqId++;
        this.currentTheme = null;
    },

    _scheduleMelody(notes, bpm, loopId, isBass) {
        if (this._musicSeqId !== loopId) return;
        if (!this.ctx || this.muted) {
            // музыка заглушена — перепланировать через секунду
            const beat = 60 / bpm;
            const totalMs = notes.reduce((s, n) => s + n[1], 0) * beat * 1000;
            setTimeout(() => this._scheduleMelody(notes, bpm, loopId, isBass), totalMs);
            return;
        }
        let t = this.ctx.currentTime + 0.06; // совпадает с задержкой _musicGain восстановления
        const beat = 60 / bpm;
        notes.forEach(([freq, beats, gainVal]) => {
            if (freq > 0 && gainVal > 0) {
                const dur = beats * beat * 0.88;
                // ноты идут через _musicGain, а не напрямую в _master
                this._note(freq, t, dur, isBass ? 'triangle' : 'sine', gainVal,
                           this._musicGain || null, isBass ? 300 : null);
            }
            t += beats * beat;
        });
        const totalMs = notes.reduce((s, n) => s + n[1], 0) * beat * 1000;
        setTimeout(() => this._scheduleMelody(notes, bpm, loopId, isBass), totalMs + 30);
    },
};
