import { describe, expect, test } from "bun:test";
import { decryptField } from "./crypto.js";

// Local key/ciphertext pair generated for this test only — never a real secret.
async function encryptFieldForTest(keyHex: string, plain: string): Promise<string> {
  const raw = new Uint8Array(keyHex.match(/.{2}/g)!.map((b) => parseInt(b, 16)));
  const key = await crypto.subtle.importKey("raw", raw, { name: "AES-GCM" }, false, ["encrypt"]);
  const iv = crypto.getRandomValues(new Uint8Array(12));
  const ct = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, new TextEncoder().encode(plain));
  const ivHex = Array.from(iv).map((b) => b.toString(16).padStart(2, "0")).join("");
  const ctHex = Array.from(new Uint8Array(ct)).map((b) => b.toString(16).padStart(2, "0")).join("");
  return `${ivHex}:${ctHex}`;
}

const TEST_KEY_HEX = "2cd06834165b2fe851bbbc9c8cbbb4da101df384c91c2ddad207fcdb446eaf02";
const WRONG_KEY_HEX = "9f1e6a5c3b2d704f8e6c1a9b5d3f2071e0c4a8b6d5f3e2c1a0b9d8e7f6a5c4b3";

describe("decryptField", () => {
  test("round-trips a value encrypted with the same AES-GCM scheme", async () => {
    const plain = "1477000000:AAtest-fake-not-a-real-token";
    const cipher = await encryptFieldForTest(TEST_KEY_HEX, plain);
    const decrypted = await decryptField(TEST_KEY_HEX, cipher);
    expect(decrypted).toBe(plain);
  });

  test("passes through plaintext (no ':') unchanged, matching db-crypto.ts", async () => {
    const result = await decryptField(TEST_KEY_HEX, "not-encrypted");
    expect(result).toBe("not-encrypted");
  });

  test("passes through empty string unchanged", async () => {
    const result = await decryptField(TEST_KEY_HEX, "");
    expect(result).toBe("");
  });

  test("wrong key fails closed with an error, never wrong plaintext", async () => {
    const plain = "some-secret-token-value";
    const cipher = await encryptFieldForTest(TEST_KEY_HEX, plain);
    await expect(decryptField(WRONG_KEY_HEX, cipher)).rejects.toThrow();
  });
});
