(function() {
    let keyring = {}; // { keyId: { passphrase, key } }
    let activeKeyId = null;

    // Helper: string to array buffer
    function strToBuf(str) {
        return new TextEncoder().encode(str);
    }

    // Helper: array buffer to string
    function bufToStr(buf) {
        return new TextDecoder().decode(buf);
    }

    // Helper: array buffer to hex string
    function bufToHex(buf) {
        return Array.from(new Uint8Array(buf))
            .map(b => b.toString(16).padStart(2, '0'))
            .join('');
    }

    // Helper: hex string to array buffer
    function hexToBuf(hex) {
        if (!hex) return new Uint8Array(0).buffer;
        const bytes = new Uint8Array(hex.length / 2);
        for (let i = 0; i < bytes.length; i++) {
            bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
        }
        return bytes.buffer;
    }

    // Short hash of passphrase to identify the key (non-reversible identifier)
    async function computeKeyId(passphrase) {
        const msgUint8 = strToBuf(passphrase);
        const hashBuffer = await window.crypto.subtle.digest('SHA-256', msgUint8);
        const hashArray = Array.from(new Uint8Array(hashBuffer));
        return hashArray.map(b => b.toString(16).padStart(2, '0')).join('').substring(0, 8);
    }

    // Derive PBKDF2 key from passphrase
    async function deriveKey(passphrase, roomName) {
        const passwordBuffer = strToBuf(passphrase);
        const saltBuffer = strToBuf("transient.chat.salt." + roomName);

        const baseKey = await window.crypto.subtle.importKey(
            "raw",
            passwordBuffer,
            "PBKDF2",
            false,
            ["deriveKey"]
        );

        return await window.crypto.subtle.deriveKey(
            {
                name: "PBKDF2",
                salt: saltBuffer,
                iterations: 100000,
                hash: "SHA-256"
            },
            baseKey,
            { name: "AES-GCM", length: 256 },
            false,
            ["encrypt", "decrypt"]
        );
    }

    // Add a passphrase to the keyring
    window.addKeyToRing = async function(passphrase, roomName) {
        try {
            const keyId = await computeKeyId(passphrase);
            const cryptoKey = await deriveKey(passphrase, roomName);
            keyring[keyId] = {
                passphrase: passphrase,
                key: cryptoKey
            };
            if (!activeKeyId) {
                activeKeyId = keyId;
            }
            return keyId;
        } catch (e) {
            console.error("Failed to add key to ring:", e);
            return null;
        }
    };

    window.removeKeyFromRing = function(keyId) {
        delete keyring[keyId];
        if (activeKeyId === keyId) {
            const remaining = Object.keys(keyring);
            activeKeyId = remaining.length > 0 ? remaining[0] : null;
        }
    };

    window.setActiveKey = function(keyId) {
        if (keyring[keyId]) {
            activeKeyId = keyId;
            return true;
        }
        return false;
    };

    window.getActiveKeyId = function() {
        return activeKeyId;
    };

    window.getKeyringIds = function() {
        return Object.keys(keyring);
    };

    window.getKeyring = function() {
        // Return passive list of { keyId, passphrase }
        const list = [];
        for (const [id, data] of Object.entries(keyring)) {
            list.push({ keyId: id, passphrase: data.passphrase });
        }
        return list;
    };

    window.isCryptoEnabled = function() {
        return activeKeyId !== null;
    };

    window.encryptData = async function(plaintext, keyId = activeKeyId) {
        if (!keyId || !keyring[keyId]) {
            throw new Error("Key not found in keyring");
        }
        const iv = window.crypto.getRandomValues(new Uint8Array(12));
        const encrypted = await window.crypto.subtle.encrypt(
            { name: "AES-GCM", iv: iv },
            keyring[keyId].key,
            strToBuf(plaintext)
        );
        return {
            ciphertext: bufToHex(encrypted),
            iv: bufToHex(iv),
            keyId: keyId
        };
    };

    window.decryptData = async function(ciphertextHex, ivHex, keyId) {
        if (!keyId || !keyring[keyId]) {
            throw new Error("Key not found in keyring");
        }
        const decrypted = await window.crypto.subtle.decrypt(
            { name: "AES-GCM", iv: hexToBuf(ivHex) },
            keyring[keyId].key,
            hexToBuf(ciphertextHex)
        );
        return bufToStr(decrypted);
    };
})();
