import { describe, expect, it } from "vitest";
import {
  evaluateCodeAttempt,
  generateCode,
  hashCode,
} from "./verificationCode";

const FUTURE = new Date("2030-01-01T00:00:00Z");
const NOW = new Date("2026-05-27T12:00:00Z");
const CORRECT_HASH = "correct-hash";
const WRONG_HASH = "wrong-hash";

describe("generateCode", () => {
  it("returns a 6-digit numeric string", () => {
    const code = generateCode();
    expect(code).toMatch(/^\d{6}$/);
  });
});

describe("hashCode", () => {
  it("produces the same hash for the same code+secret", () => {
    const a = hashCode("482911", "secret-x");
    const b = hashCode("482911", "secret-x");
    expect(a).toBe(b);
  });

  it("produces different hashes when the secret differs", () => {
    const a = hashCode("482911", "secret-x");
    const b = hashCode("482911", "secret-y");
    expect(a).not.toBe(b);
  });
});

describe("evaluateCodeAttempt", () => {
  it("rejects a wrong code and increments attempts", () => {
    const result = evaluateCodeAttempt({
      submittedCodeHash: WRONG_HASH,
      row: { codeHash: CORRECT_HASH, attempts: 0, expires: FUTURE },
      now: NOW,
    });

    expect(result).toEqual({ outcome: "wrong", newAttempts: 1 });
  });

  it("accepts a correct code", () => {
    const result = evaluateCodeAttempt({
      submittedCodeHash: CORRECT_HASH,
      row: { codeHash: CORRECT_HASH, attempts: 0, expires: FUTURE },
      now: NOW,
    });

    expect(result).toEqual({ outcome: "ok" });
  });

  it("locks out on the 5th consecutive wrong attempt", () => {
    const result = evaluateCodeAttempt({
      submittedCodeHash: WRONG_HASH,
      row: { codeHash: CORRECT_HASH, attempts: 4, expires: FUTURE },
      now: NOW,
    });

    expect(result).toEqual({ outcome: "locked-out" });
  });

  it("still rejects a correct code submitted after lock-out", () => {
    const result = evaluateCodeAttempt({
      submittedCodeHash: CORRECT_HASH,
      row: { codeHash: CORRECT_HASH, attempts: 5, expires: FUTURE },
      now: NOW,
    });

    expect(result).toEqual({ outcome: "locked-out" });
  });

  it("reports no-such-row when there's no row for the email", () => {
    const result = evaluateCodeAttempt({
      submittedCodeHash: CORRECT_HASH,
      row: null,
      now: NOW,
    });

    expect(result).toEqual({ outcome: "no-such-row" });
  });

  it("reports expired when the row's expiry is in the past", () => {
    const expired = new Date(NOW.getTime() - 1000);
    const result = evaluateCodeAttempt({
      submittedCodeHash: CORRECT_HASH,
      row: { codeHash: CORRECT_HASH, attempts: 0, expires: expired },
      now: NOW,
    });

    expect(result).toEqual({ outcome: "expired" });
  });
});
