import { createHash } from 'crypto';
import {
  base64Url,
  generateCodeChallenge,
  generateCodeVerifier,
  generateState,
} from './pkce.util';

describe('pkce.util', () => {
  describe('base64Url', () => {
    it('strips padding and replaces + and / with - and _', () => {
      const input = Buffer.from([0xfb, 0xff, 0xbf, 0xfe]);
      const out = base64Url(input);
      expect(out).not.toMatch(/[+/=]/);
    });
  });

  describe('generateCodeVerifier', () => {
    it('returns a base64url-safe string', () => {
      expect(generateCodeVerifier()).toMatch(/^[A-Za-z0-9_-]+$/);
    });

    it('produces unique values across calls', () => {
      const a = generateCodeVerifier();
      const b = generateCodeVerifier();
      expect(a).not.toEqual(b);
    });

    it('produces at least 43 characters (RFC 7636 minimum)', () => {
      expect(generateCodeVerifier().length).toBeGreaterThanOrEqual(43);
    });
  });

  describe('generateCodeChallenge', () => {
    it('equals base64url(sha256(verifier))', () => {
      const verifier = 'fixed-test-verifier';
      const expected = createHash('sha256')
        .update(verifier)
        .digest('base64')
        .replace(/\+/g, '-')
        .replace(/\//g, '_')
        .replace(/=/g, '');
      expect(generateCodeChallenge(verifier)).toBe(expected);
    });

    it('is deterministic for the same verifier', () => {
      const verifier = generateCodeVerifier();
      expect(generateCodeChallenge(verifier)).toBe(generateCodeChallenge(verifier));
    });

    it('differs for different verifiers', () => {
      expect(generateCodeChallenge('a')).not.toBe(generateCodeChallenge('b'));
    });
  });

  describe('generateState', () => {
    it('returns a base64url-safe string', () => {
      expect(generateState()).toMatch(/^[A-Za-z0-9_-]+$/);
    });

    it('produces unique values across calls', () => {
      expect(generateState()).not.toEqual(generateState());
    });
  });
});
