/**
 * §4.3 Capability token — signed, stateless, short-TTL (Ed25519-signed PASETO/JWS).
 *
 * `sub`/`host` come from the authenticated principal + registry (INV3) — NEVER client input.
 * Scopes exactly one host and a least-privilege rights subset ('attach' ⊉ 'kill'). Revocable
 * by `jti` (INV12). relay-contracts owns the SHAPE + Zod validation; the signature VERIFY is
 * crypto owned by P5 (`relay-auth`) — `verifyCapabilityToken` is a frozen signature here.
 */
import { z } from 'zod'
import { NotImplementedInContractsError } from '../errors.js'

/**
 * Rights a token may carry (string-literal union, §4.3). `enroll` is the device/host
 * enrollment scope minted by the login→session subsystem for a `device:enroll` token,
 * kept separate from the 30–60s connect clamp (FIX C-native-1). Additive — never removed.
 */
export type CapabilityRight = 'attach' | 'manage' | 'kill' | 'enroll'
export const CapabilityRightSchema = z.enum(['attach', 'manage', 'kill', 'enroll'])

export interface CapabilityToken {
  readonly sub: string // principal id — from authenticated session, NOT client
  readonly aud: string // subdomain this token is valid at (Host-confusion guard, INV1)
  readonly host: string // exact host_id scope — single host, never wildcard
  readonly rights: readonly CapabilityRight[] // least-privilege subset
  readonly iat: number
  readonly exp: number // short TTL
  readonly jti: string // unique id for revocation-list lookup
}

/**
 * Shape/claims validation for a DECODED token body (structural checks only — the Ed25519
 * signature is verified by P5). `rights` must be non-empty and unique.
 */
export const CapabilityTokenSchema = z
  .object({
    sub: z.string().min(1),
    aud: z.string().min(1),
    host: z.string().min(1),
    rights: z.array(CapabilityRightSchema).nonempty(),
    iat: z.number().int().nonnegative(),
    exp: z.number().int().nonnegative(),
    jti: z.string().min(1),
  })
  .strict()
  .superRefine((tok, ctx) => {
    if (new Set(tok.rights).size !== tok.rights.length) {
      ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'rights must be unique' })
    }
    if (tok.exp <= tok.iat) {
      ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'exp must be after iat' })
    }
  })

/**
 * FROZEN SIGNATURE (§4.3). Verifies the Ed25519 signature, `aud === expectedAud`, and
 * `now < exp`, returning the validated claims. The crypto IMPLEMENTATION lives in P5
 * `relay-auth`; relay-contracts owns only the shape. Do not call from relay-contracts.
 */
export function verifyCapabilityToken(
  _raw: string,
  _expectedAud: string,
  _now: number,
): CapabilityToken {
  throw new NotImplementedInContractsError('verifyCapabilityToken', 'P5 relay-auth')
}
