/**
 * User-version revocation — foundation-auth-rbac (Task 10).
 *
 * Every session JWT carries `v` (the user_version at issue time). Bumping a
 * user's version (`bumpUserVersion`) invalidates ALL their live sessions: the
 * next request whose `token.v < currentVersion` is rejected.
 *
 * Read path is two-tier for edge performance:
 *   1. cache.default (per-PoP, 60s TTL) — keyed by a synthetic internal URL.
 *   2. KV `user_version:{userId}` — authoritative, read on cache miss.
 * Worst-case staleness is the 60s cache TTL (acceptance: revoked within <=60s).
 */
import type { Env } from '@zync/types'

const CACHE_TTL_SECONDS = 60

function versionCacheKey(userId: string): Request {
  return new Request(`https://zync-internal/user-version/${userId}`)
}

/**
 * Resolve the current user_version for `userId`. Checks cache.default first,
 * falls back to KV, and back-fills the cache with a 60s TTL. Returns 0 when no
 * version has ever been set (fresh user — every issued token has v>=0).
 */
export async function getUserVersion(env: Env, userId: string): Promise<number> {
  const cache = caches.default
  const cacheKey = versionCacheKey(userId)

  const hit = await cache.match(cacheKey)
  if (hit) {
    const cached = Number(await hit.text())
    if (Number.isFinite(cached)) return cached
  }

  const raw = await env.KV.get(`user_version:${userId}`)
  const version = raw ? Number(raw) : 0
  const safe = Number.isFinite(version) ? version : 0

  await cache.put(
    cacheKey,
    new Response(String(safe), {
      headers: { 'Cache-Control': `max-age=${CACHE_TTL_SECONDS}` },
    }),
  )
  return safe
}

/**
 * Increment a user's version in KV (authoritative) and best-effort purge the
 * per-PoP cache so the new value is seen immediately on this PoP. Called on
 * freeze and on any role/permission change.
 */
export async function bumpUserVersion(env: Env, userId: string): Promise<void> {
  const raw = await env.KV.get(`user_version:${userId}`)
  const current = raw ? Number(raw) : 0
  const next = (Number.isFinite(current) ? current : 0) + 1
  await env.KV.put(`user_version:${userId}`, String(next))
  // Best-effort: drop the stale cached value on this PoP. Other PoPs expire
  // within the 60s TTL.
  await caches.default.delete(versionCacheKey(userId))
}
