import crypto from "node:crypto";
import { RESTIC_PASS_FILE } from "../core/constants";

const algorithm = "aes-256-gcm" as const;
const keyLength = 32;
const encryptionPrefix = "encv1";

/**
 * Given a string, encrypts it using a randomly generated salt
 */
const encrypt = async (data: string) => {
	if (!data) {
		return data;
	}

	if (data.startsWith(encryptionPrefix)) {
		return data;
	}

	const secret = (await Bun.file(RESTIC_PASS_FILE).text()).trim();

	const salt = crypto.randomBytes(16);
	const key = crypto.pbkdf2Sync(secret, salt, 100000, keyLength, "sha256");
	const iv = crypto.randomBytes(12);

	const cipher = crypto.createCipheriv(algorithm, key, iv);
	const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);

	const tag = cipher.getAuthTag();
	return `${encryptionPrefix}:${salt.toString("hex")}:${iv.toString("hex")}:${encrypted.toString("hex")}:${tag.toString("hex")}`;
};

/**
 * Given an encrypted string, decrypts it using the salt stored in the string
 */
const decrypt = async (encryptedData: string) => {
	const secret = await Bun.file(RESTIC_PASS_FILE).text();

	const parts = encryptedData.split(":").slice(1); // Remove prefix
	const saltHex = parts.shift() as string;
	const salt = Buffer.from(saltHex, "hex");

	const key = crypto.pbkdf2Sync(secret, salt, 100000, keyLength, "sha256");

	const iv = Buffer.from(parts.shift() as string, "hex");
	const encrypted = Buffer.from(parts.shift() as string, "hex");
	const tag = Buffer.from(parts.shift() as string, "hex");
	const decipher = crypto.createDecipheriv(algorithm, key, iv);

	decipher.setAuthTag(tag);

	let decrypted = decipher.update(encrypted);
	decrypted = Buffer.concat([decrypted, decipher.final()]);

	return decrypted.toString();
};

export const cryptoUtils = {
	encrypt,
	decrypt,
};
