import { exportJWK, generateKeyPair, importJWK, jwtVerify, type JWK } from 'jose';
import { generateClientAssertion } from './client-assertion.util';

describe('generateClientAssertion', () => {
  let privateJwk: JWK;
  let publicJwk: JWK;

  beforeAll(async () => {
    const kp = await generateKeyPair('RS256', { extractable: true });
    privateJwk = await exportJWK(kp.privateKey);
    publicJwk = await exportJWK(kp.publicKey);
  });

  it('produces a JWT verifiable with the matching public key', async () => {
    const jwt = await generateClientAssertion({
      clientId: 'edr-passenger-test',
      audience: 'https://esignet.example.com/token',
      privateJwk,
    });

    const verifier = await importJWK(publicJwk, 'RS256');
    const { payload, protectedHeader } = await jwtVerify(jwt, verifier, {
      issuer: 'edr-passenger-test',
      subject: 'edr-passenger-test',
      audience: 'https://esignet.example.com/token',
    });

    expect(protectedHeader.alg).toBe('RS256');
    expect(protectedHeader.typ).toBe('JWT');
    expect(payload.iss).toBe('edr-passenger-test');
    expect(payload.sub).toBe('edr-passenger-test');
    expect(payload.aud).toBe('https://esignet.example.com/token');
    expect(typeof payload.iat).toBe('number');
    expect(typeof payload.exp).toBe('number');
  });

  it('defaults exp to 120 seconds after iat', async () => {
    const jwt = await generateClientAssertion({
      clientId: 'c',
      audience: 'https://a/token',
      privateJwk,
    });
    const verifier = await importJWK(publicJwk, 'RS256');
    const { payload } = await jwtVerify(jwt, verifier);
    expect(payload.exp! - payload.iat!).toBe(120);
  });

  it('honors a custom expiresIn', async () => {
    const jwt = await generateClientAssertion({
      clientId: 'c',
      audience: 'https://a/token',
      privateJwk,
      expiresIn: '5m',
    });
    const verifier = await importJWK(publicJwk, 'RS256');
    const { payload } = await jwtVerify(jwt, verifier);
    expect(payload.exp! - payload.iat!).toBe(300);
  });

  it('fails verification against a wrong audience', async () => {
    const jwt = await generateClientAssertion({
      clientId: 'c',
      audience: 'https://a/token',
      privateJwk,
    });
    const verifier = await importJWK(publicJwk, 'RS256');
    await expect(
      jwtVerify(jwt, verifier, { audience: 'https://other/token' }),
    ).rejects.toThrow();
  });
});
