import { createHmac, timingSafeEqual } from 'crypto'

export type ExternalLunchTokenPayload = {
  externalId: string
  /** Unix seconds. Token is rejected after this. */
  exp: number
}

function getSecret(): string {
  const s = process.env.NEXTAUTH_SECRET
  if (!s) throw new Error('NEXTAUTH_SECRET is not set; cannot sign external lunch tokens')
  return s
}

function hmac(payloadB64: string): string {
  return createHmac('sha256', getSecret()).update(payloadB64).digest('hex')
}

export function signExternalLunchToken(payload: ExternalLunchTokenPayload): string {
  const payloadB64 = Buffer.from(JSON.stringify(payload)).toString('base64url')
  const sig = hmac(payloadB64)
  return `${payloadB64}.${sig}`
}

export function verifyExternalLunchToken(token: string): ExternalLunchTokenPayload {
  const parts = token.split('.')
  if (parts.length !== 2) throw new Error('Invalid external lunch token: malformed')
  const [payloadB64, sig] = parts
  const expected = hmac(payloadB64)
  const a = Buffer.from(sig, 'hex')
  const b = Buffer.from(expected, 'hex')
  if (a.length !== b.length || !timingSafeEqual(a, b)) {
    throw new Error('Invalid external lunch token: signature mismatch')
  }
  let payload: ExternalLunchTokenPayload
  try {
    payload = JSON.parse(Buffer.from(payloadB64, 'base64url').toString('utf-8'))
  } catch {
    throw new Error('Invalid external lunch token: payload not parseable')
  }
  if (typeof payload.exp !== 'number' || payload.exp < Math.floor(Date.now() / 1000)) {
    throw new Error('Invalid external lunch token: expired')
  }
  return payload
}
