/**
 * JWT claim shapes (HS256 via `jose`, secret AUTH_SECRET shared by web and game server).
 * See ARCHITECTURE §4 "Auth".
 *
 * Claims schemas are deliberately *non-strict* `z.object`s: a verified JWT payload also carries the
 * registered claims (`iss`, `aud`, `iat`, `exp`, ...) that `jose` already checked, and parsing strips
 * them so the result is exactly the custom claims a token was signed with.
 */
import { z } from 'zod';
import { TimestampSchema, UuidSchema } from './common';

export const JWT_ISSUER = 'pintrush';
export const JWT_AUDIENCE = 'pintrush-game';
export const JWT_ALGORITHM = 'HS256';

/** Token lifetimes in **seconds** (JWT `exp` is in seconds). TV links never expire; they are revoked by version. */
export const TOKEN_TTL = {
  guestSeconds: 6 * 60 * 60,
  hostSeconds: 12 * 60 * 60,
  tvSeconds: null,
} as const;

export const VENUE_ROLES = ['OWNER', 'HOST'] as const;
export const VenueRoleSchema = z.enum(VENUE_ROLES);
export type VenueRole = z.output<typeof VenueRoleSchema>;

/** Phone player. Rotated on every reconnect; `tv` = token version (current or previous accepted). */
export const GuestClaimsSchema = z.object({
  scope: z.literal('guest'),
  /** PlayerSession id. */
  sub: UuidSchema,
  /** GameSession id. */
  gsid: UuidSchema,
  roomId: UuidSchema,
  venueId: UuidSchema,
  tv: z.int().min(0),
});

/** TV display link. Display only: it can never send host or player commands. */
export const TvClaimsSchema = z.object({
  scope: z.literal('tv'),
  roomId: UuidSchema,
  venueId: UuidSchema,
  /** Room.tvTokenVersion at mint time; bumping it revokes old links. */
  v: z.int().min(0),
});

/** Signed-in venue user. Every host command checks `room.venueId === claims.venueId`. */
export const HostClaimsSchema = z.object({
  scope: z.literal('host'),
  /** VenueUser id. */
  sub: UuidSchema,
  venueId: UuidSchema,
  role: VenueRoleSchema,
});

export const TokenClaimsSchema = z.discriminatedUnion('scope', [GuestClaimsSchema, TvClaimsSchema, HostClaimsSchema]);

export type GuestClaims = z.output<typeof GuestClaimsSchema>;
export type TvClaims = z.output<typeof TvClaimsSchema>;
export type HostClaims = z.output<typeof HostClaimsSchema>;
export type TokenClaims = z.output<typeof TokenClaimsSchema>;
export type TokenScope = TokenClaims['scope'];

/** Payload of the `session.token` server message and part of the `room.join` ack. */
export const SessionTokenDataSchema = z.strictObject({
  token: z.string().min(1),
  playerSessionId: UuidSchema,
  gameSessionId: UuidSchema,
  /** Epoch **milliseconds** (like every other protocol timestamp). */
  expiresAt: TimestampSchema,
});
export type SessionTokenData = z.output<typeof SessionTokenDataSchema>;
