import { describe, test, expect, beforeAll } from "bun:test";
import { encryptToSeedPgp, decryptSeedPgp, buildPlaintext } from "./seedpgp";
import * as openpgp from "openpgp";

// Official BIP39 test vectors from Trezor
const TREZOR_VECTORS = [
    {
        name: "12-word all zeros",
        mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about",
    },
    {
        name: "12-word all 0x7f",
        mnemonic: "legal winner thank year wave sausage worth useful legal winner thank yellow",
    },
    {
        name: "12-word all 0x80",
        mnemonic: "letter advice cage absurd amount doctor acoustic avoid letter advice cage above",
    },
    {
        name: "12-word all 0xff",
        mnemonic: "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong",
    },
    {
        name: "18-word all zeros",
        mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon agent",
    },
    {
        name: "24-word all zeros",
        mnemonic: "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art",
    },
    {
        name: "24-word random entropy",
        mnemonic: "hamster diagram private dutch cause delay private meat slide toddler razor book happy fancy gospel tennis maple dilemma loan word shrug inflict delay length",
    },
    {
        name: "12-word random entropy",
        mnemonic: "scheme spot photo card baby mountain device kick cradle pact join borrow",
    },
];

// Your existing tests
describe("seedpgp test vectors", () => {
    let testPublicKey: string;
    let testPrivateKey: string;
    const testPassphrase = "test-passphrase-123";

    beforeAll(async () => {
        const { privateKey, publicKey } = await openpgp.generateKey({
            type: "ecc",
            curve: "curve25519Legacy",
            userIDs: [{ name: "Test User", email: "test@example.com" }],
            passphrase: testPassphrase,
            format: "armored",
        });

        testPublicKey = publicKey;
        testPrivateKey = privateKey;
    });

    test("vector 1: standard 24-word mnemonic", async () => {
        const mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon art";

        const plaintext = buildPlaintext(mnemonic, false);

        const encrypted = await encryptToSeedPgp({
            plaintext,
            publicKeyArmored: testPublicKey,
        });

        const decrypted = await decryptSeedPgp({
            frameText: encrypted.framed,
            privateKeyArmored: testPrivateKey,
            privateKeyPassphrase: testPassphrase,
        });

        expect(decrypted.v).toBe(1);
        expect(decrypted.t).toBe("bip39");
        expect(decrypted.l).toBe("en");
        expect(decrypted.w).toBe(mnemonic);
        expect(decrypted.pp).toBe(0);
    });

    test("vector 2: 12-word mnemonic", async () => {
        const mnemonic = "legal winner thank year wave sausage worth useful legal winner thank yellow";

        const plaintext = buildPlaintext(mnemonic, false);

        const encrypted = await encryptToSeedPgp({
            plaintext,
            publicKeyArmored: testPublicKey,
        });

        const decrypted = await decryptSeedPgp({
            frameText: encrypted.framed,
            privateKeyArmored: testPrivateKey,
            privateKeyPassphrase: testPassphrase,
        });

        expect(decrypted.w).toBe(mnemonic);
    });

    test("vector 3: mnemonic with BIP39 passphrase flag", async () => {
        const mnemonic = "test wallet seed phrase example only do not use real funds";

        const plaintext = buildPlaintext(mnemonic, true);

        const encrypted = await encryptToSeedPgp({
            plaintext,
            publicKeyArmored: testPublicKey,
        });

        expect(encrypted.framed).toContain("SEEDPGP1:");
        expect(encrypted.recipientFingerprint).toBeDefined();

        const decrypted = await decryptSeedPgp({
            frameText: encrypted.framed,
            privateKeyArmored: testPrivateKey,
            privateKeyPassphrase: testPassphrase,
        });

        expect(decrypted.w).toBe(mnemonic);
        expect(decrypted.pp).toBe(1);
    });

    test("vector 4: reject wrong private key", async () => {
        const mnemonic = "test seed phrase";
        const plaintext = buildPlaintext(mnemonic, false);

        const { privateKey: wrongKey } = await openpgp.generateKey({
            type: "ecc",
            curve: "curve25519Legacy",
            userIDs: [{ name: "Wrong User", email: "wrong@example.com" }],
            passphrase: "wrong-pw",
            format: "armored",
        });

        const encrypted = await encryptToSeedPgp({
            plaintext,
            publicKeyArmored: testPublicKey,
        });

        await expect(
            decryptSeedPgp({
                frameText: encrypted.framed,
                privateKeyArmored: wrongKey,
                privateKeyPassphrase: "wrong-pw",
            })
        ).rejects.toThrow(/not encrypted to the provided private key/);
    });

    test("vector 5: reject invalid passphrase", async () => {
        const mnemonic = "test seed phrase";
        const plaintext = buildPlaintext(mnemonic, false);

        const encrypted = await encryptToSeedPgp({
            plaintext,
            publicKeyArmored: testPublicKey,
        });

        await expect(
            decryptSeedPgp({
                frameText: encrypted.framed,
                privateKeyArmored: testPrivateKey,
                privateKeyPassphrase: "wrong-passphrase",
            })
        ).rejects.toThrow();
    });

    test("vector 6: frame format validation", async () => {
        const mnemonic = "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong";
        const plaintext = buildPlaintext(mnemonic, false);

        const encrypted = await encryptToSeedPgp({
            plaintext,
            publicKeyArmored: testPublicKey,
        });

        const parts = encrypted.framed.split(":");
        expect(parts[0]).toBe("SEEDPGP1");
        expect(parts[1]).toBe("0");
        expect(parts[2]).toMatch(/^[0-9A-F]{4}$/);
        expect(parts.length).toBeGreaterThanOrEqual(4);
    });
});

