import { expect, test } from "bun:test";
import {
  decodeBase32,
  generateTotp,
  generateTotpSecret,
  totpUri,
  verifyTotp,
} from "../src/totp/index.ts";

test("TOTP matches the RFC 6238 SHA-1 vector", async () => {
  const secret = "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ";
  expect(await generateTotp(secret, { timestamp: 59_000, digits: 8 })).toBe("94287082");
  expect(await verifyTotp(secret, "94287082", { timestamp: 59_000, digits: 8, window: 0 })).toEqual(
    { valid: true, counter: 1 },
  );
});

test("TOTP rejects malformed secrets, tokens, and unsafe options", async () => {
  expect(() => decodeBase32("JBSW0Y3P")).toThrow("Invalid base32 secret");
  expect(() => decodeBase32("====")).toThrow("Invalid base32 secret");
  expect(() => generateTotpSecret(() => new Uint8Array(19))).toThrow(
    "must return exactly 20 bytes",
  );
  await expect(generateTotp("JBSWY3DPEHPK3PXP", { period: 0 })).rejects.toThrow("TOTP period");
  expect(await verifyTotp("JBSWY3DPEHPK3PXP", "12ab56", { timestamp: 59_000 })).toEqual({
    valid: false,
  });
});

test("TOTP URI validates and normalizes configuration", () => {
  const uri = totpUri({
    issuer: " WorkRoot ",
    accountName: " user@example.com ",
    secret: "JBSW Y3DP-EHPK3PXP",
  });
  expect(uri).toContain("secret=JBSWY3DPEHPK3PXP");
  expect(uri).toContain("issuer=WorkRoot");
  expect(() =>
    totpUri({ issuer: "", accountName: "user@example.com", secret: "JBSWY3DPEHPK3PXP" }),
  ).toThrow("issuer and account name");
});
