const AUDIO_PREFS_KEY = 'caminoacasa_audio';

const audioCtx = {
    ctx: null,
    masterGain: null,
    musicGain: null,
    sfxGain: null,
    muted: false,
    initialized: false,
    musicAmbience: null,
    musicLoop: null,
    schedulerId: null,
    nextNoteTime: 0,
    step: 0,
    spanishVoice: null,
    voicesReady: false,
    lastSpeakAt: 0
};

const VOICE_PROFILES = {
    trilero:    { rate: 1.20, pitch: 1.10, vol: 1.0 },
    flyers:     { rate: 1.15, pitch: 1.05, vol: 1.0 },
    ong:        { rate: 1.00, pitch: 1.00, vol: 1.0 },
    selfies:    { rate: 1.20, pitch: 1.20, vol: 1.0 },
    carterista: { rate: 1.30, pitch: 0.95, vol: 0.8 },
    predicador: { rate: 0.85, pitch: 0.80, vol: 1.0 },
    borracho:   { rate: 0.80, pitch: 0.75, vol: 1.0 },
    scooter:    { rate: 1.40, pitch: 1.30, vol: 1.0 },
    charlatan:  { rate: 1.30, pitch: 1.15, vol: 1.0 },
    mendigo:    { rate: 0.90, pitch: 0.85, vol: 1.0 },
    amego:      { rate: 1.25, pitch: 1.10, vol: 1.0 }
};

const MUSIC_TRACKS = {
    dia: {
        bpm: 102,
        scale: [0, 2, 4, 7, 9],
        rootHz: 261.63,
        bassPattern:  [0, null, 4, null, 0, null, 4, null,  0, null, 2, null, 4, null, 2, null],
        leadPattern:  [12, null, 14, null, 16, null, 14, 12,  19, null, 16, null, 14, null, 12, null],
        drum:         [1,0,0,0, 1,0,1,0, 1,0,0,0, 1,0,1,0],
        padFreqs: [130.81, 196.00],
        padFilter: 1100,
        leadVol: 0.06,
        bassVol: 0.16
    },
    tarde: {
        bpm: 86,
        scale: [0, 2, 3, 5, 7, 8, 10],
        rootHz: 220.00,
        bassPattern:  [0, null, null, null, 4, null, null, null, 5, null, null, null, 2, null, null, null],
        leadPattern:  [null, 12, null, 10, null, 8, 7, null, null, 12, null, 14, null, 12, 10, null],
        drum:         [1,0,0,0, 0,0,1,0, 1,0,0,0, 0,0,1,0],
        padFreqs: [110.00, 164.81],
        padFilter: 750,
        leadVol: 0.05,
        bassVol: 0.18
    },
    noche: {
        bpm: 72,
        scale: [0, 2, 3, 5, 7, 8, 10],
        rootHz: 174.61,
        bassPattern:  [0, null, null, null, null, null, null, null, 4, null, null, null, null, null, null, null],
        leadPattern:  [null, null, null, 12, null, null, 10, null, null, null, null, 15, null, null, 12, null],
        drum:         [0,0,0,0, 0,0,1,0, 0,0,0,0, 0,0,1,0],
        padFreqs: [87.31, 130.81],
        padFilter: 520,
        leadVol: 0.04,
        bassVol: 0.20
    }
};

function initAudio() {
    if (audioCtx.initialized) return;
    audioCtx.initialized = true;
    _loadAudioPrefs();
    _setupVoices();
    _installAudioToggleUI();
    _installButtonClickSFX();

    const unlock = () => {
        _ensureAudioContext();
        document.removeEventListener('pointerdown', unlock, true);
        document.removeEventListener('keydown', unlock, true);
        document.removeEventListener('touchstart', unlock, true);
    };
    document.addEventListener('pointerdown', unlock, true);
    document.addEventListener('keydown', unlock, true);
    document.addEventListener('touchstart', unlock, true);
}

function _loadAudioPrefs() {
    try {
        const raw = localStorage.getItem(AUDIO_PREFS_KEY);
        if (!raw) return;
        const data = JSON.parse(raw);
        if (data && typeof data.muted === 'boolean') audioCtx.muted = data.muted;
    } catch (e) {}
}

function _saveAudioPrefs() {
    try {
        localStorage.setItem(AUDIO_PREFS_KEY, JSON.stringify({ muted: audioCtx.muted }));
    } catch (e) {}
}

