import tweetnacl from 'tweetnacl';
import tweetnaclUtil from 'tweetnacl-util';
import zlib from 'zlib';
import { promisify } from 'util';
const gzip = promisify(zlib.gzip);
const gunzip = promisify(zlib.gunzip);
const { encodeBase64, decodeBase64, encodeUTF8, decodeUTF8 } = tweetnaclUtil;
import { isLargeMessage, isLargeOutbound, workerParseMessage, workerEncryptAndSend } from './worker-pool.js';
const COMPRESS_THRESHOLD = 512;
export function generateSessionKey() {
    return tweetnacl.randomBytes(32);
}
export async function encrypt(data, key) {
    const nonce = tweetnacl.randomBytes(24);
    const jsonStr = JSON.stringify(data);
    let message;
    let compressed = false;
    if (jsonStr.length > COMPRESS_THRESHOLD) {
        const compressedBuf = await gzip(Buffer.from(jsonStr, 'utf8'));
        message = new Uint8Array(compressedBuf);
        compressed = true;
    }
    else {
        message = decodeUTF8(jsonStr);
    }
    const encrypted = tweetnacl.secretbox(message, nonce, key);
    const result = {
        n: encodeBase64(nonce),
        c: encodeBase64(encrypted),
    };
    if (compressed)
        result.z = true;
    return result;
}
export async function decrypt(encrypted, key) {
    try {
        const nonce = decodeBase64(encrypted.n);
        const ciphertext = decodeBase64(encrypted.c);
        const decrypted = tweetnacl.secretbox.open(ciphertext, nonce, key);
        if (!decrypted)
            return null;
        if (encrypted.z) {
            const decompressed = await gunzip(Buffer.from(decrypted));
            return JSON.parse(decompressed.toString('utf8'));
        }
        else {
            return JSON.parse(encodeUTF8(decrypted));
        }
    }
    catch {
        return null;
    }
}
export function isEncrypted(msg) {
    return msg !== null && typeof msg === 'object' && typeof msg.n === 'string' && typeof msg.c === 'string';
}
export function encodeKey(key) {
    return encodeBase64(key);
}
export function decodeKey(encodedKey) {
    return decodeBase64(encodedKey);
}
export async function parseMessage(data, sessionKey) {
    // Offload large messages to Worker thread to avoid blocking the event loop
    if (isLargeMessage(data)) {
        const result = await workerParseMessage(data, sessionKey);
        if (result !== null)
            return result;
        // Worker returned null — could be parse error or worker failure; fall through to inline
    }
    try {
        const parsed = JSON.parse(data);
        if (sessionKey && isEncrypted(parsed)) {
            return await decrypt(parsed, sessionKey);
        }
        return parsed;
    }
    catch {
        return null;
    }
}
export async function encryptAndSend(ws, msg, sessionKey) {
    // Offload large messages to Worker thread
    if (isLargeOutbound(msg)) {
        await workerEncryptAndSend(ws, msg, sessionKey);
        return;
    }
    try {
        if (sessionKey) {
            const encrypted = await encrypt(msg, sessionKey);
            ws.send(JSON.stringify(encrypted));
        }
        else {
            ws.send(JSON.stringify(msg));
        }
    }
    catch (err) {
        console.error('[encryptAndSend] Failed to send message:', err.message);
    }
}
//# sourceMappingURL=encryption.js.map