/**
 * T11 · Rate-limits / quotas. Two layers (Finding-5):
 *  1. PRE-AUTH throttle keyed on the salted `remoteAddrHash` — the ONLY identifier before a
 *     principal exists. Applied in T12 `onUpgrade` BEFORE token verification. Blunts subdomain
 *     enumeration, capability-verifier brute-force, and WebAuthn/TOTP guessing DoS.
 *  2. PER-TENANT quotas keyed strictly on the authenticated `accountId` (INV3), never a client id.
 *
 * Pre-auth and per-tenant keys live in DISJOINT namespaces (no bleed). Keys are OPAQUE to the store.
 */
import type { PlanTier } from 'relay-contracts'
import type { RateLimitPolicy, TokenBucketStore } from '../types.js'

const SECONDS_PER_MINUTE = 60
const SECONDS_PER_HOUR = 3600

/** Fixed, plan-independent pre-auth policy (no account known yet). */
export const PRE_AUTH_POLICY: RateLimitPolicy = {
  connectPerMin: 30,
  enrollPerHour: 10,
  maxConcurrentSessions: 1,
  maxPairedHosts: 1,
  preAuthPerMinPerIp: 60,
  totpMaxFailsPerWindow: 5,
  totpLockoutWindowSec: 300,
}

const PLAN_POLICIES: Readonly<Record<PlanTier, RateLimitPolicy>> = {
  free: {
    connectPerMin: 20,
    enrollPerHour: 5,
    maxConcurrentSessions: 2,
    maxPairedHosts: 1,
    preAuthPerMinPerIp: 60,
    totpMaxFailsPerWindow: 5,
    totpLockoutWindowSec: 300,
  },
  personal: {
    connectPerMin: 60,
    enrollPerHour: 20,
    maxConcurrentSessions: 5,
    maxPairedHosts: 5,
    preAuthPerMinPerIp: 60,
    totpMaxFailsPerWindow: 5,
    totpLockoutWindowSec: 300,
  },
  pro: {
    connectPerMin: 120,
    enrollPerHour: 60,
    maxConcurrentSessions: 20,
    maxPairedHosts: 25,
    preAuthPerMinPerIp: 120,
    totpMaxFailsPerWindow: 5,
    totpLockoutWindowSec: 300,
  },
  team: {
    connectPerMin: 300,
    enrollPerHour: 200,
    maxConcurrentSessions: 100,
    maxPairedHosts: 200,
    preAuthPerMinPerIp: 240,
    totpMaxFailsPerWindow: 5,
    totpLockoutWindowSec: 300,
  },
}

export function policyForPlan(plan: PlanTier): RateLimitPolicy {
  return PLAN_POLICIES[plan]
}

/** PRE-AUTH throttle keyed on `remoteAddrHash`. false = throttled (caller returns 429-equiv). */
export function checkPreAuthRate(
  remoteAddrHash: string,
  store: TokenBucketStore,
  now: number,
): Promise<boolean> {
  const perMin = PRE_AUTH_POLICY.preAuthPerMinPerIp
  return store.take(`preauth:ip:${remoteAddrHash}`, perMin / SECONDS_PER_MINUTE, perMin, now)
}

/** PER-TENANT connect rate keyed on the authenticated accountId (INV3). */
export function checkConnectRate(
  accountId: string,
  policy: RateLimitPolicy,
  store: TokenBucketStore,
  now: number,
): Promise<boolean> {
  const perMin = policy.connectPerMin
  return store.take(`connect:acct:${accountId}`, perMin / SECONDS_PER_MINUTE, perMin, now)
}

/** Concurrent-session cap (synchronous; caller supplies the live count). */
export function checkConcurrentSessions(
  _accountId: string,
  active: number,
  policy: RateLimitPolicy,
): boolean {
  return active < policy.maxConcurrentSessions
}

/** Enrollment rate keyed on the authenticated accountId (INV3). */
export function checkEnrollRate(
  accountId: string,
  policy: RateLimitPolicy,
  store: TokenBucketStore,
  now: number,
): Promise<boolean> {
  const perHour = policy.enrollPerHour
  return store.take(`enroll:acct:${accountId}`, perHour / SECONDS_PER_HOUR, perHour, now)
}