// NEW: Add the Trezor vectors as a second describe block
describe("BIP39 Trezor test vectors", () => {
    let testPublicKey: string;
    let testPrivateKey: string;
    const testPassphrase = "test-passphrase-123";

    beforeAll(async () => {
        const { privateKey, publicKey } = await openpgp.generateKey({
            type: "ecc",
            curve: "curve25519Legacy",
            userIDs: [{ name: "Test User", email: "test@example.com" }],
            passphrase: testPassphrase,
            format: "armored",
        });

        testPublicKey = publicKey;
        testPrivateKey = privateKey;
    });

    TREZOR_VECTORS.forEach(({ name, mnemonic }) => {
        test(`${name}`, async () => {
            const plaintext = buildPlaintext(mnemonic, false);

            const encrypted = await encryptToSeedPgp({
                plaintext,
                publicKeyArmored: testPublicKey,
            });

            expect(encrypted.framed).toMatch(/^SEEDPGP1:0:[0-9A-F]{4}:.+$/);
            expect(encrypted.recipientFingerprint).toBeDefined();

            const decrypted = await decryptSeedPgp({
                frameText: encrypted.framed,
                privateKeyArmored: testPrivateKey,
                privateKeyPassphrase: testPassphrase,
            });

            expect(decrypted.v).toBe(1);
            expect(decrypted.t).toBe("bip39");
            expect(decrypted.l).toBe("en");
            expect(decrypted.pp).toBe(0);
            expect(decrypted.w).toBe(mnemonic);
        });
    });

    test("with BIP39 passphrase flag", async () => {
        const mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
        const plaintext = buildPlaintext(mnemonic, true);

        const encrypted = await encryptToSeedPgp({
            plaintext,
            publicKeyArmored: testPublicKey,
        });

        const decrypted = await decryptSeedPgp({
            frameText: encrypted.framed,
            privateKeyArmored: testPrivateKey,
            privateKeyPassphrase: testPassphrase,
        });

        expect(decrypted.w).toBe(mnemonic);
        expect(decrypted.pp).toBe(1);
    });
});
