import o from "@tutao/otest"
import { decapsulate, encapsulate, generateKeyPair, LibOQSExports } from "../lib/encryption/Liboqs/Kyber.js"
import { extractKyberPublicKeyFromKyberPrivateKey, random } from "../lib/index.js"
import { loadWasmModuleFallback, loadWasmModuleFromFile } from "./WebAssemblyTestUtils.js"
import { $ } from "zx"
import fs from "node:fs"
import { assertThrows } from "@tutao/tutanota-test-utils"

o.spec("Kyber", function () {
	// We need to generate the webassembly stuff during tests
	// since they're generated by esbuild during app build process
	// and packages tests run before the app build.
	o.before(async () => {
		const currentPath = process.cwd()

		const make = $
		make.verbose = false
		make.cwd = "../../../libs/webassembly/"
		make.env = {
			...process.env,
			WASM: `${currentPath}/liboqs.wasm`,
		}

		await make`make -f Makefile_liboqs build`

		make.cwd = currentPath
	})

	// We remove them after the tests since we don't need them anymore
	o.after(() => {
		fs.rmSync("./liboqs.wasm")
	})

	o("encryption roundtrip", async function () {
		const liboqs = (await loadWasmModuleFromFile("./liboqs.wasm")) as LibOQSExports

		const keyPair = generateKeyPair(liboqs, random)
		o(keyPair.privateKey.raw.length).equals(3168)
		o(keyPair.publicKey.raw.length).equals(1568)

		const encapsulation = encapsulate(liboqs, keyPair.publicKey, random)
		o(encapsulation.sharedSecret.length).equals(32)
		o(encapsulation.ciphertext.length).equals(1568)

		const decapsulatedSecret = decapsulate(liboqs, keyPair.privateKey, encapsulation.ciphertext)

		o(encapsulation.sharedSecret).deepEquals(decapsulatedSecret)
	})

	o("liboqs fallback unavailable", async function () {
		await assertThrows(Error, async () => await loadWasmModuleFallback("../liboqs.js"))
	})

	o("extract public key", async function () {
		const liboqs = (await loadWasmModuleFromFile("./liboqs.wasm")) as LibOQSExports
		const keyPair = generateKeyPair(liboqs, random)
		const extractedPublicKey = extractKyberPublicKeyFromKyberPrivateKey(keyPair.privateKey)

		o(extractedPublicKey).deepEquals(keyPair.publicKey)
	})
})
