import { test, expect } from "bun:test";
import {
  generateKey,
  encrypt,
  decrypt,
  deriveKey,
  sha256,
  hmacSign,
  hmacVerify,
  createKeyring,
} from "../src/index.ts";

test("sha256 is stable and hex-encoded", async () => {
  const a = await sha256("hello");
  expect(a).toBe("2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824");
  expect(await sha256("hello")).toBe(a);
  expect(await sha256("world")).not.toBe(a);
});

test("hmacSign / hmacVerify (webhook signatures)", async () => {
  const sig = await hmacSign("payload", "secret");
  expect(await hmacVerify("payload", "secret", sig)).toBe(true);
  expect(await hmacVerify("payload", "wrong", sig)).toBe(false);
  expect(await hmacVerify("tampered", "secret", sig)).toBe(false);
});

test("encrypt/decrypt round-trip", async () => {
  const key = await generateKey();
  const box = await encrypt("card #1234 secret", key);
  expect(box).not.toContain("card"); // opaque
  expect(await decrypt(box, key)).toBe("card #1234 secret");
});

test("each encryption uses a fresh IV (different ciphertexts)", async () => {
  const key = await generateKey();
  const a = await encrypt("same", key);
  const b = await encrypt("same", key);
  expect(a).not.toBe(b);
  expect(await decrypt(a, key)).toBe("same");
  expect(await decrypt(b, key)).toBe("same");
});

test("wrong key or tampered data fails (authenticated)", async () => {
  const key = await generateKey();
  const other = await generateKey();
  const box = await encrypt("secret", key);
  await expect(decrypt(box, other)).rejects.toThrow();
  await expect(decrypt(box.slice(0, -4) + "AAAA", key)).rejects.toThrow();
});

test("deriveKey is deterministic for the same password+salt", async () => {
  const k1 = await deriveKey("hunter2", "user-salt");
  const k2 = await deriveKey("hunter2", "user-salt");
  const k3 = await deriveKey("hunter2", "other-salt");
  expect(k1).toBe(k2);
  expect(k1).not.toBe(k3);
  // usable as an encryption key
  expect(await decrypt(await encrypt("x", k1), k1)).toBe("x");
});

test("keyrings reject duplicate keys and return defensive copies", () => {
  expect(() =>
    createKeyring([
      { id: "one", secret: "secret-one", active: true },
      { id: "one", secret: "secret-two" },
    ]),
  ).toThrow("DUPLICATE");

  const keyring = createKeyring([
    { id: "one", secret: "secret-one", active: true },
    { id: "two", secret: "secret-two" },
  ]);
  const active = keyring.active();
  active.secret = "changed";
  expect(keyring.active().secret).toBe("secret-one");
});
