import { InvalidRequestError } from '../../domain/errors';
import { oAuthVerifyPkceS256 } from './o-auth-verify-pkce-s256';

/**
 * Tests for PKCE S256 verification helper.
 * Decision D3: Only S256 is accepted; `plain` and missing challenge are rejected
 * with `invalid_request`. The verifier checks:
 *   BASE64URL(SHA256(code_verifier)) === code_challenge
 */
describe('oAuthVerifyPkceS256', () => {
  // Valid code_verifier and its pre-computed S256 challenge.
  const VALID_VERIFIER = 'aurora-pkce-test-verifier-abcdefghijklmnop';
  let VALID_CHALLENGE: string;

  beforeAll(() => {
    const { createHash } = require('node:crypto');
    VALID_CHALLENGE = createHash('sha256').update(VALID_VERIFIER).digest('base64url');
  });

  describe('S256 method — success path', () => {
    it('does not throw when code_verifier hashes to the stored code_challenge', () => {
      expect(() =>
        oAuthVerifyPkceS256({
          codeVerifier: VALID_VERIFIER,
          codeChallenge: VALID_CHALLENGE,
          codeChallengeMethod: 'S256',
        }),
      ).not.toThrow();
    });
  });

  describe('S256 method — mismatch', () => {
    it('throws InvalidRequestError when the hash does not match the stored challenge', () => {
      expect(() =>
        oAuthVerifyPkceS256({
          codeVerifier: 'wrong-verifier-that-produces-a-different-hash',
          codeChallenge: VALID_CHALLENGE,
          codeChallengeMethod: 'S256',
        }),
      ).toThrow(InvalidRequestError);
    });

    it('throws InvalidRequestError when code_challenge is an empty string', () => {
      expect(() =>
        oAuthVerifyPkceS256({
          codeVerifier: VALID_VERIFIER,
          codeChallenge: '',
          codeChallengeMethod: 'S256',
        }),
      ).toThrow(InvalidRequestError);
    });

    it('throws InvalidRequestError when code_verifier is an empty string', () => {
      expect(() =>
        oAuthVerifyPkceS256({
          codeVerifier: '',
          codeChallenge: VALID_CHALLENGE,
          codeChallengeMethod: 'S256',
        }),
      ).toThrow(InvalidRequestError);
    });
  });

  describe('plain method — rejected (D3)', () => {
    it('throws InvalidRequestError when codeChallengeMethod is "plain"', () => {
      expect(() =>
        oAuthVerifyPkceS256({
          codeVerifier: VALID_VERIFIER,
          codeChallenge: VALID_VERIFIER, // plain: challenge === verifier
          codeChallengeMethod: 'plain',
        }),
      ).toThrow(InvalidRequestError);
    });
  });

  describe('missing or unsupported method', () => {
    it('throws InvalidRequestError when codeChallengeMethod is undefined', () => {
      expect(() =>
        oAuthVerifyPkceS256({
          codeVerifier: VALID_VERIFIER,
          codeChallenge: VALID_CHALLENGE,
          codeChallengeMethod: undefined,
        }),
      ).toThrow(InvalidRequestError);
    });

    it('throws InvalidRequestError when codeChallengeMethod is an unknown value', () => {
      // Use a runtime-unknown value without TS double-cast
      const unknownMethod = 'RS256';
      expect(() =>
        oAuthVerifyPkceS256({
          codeVerifier: VALID_VERIFIER,
          codeChallenge: VALID_CHALLENGE,
          codeChallengeMethod: unknownMethod as 'S256',
        }),
      ).toThrow(InvalidRequestError);
    });
  });
});