function _ensureAudioContext() {
    if (audioCtx.ctx) {
        if (audioCtx.ctx.state === 'suspended') {
            audioCtx.ctx.resume().catch(() => {});
        }
        return audioCtx.ctx;
    }
    const Ctor = window.AudioContext || window.webkitAudioContext;
    if (!Ctor) return null;
    const ctx = new Ctor();
    audioCtx.ctx = ctx;

    audioCtx.masterGain = ctx.createGain();
    audioCtx.masterGain.gain.value = audioCtx.muted ? 0 : 1;
    audioCtx.masterGain.connect(ctx.destination);

    audioCtx.musicGain = ctx.createGain();
    audioCtx.musicGain.gain.value = 0.30;
    audioCtx.musicGain.connect(audioCtx.masterGain);

    audioCtx.sfxGain = ctx.createGain();
    audioCtx.sfxGain.gain.value = 0.55;
    audioCtx.sfxGain.connect(audioCtx.masterGain);

    return ctx;
}

function _setupVoices() {
    if (!('speechSynthesis' in window)) return;
    const pick = () => {
        const list = window.speechSynthesis.getVoices();
        if (!list || !list.length) return;
        const es = list.find(v => /es[-_]ES/i.test(v.lang)) ||
                   list.find(v => /^es/i.test(v.lang)) ||
                   null;
        audioCtx.spanishVoice = es;
        audioCtx.voicesReady = true;
    };
    pick();
    try { window.speechSynthesis.onvoiceschanged = pick; } catch (e) {}
}

function _installAudioToggleUI() {
    const btn = document.getElementById('audio-toggle');
    if (!btn) return;
    _renderAudioBtn();
    btn.addEventListener('click', (e) => {
        e.preventDefault();
        e.stopPropagation();
        toggleAudioMute();
    });
}

function _renderAudioBtn() {
    const btn = document.getElementById('audio-toggle');
    if (!btn) return;
    btn.textContent = audioCtx.muted ? '🔇' : '🔊';
    btn.setAttribute('aria-label', audioCtx.muted ? 'Activar sonido' : 'Silenciar');
    btn.setAttribute('aria-pressed', String(audioCtx.muted));
}

function toggleAudioMute() {
    audioCtx.muted = !audioCtx.muted;
    _saveAudioPrefs();
    _renderAudioBtn();
    if (audioCtx.masterGain && audioCtx.ctx) {
        const t = audioCtx.ctx.currentTime;
        audioCtx.masterGain.gain.cancelScheduledValues(t);
        audioCtx.masterGain.gain.linearRampToValueAtTime(audioCtx.muted ? 0 : 1, t + 0.05);
    }
    if (audioCtx.muted && 'speechSynthesis' in window) {
        try { window.speechSynthesis.cancel(); } catch (e) {}
    }
    if (!audioCtx.muted && audioCtx.musicAmbience && !audioCtx.musicLoop) {
        const ambient = audioCtx.musicAmbience;
        audioCtx.musicAmbience = null;
        startMusic(ambient);
    }
}

function isAudioMuted() {
    return audioCtx.muted;
}

function _installButtonClickSFX() {
    document.addEventListener('click', (e) => {
        const t = e.target;
        if (!t || !t.closest) return;
        const btn = t.closest('.btn-primary, .btn-secondary');
        if (btn) playSfx('click');
    }, true);
}

// --- SFX ---

function playSfx(name) {
    if (audioCtx.muted) return;
    const ctx = _ensureAudioContext();
    if (!ctx) return;
    const t = ctx.currentTime;
    switch (name) {
        case 'click':            _sfxBlip(t, 880, 0.06, 0.18); break;
        case 'powerup':          _sfxArp(t, [523, 659, 784, 1047], 0.07, 0.22); break;
        case 'lifeLost':         _sfxFall(t); break;
        case 'levelComplete':    _sfxChime(t, [659, 784, 988, 1175]); break;
        case 'victory':          _sfxFanfare(t); break;
        case 'gameOver':         _sfxGameOver(t); break;
        case 'encounterStart':   _sfxAlert(t); break;
        case 'encounterSuccess': _sfxArp(t, [392, 523, 659, 784], 0.08, 0.20); break;
        case 'encounterFail':    _sfxBuzz(t); break;
    }
}

function _sfxBlip(t, freq, dur, vol) {
    const ctx = audioCtx.ctx;
    const o = ctx.createOscillator();
    const g = ctx.createGain();
    o.type = 'square';
    o.frequency.setValueAtTime(freq, t);
    g.gain.setValueAtTime(0.0001, t);
    g.gain.exponentialRampToValueAtTime(vol, t + 0.005);
    g.gain.exponentialRampToValueAtTime(0.0001, t + dur);
    o.connect(g).connect(audioCtx.sfxGain);
    o.start(t);
    o.stop(t + dur + 0.02);
}

