import { describe, expect, it } from 'vitest'
import { signSession, verifySession } from './session.js'

const SECRET_A = 'jwt-secret-current'
const SECRET_B = 'jwt-secret-previous'

describe('signSession / verifySession', () => {
  it('returns claims for a valid token', async () => {
    const token = await signSession({ sub: 'u1', sid: 's1', sv: 2 }, SECRET_A, 900)
    const claims = await verifySession(token, SECRET_A)
    expect(claims?.sub).toBe('u1')
    expect(claims?.sv).toBe(2)
  })

  it('returns null for tampered payload', async () => {
    const token = await signSession({ sub: 'u1', sid: 's1', sv: 1 }, SECRET_A, 900)
    const [header, , sig] = token.split('.')
    const tampered = `${header}.${btoa(JSON.stringify({ sub: 'evil', sid: 's1', sv: 99, iat: 1, exp: 9_999_999_999 })).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')}.${sig}`
    await expect(verifySession(tampered, SECRET_A)).resolves.toBeNull()
  })

  it('returns null for alg:none header — recompute ignores header alg', async () => {
    const token = await signSession({ sub: 'u1', sid: 's1', sv: 1 }, SECRET_A, 900)
    const [, payload, sig] = token.split('.')
    const noneHeader = btoa(JSON.stringify({ alg: 'none', typ: 'JWT' }))
      .replace(/\+/g, '-')
      .replace(/\//g, '_')
      .replace(/=+$/, '')
    const swapped = `${noneHeader}.${payload}.${sig}`
    await expect(verifySession(swapped, SECRET_A)).resolves.toBeNull()
  })

  it('returns null when expired beyond skew', async () => {
    const token = await signSession({ sub: 'u1', sid: 's1', sv: 1 }, SECRET_A, -120)
    await expect(verifySession(token, SECRET_A)).resolves.toBeNull()
  })

  it('rejects sv below minSessionVersion', async () => {
    const token = await signSession({ sub: 'u1', sid: 's1', sv: 1 }, SECRET_A, 900)
    await expect(verifySession(token, SECRET_A, { minSessionVersion: 2 })).resolves.toBeNull()
  })

  it('verifies with a rotated-out secret during grace window', async () => {
    const token = await signSession({ sub: 'u1', sid: 's1', sv: 1 }, SECRET_B, 900)
    await expect(verifySession(token, [SECRET_A, SECRET_B])).resolves.not.toBeNull()
  })
})
