/**
 * modules/core/cryptoHelpers.js — PBKDF2 (Web Crypto), comparación segura en tiempo constante.
 */
(function (g) {
    g.modules = g.modules || {};
    g.modules.core = g.modules.core || {};
    var PBKDF2_ITERATIONS = 120000;
    var SALT_BYTES = 16;

    function bufToB64(buf) {
        var bytes = new Uint8Array(buf);
        var bin = '';
        for (var i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);
        return btoa(bin);
    }

    function b64ToBuf(b64) {
        var bin = atob(b64);
        var bytes = new Uint8Array(bin.length);
        for (var i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
        return bytes.buffer;
    }

    function randomSaltB64() {
        if (!g.crypto || !g.crypto.getRandomValues) throw new Error('crypto_unavailable');
        var buf = new Uint8Array(SALT_BYTES);
        g.crypto.getRandomValues(buf);
        return bufToB64(buf);
    }

    async function pbkdf2Hash(passwordText, saltB64) {
        if (!g.crypto || !g.crypto.subtle) {
            throw new Error('crypto_unavailable');
        }
        var enc = new TextEncoder();
        var salt = new Uint8Array(b64ToBuf(saltB64));
        var keyMaterial = await g.crypto.subtle.importKey('raw', enc.encode(passwordText), 'PBKDF2', false, ['deriveBits']);
        var bits = await g.crypto.subtle.deriveBits(
            { name: 'PBKDF2', salt: salt, iterations: PBKDF2_ITERATIONS, hash: 'SHA-256' },
            keyMaterial,
            256
        );
        return bufToB64(bits);
    }

    function timingSafeEqualB64(a, b) {
        try {
            var A = new Uint8Array(b64ToBuf(a));
            var B = new Uint8Array(b64ToBuf(b));
            if (A.length !== B.length) return false;
            var d = 0;
            for (var i = 0; i < A.length; i++) d |= A[i] ^ B[i];
            return d === 0;
        } catch (_) {
            return false;
        }
    }

    g.modules.core.crypto = {
        PBKDF2_ITERATIONS: PBKDF2_ITERATIONS,
        randomSaltB64: randomSaltB64,
        pbkdf2Hash: pbkdf2Hash,
        timingSafeEqualB64: timingSafeEqualB64,
        hashSecret: async function (plain, saltB64) {
            return pbkdf2Hash(String(plain || ''), saltB64);
        },
    };
})(typeof window !== 'undefined' ? window : this);
