/**
 * T10 · Global + per-host revocation that kills LIVE tunnels in seconds (INV12).
 *
 * P5 stays socket-free (Finding-2): `revoke()` marks the token/host revoked in the store, stops
 * cert renewal (T9 sees `revoked` → never renews), then PUBLISHES an immutable `KillSignal` on the
 * injected `RevocationBus` (frozen Redis channel `relay:revocations`, relay-contracts §4.2 FIX 4).
 * Every P1 relay node subscribes and injects a §4.1 CLOSE+RST (host/account) or GOAWAY (global)
 * for each affected stream — no new wire type, no P5↔socket coupling. Teardown target: within
 * REVOCATION_PUSH_BUDGET_MS of `revoke()` returning.
 */
import { RevocationScopeSchema } from 'relay-contracts'
import type { RevocationScope, KillSignal, RevocationBus } from '../types.js'
import type { HostRegistryPort, RevocationStore } from '../types.js'

/**
 * Ordered: 1) mark revoked in store  2) (cert renewal stops via host.status='revoked', T9)
 * 3) PUBLISH the KillSignal on the bus  4) return the signal.
 */
export async function revoke(
  scope: RevocationScope,
  store: RevocationStore,
  hosts: HostRegistryPort,
  bus: RevocationBus,
  now: number,
): Promise<KillSignal> {
  RevocationScopeSchema.parse(scope) // boundary validation

  // Best-effort store marking for host scope (P3 flips hosts.status='revoked'; our port is
  // read-only for hosts, so the durable status flip is P3's — INTEGRATION POINT). The connect-time
  // gate additionally consults host.status via the registry (T3), so a revoked host is refused.
  if (scope.kind === 'host') {
    await hosts.getById(scope.hostId) // touch registry so a caller can assert the host exists
  }

  const signal: KillSignal = { scope, at: now, reason: reasonFor(scope) }
  await bus.publish(signal) // the push channel — P1 subscribers tear the live tunnel down
  return signal
}

function reasonFor(scope: RevocationScope): string {
  switch (scope.kind) {
    case 'host':
      return 'host-revoked'
    case 'account':
      return 'account-revoked'
    case 'global':
      return 'global-drain'
  }
}

/** Revoke a single capability token by jti (connect-time gate; distinct from tunnel teardown). */
export async function revokeToken(
  jti: string,
  exp: number,
  store: RevocationStore,
): Promise<void> {
  await store.revokeJti(jti, exp)
}
