/**
 * PIKEY Crypto Module
 * Handles client-side encryption/decryption using AES-GCM 256.
 * The Master Password is never stored; it's used to derive the encryption key via PBKDF2.
 */

const PIKEY_Crypto = {
    // Configuration
    PBKDF2_ITERATIONS: 100000,
    SALT_SIZE: 16,
    IV_SIZE: 12,

    /**
     * Derives a cryptographic key from a master password.
     */
    async deriveKey(password, salt) {
        const enc = new TextEncoder();
        const keyMaterial = await window.crypto.subtle.importKey(
            "raw",
            enc.encode(password),
            { name: "PBKDF2" },
            false,
            ["deriveBits", "deriveKey"]
        );
        return window.crypto.subtle.deriveKey(
            {
                name: "PBKDF2",
                salt: salt,
                iterations: this.PBKDF2_ITERATIONS,
                hash: "SHA-256"
            },
            keyMaterial,
            { name: "AES-GCM", length: 256 },
            false,
            ["encrypt", "decrypt"]
        );
    },

    /**
     * Encrypts a string using the master password.
     */
    async encrypt(plainText, masterPassword) {
        try {
            const enc = new TextEncoder();
            const salt = window.crypto.getRandomValues(new Uint8Array(this.SALT_SIZE));
            const iv = window.crypto.getRandomValues(new Uint8Array(this.IV_SIZE));
            const key = await this.deriveKey(masterPassword, salt);

            const encryptedContent = await window.crypto.subtle.encrypt(
                { name: "AES-GCM", iv: iv },
                key,
                enc.encode(plainText)
            );

            // Combine salt, iv, and encrypted data into a single base64 string
            const combined = new Uint8Array(salt.length + iv.length + encryptedContent.byteLength);
            combined.set(salt, 0);
            combined.set(iv, salt.length);
            combined.set(new Uint8Array(encryptedContent), salt.length + iv.length);

            return btoa(String.fromCharCode.apply(null, combined));
        } catch (e) {
            console.error("Encryption failed:", e);
            throw e;
        }
    },

    /**
     * Decrypts a base64 string using the master password.
     */
    async decrypt(cipherTextBase64, masterPassword) {
        try {
            const combined = new Uint8Array(atob(cipherTextBase64).split("").map(c => c.charCodeAt(0)));
            const salt = combined.slice(0, this.SALT_SIZE);
            const iv = combined.slice(this.SALT_SIZE, this.SALT_SIZE + this.IV_SIZE);
            const data = combined.slice(this.SALT_SIZE + this.IV_SIZE);

            const key = await this.deriveKey(masterPassword, salt);
            const dec = new TextDecoder();

            const decryptedContent = await window.crypto.subtle.decrypt(
                { name: "AES-GCM", iv: iv },
                key,
                data
            );

            return dec.decode(decryptedContent);
        } catch (e) {
            console.error("Decryption failed. Incorrect password?");
            throw new Error("Falha na descriptografia. Verifique sua Senha Mestra.");
        }
    }
};

window.PIKEY_Crypto = PIKEY_Crypto;
