/*
 * vault1984 — TOTP generation (RFC 6238)
 * Runs in both QuickJS (CLI) and browser (extension).
 *
 * In CLI (QuickJS): native_hmac_sha1 provided by jsbridge.c via BearSSL.
 *   All calls are synchronous.
 * In browser: Web Crypto API (async).
 */

/* IS_BROWSER defined in crypto.js, reuse if available */
var IS_BROWSER_TOTP = (typeof IS_BROWSER !== 'undefined') ? IS_BROWSER
    : (typeof globalThis.crypto !== 'undefined' && typeof globalThis.crypto.subtle !== 'undefined');

/* --- Base32 decode (RFC 4648) --- */

function base32_decode(input) {
    var alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
    input = input.replace(/[\s=]/g, '').toUpperCase();

    var bits = 0, value = 0, idx = 0;
    var output = new Uint8Array(Math.floor(input.length * 5 / 8));

    for (var i = 0; i < input.length; i++) {
        var v = alphabet.indexOf(input[i]);
        if (v < 0) continue;
        value = (value << 5) | v;
        bits += 5;
        if (bits >= 8) {
            output[idx++] = (value >>> (bits - 8)) & 0xFF;
            bits -= 8;
        }
    }

    return new Uint8Array(output.buffer, 0, idx);
}

/* --- HMAC-SHA1 --- */

function hmac_sha1(key, data) {
    if (IS_BROWSER_TOTP) {
        return crypto.subtle.importKey(
            'raw', key, { name: 'HMAC', hash: 'SHA-1' }, false, ['sign']
        ).then(function(cryptoKey) {
            return crypto.subtle.sign('HMAC', cryptoKey, data);
        }).then(function(sig) {
            return new Uint8Array(sig);
        });
    } else {
        return native_hmac_sha1(key, data);
    }
}

/* --- TOTP (RFC 6238) --- */

/**
 * Generate a TOTP code.
 * @param {string} secret_b32 - base32-encoded TOTP secret
 * @param {number} [time] - Unix timestamp (default: now)
 * @param {number} [period] - Time step in seconds (default: 30)
 * @param {number} [digits] - Number of digits (default: 6)
 * @returns {string|Promise<string>} TOTP code (zero-padded)
 */
function generate_totp(secret_b32, time, period, digits) {
    period = period || 30;
    digits = digits || 6;
    time = time || Math.floor(Date.now() / 1000);

    var key = base32_decode(secret_b32);
    var counter = Math.floor(time / period);

    /* Encode counter as 8-byte big-endian */
    var msg = new Uint8Array(8);
    var tmp = counter;
    for (var i = 7; i >= 0; i--) {
        msg[i] = tmp & 0xFF;
        tmp = Math.floor(tmp / 256);
    }

    function truncate(hash) {
        var offset = hash[hash.length - 1] & 0x0F;
        var code = (
            ((hash[offset] & 0x7F) << 24) |
            ((hash[offset + 1] & 0xFF) << 16) |
            ((hash[offset + 2] & 0xFF) << 8) |
            (hash[offset + 3] & 0xFF)
        ) % Math.pow(10, digits);
        var s = code.toString();
        while (s.length < digits) s = '0' + s;
        return s;
    }

    if (IS_BROWSER_TOTP) {
        return hmac_sha1(key, msg).then(truncate);
    } else {
        return truncate(hmac_sha1(key, msg));
    }
}

/**
 * Time remaining until current TOTP code expires.
 * @param {number} [period] - Time step (default: 30)
 * @returns {number} seconds remaining
 */
function totp_remaining(period) {
    period = period || 30;
    return period - (Math.floor(Date.now() / 1000) % period);
}

/* Export */
if (typeof globalThis.vault1984 === 'undefined') globalThis.vault1984 = {};
globalThis.vault1984.totp = {
    generate_totp: generate_totp,
    totp_remaining: totp_remaining,
    base32_decode: base32_decode
};