function _sfxArp(t, freqs, step, vol) {
    for (let i = 0; i < freqs.length; i++) {
        _sfxBlip(t + i * step, freqs[i], step + 0.05, vol);
    }
}

function _sfxFall(t) {
    const ctx = audioCtx.ctx;
    const o = ctx.createOscillator();
    const g = ctx.createGain();
    o.type = 'sawtooth';
    o.frequency.setValueAtTime(440, t);
    o.frequency.exponentialRampToValueAtTime(110, t + 0.6);
    g.gain.setValueAtTime(0.0001, t);
    g.gain.exponentialRampToValueAtTime(0.30, t + 0.04);
    g.gain.exponentialRampToValueAtTime(0.0001, t + 0.7);
    o.connect(g).connect(audioCtx.sfxGain);
    o.start(t);
    o.stop(t + 0.75);
}

function _sineNote(t, freq, dur, vol) {
    const ctx = audioCtx.ctx;
    const o = ctx.createOscillator();
    const g = ctx.createGain();
    o.type = 'sine';
    o.frequency.setValueAtTime(freq, t);
    g.gain.setValueAtTime(0.0001, t);
    g.gain.exponentialRampToValueAtTime(vol, t + 0.02);
    g.gain.exponentialRampToValueAtTime(0.0001, t + dur);
    o.connect(g).connect(audioCtx.sfxGain);
    o.start(t);
    o.stop(t + dur + 0.02);
}

function _sfxChime(t, freqs) {
    for (let i = 0; i < freqs.length; i++) {
        _sineNote(t + i * 0.09, freqs[i], 0.32, 0.22);
    }
}

function _sfxFanfare(t) {
    const seq = [[523, 0.14], [659, 0.14], [784, 0.14], [1047, 0.45]];
    let cur = t;
    for (const item of seq) {
        _sineNote(cur, item[0], item[1] + 0.05, 0.26);
        _sineNote(cur, item[0] * 2, item[1] + 0.05, 0.10);
        cur += item[1];
    }
}

function _sfxGameOver(t) {
    _sineNote(t,        220, 0.50, 0.26);
    _sineNote(t + 0.18, 196, 0.55, 0.24);
    _sineNote(t + 0.36, 165, 0.75, 0.24);
}

function _sfxAlert(t) {
    _sfxBlip(t,        1320, 0.08, 0.26);
    _sfxBlip(t + 0.10, 1320, 0.08, 0.26);
    _sfxBlip(t + 0.20, 1760, 0.12, 0.26);
}

function _sfxBuzz(t) {
    const ctx = audioCtx.ctx;
    const o = ctx.createOscillator();
    const g = ctx.createGain();
    o.type = 'square';
    o.frequency.setValueAtTime(140, t);
    g.gain.setValueAtTime(0.0001, t);
    g.gain.exponentialRampToValueAtTime(0.22, t + 0.02);
    g.gain.exponentialRampToValueAtTime(0.0001, t + 0.42);
    o.connect(g).connect(audioCtx.sfxGain);
    o.start(t);
    o.stop(t + 0.5);
}

// --- Música procedural ---

function startMusic(ambience) {
    if (audioCtx.musicAmbience === ambience && audioCtx.musicLoop) return;
    stopMusic();
    audioCtx.musicAmbience = ambience;
    if (audioCtx.muted) return;
    const ctx = _ensureAudioContext();
    if (!ctx) return;
    const track = MUSIC_TRACKS[ambience] || MUSIC_TRACKS.dia;
    const stepDur = 60.0 / track.bpm / 4;
    audioCtx.nextNoteTime = ctx.currentTime + 0.1;
    audioCtx.step = 0;
    audioCtx.musicLoop = { track, stepDur, padNode: null };
    _startPad(track);
    _scheduler();
}

function stopMusic() {
    if (audioCtx.schedulerId) {
        clearTimeout(audioCtx.schedulerId);
        audioCtx.schedulerId = null;
    }
    if (audioCtx.musicLoop && audioCtx.musicLoop.padNode && audioCtx.ctx) {
        const pad = audioCtx.musicLoop.padNode;
        const t = audioCtx.ctx.currentTime;
        try {
            pad.gainNode.gain.cancelScheduledValues(t);
            pad.gainNode.gain.linearRampToValueAtTime(0.0001, t + 0.4);
            pad.oscs.forEach(o => o.stop(t + 0.5));
        } catch (e) {}
    }
    audioCtx.musicLoop = null;
}

function _startPad(track) {
    const ctx = audioCtx.ctx;
    const t = ctx.currentTime;
    const oscs = [];
    const g = ctx.createGain();
    g.gain.setValueAtTime(0.0001, t);
    g.gain.linearRampToValueAtTime(0.10, t + 1.5);
    const filter = ctx.createBiquadFilter();
    filter.type = 'lowpass';
    filter.frequency.value = track.padFilter;

    for (const f of track.padFreqs) {
        const o = ctx.createOscillator();
        o.type = 'sine';
        o.frequency.value = f;
        o.connect(g);
        o.start(t);
        oscs.push(o);
    }
    g.connect(filter).connect(audioCtx.musicGain);
    audioCtx.musicLoop.padNode = { oscs, gainNode: g };
}

function _scheduler() {
    const loop = audioCtx.musicLoop;
    if (!loop) return;
    const ctx = audioCtx.ctx;
    const horizon = 0.20;
    while (audioCtx.nextNoteTime < ctx.currentTime + horizon) {
        if (!audioCtx.muted) _scheduleStep(loop, audioCtx.step, audioCtx.nextNoteTime);
        audioCtx.nextNoteTime += loop.stepDur;
        audioCtx.step = (audioCtx.step + 1) % 16;
    }
    audioCtx.schedulerId = setTimeout(_scheduler, 50);
}

function _scheduleStep(loop, step, when) {
    const track = loop.track;
    const bn = track.bassPattern[step];
    if (bn !== null && bn !== undefined) {
        const f = _scaleNote(track, bn) / 2;
        _musicNote('triangle', f, when, loop.stepDur * 1.6, track.bassVol);
    }
    const ln = track.leadPattern[step];
    if (ln !== null && ln !== undefined) {
        const f = _scaleNote(track, ln);
        _musicNote('square', f, when, loop.stepDur * 0.85, track.leadVol);
    }
    if (track.drum[step]) {
        _musicDrum(when);
    }
}

function _scaleNote(track, scaleStep) {
    const scale = track.scale;
    const len = scale.length;
    const oct = Math.floor(scaleStep / len);
    const idx = ((scaleStep % len) + len) % len;
    const semitones = oct * 12 + scale[idx];
    return track.rootHz * Math.pow(2, semitones / 12);
}

function _musicNote(type, freq, when, dur, vol) {
    const ctx = audioCtx.ctx;
    const o = ctx.createOscillator();
    const g = ctx.createGain();
    o.type = type;
    o.frequency.setValueAtTime(freq, when);
    g.gain.setValueAtTime(0.0001, when);
    g.gain.exponentialRampToValueAtTime(vol, when + 0.012);
    g.gain.exponentialRampToValueAtTime(0.0001, when + dur);
    o.connect(g).connect(audioCtx.musicGain);
    o.start(when);
    o.stop(when + dur + 0.05);
}

function _musicDrum(when) {
    const ctx = audioCtx.ctx;
    const length = Math.floor(ctx.sampleRate * 0.08);
    const buf = ctx.createBuffer(1, length, ctx.sampleRate);
    const data = buf.getChannelData(0);
    for (let i = 0; i < length; i++) data[i] = (Math.random() * 2 - 1) * (1 - i / length);
    const src = ctx.createBufferSource();
    src.buffer = buf;
    const g = ctx.createGain();
    g.gain.value = 0.10;
    const f = ctx.createBiquadFilter();
    f.type = 'lowpass';
    f.frequency.value = 220;
    src.connect(f).connect(g).connect(audioCtx.musicGain);
    src.start(when);
}

// --- Voces ---

function speakLine(text, npcId, opts) {
    if (audioCtx.muted) return;
    if (!('speechSynthesis' in window)) return;
    if (!text) return;
    opts = opts || {};
    const now = performance.now();
    const minGap = opts.priority === 'high' ? 0 : 1500;
    if (now - audioCtx.lastSpeakAt < minGap) return;
    try {
        if (opts.priority === 'high') {
            window.speechSynthesis.cancel();
        } else if (window.speechSynthesis.speaking || window.speechSynthesis.pending) {
            return;
        }
        audioCtx.lastSpeakAt = now;
        const u = new SpeechSynthesisUtterance(text);
        if (audioCtx.spanishVoice) u.voice = audioCtx.spanishVoice;
        u.lang = 'es-ES';
        const p = VOICE_PROFILES[npcId] || { rate: 1.0, pitch: 1.0, vol: 1.0 };
        u.rate = p.rate;
        u.pitch = p.pitch;
        u.volume = p.vol;
        window.speechSynthesis.speak(u);
    } catch (e) {}
}

function stopSpeak() {
    if (!('speechSynthesis' in window)) return;
    try { window.speechSynthesis.cancel(); } catch (e) {}
}
