import { SignJWT, jwtVerify } from 'jose'
import type { AdminSessionPayload, ImpersonationTokenPayload, SessionPayload } from '@zync/types'

/** Access-session JWT TTL, in seconds (1 hour). */
export const SESSION_TTL_SECONDS = 3600

/**
 * Sign a JWT payload with HS256.
 * JWT_SECRET is provided by the Worker Env — never hardcode.
 */
export async function signJwt(
  payload: Record<string, unknown>,
  secret: string,
  expiresIn: string = '7d'
): Promise<string> {
  const secretKey = new TextEncoder().encode(secret)
  return new SignJWT(payload)
    .setProtectedHeader({ alg: 'HS256' })
    .setIssuedAt()
    .setExpirationTime(expiresIn)
    .sign(secretKey)
}

/**
 * Verify and decode a JWT.
 * Returns the payload or throws on invalid/expired token.
 */
export async function verifyJwt<T extends Record<string, unknown>>(
  token: string,
  secret: string
): Promise<T> {
  const secretKey = new TextEncoder().encode(secret)
  const { payload } = await jwtVerify(token, secretKey)
  return payload as T
}

/**
 * Sign a user access session (HS256, 1h TTL). `exp`/`iat` are set by jose;
 * the caller passes everything else (sub, tid, role, permissions, tier, type, v).
 */
export async function signSession(
  payload: Omit<SessionPayload, 'exp' | 'iat'>,
  secret: string,
): Promise<string> {
  const secretKey = new TextEncoder().encode(secret)
  return new SignJWT({ ...payload })
    .setProtectedHeader({ alg: 'HS256' })
    .setIssuedAt()
    .setExpirationTime(`${SESSION_TTL_SECONDS}s`)
    .sign(secretKey)
}

/** Verify a user access session JWT. Throws on invalid/expired/tampered token. */
export async function verifySession(token: string, secret: string): Promise<SessionPayload> {
  const secretKey = new TextEncoder().encode(secret)
  const { payload } = await jwtVerify(token, secretKey)
  return payload as unknown as SessionPayload
}

/**
 * Sign a system-admin session (HS256, 1h TTL). Carries `totp_verified` which
 * gates admin-route access.
 */
export async function signAdminSession(
  payload: Omit<AdminSessionPayload, 'exp' | 'iat'>,
  secret: string,
): Promise<string> {
  const secretKey = new TextEncoder().encode(secret)
  return new SignJWT({ ...payload })
    .setProtectedHeader({ alg: 'HS256' })
    .setIssuedAt()
    .setExpirationTime(`${SESSION_TTL_SECONDS}s`)
    .sign(secretKey)
}

/** Verify a system-admin session JWT. Throws on invalid/expired/tampered token. */
export async function verifyAdminSession(
  token: string,
  secret: string,
): Promise<AdminSessionPayload> {
  const secretKey = new TextEncoder().encode(secret)
  const { payload } = await jwtVerify(token, secretKey)
  return payload as unknown as AdminSessionPayload
}

/** TTL for impersonation JWTs — 15 minutes. */
export const IMPERSONATION_TTL_SECONDS = 900

/**
 * Sign a short-lived impersonation JWT (HS256, 15-min TTL).
 * Must be signed with IMPERSONATION_SECRET, not JWT_SECRET.
 */
export async function signImpersonationToken(
  payload: Omit<ImpersonationTokenPayload, 'exp' | 'iat'>,
  secret: string,
): Promise<string> {
  const secretKey = new TextEncoder().encode(secret)
  return new SignJWT({ ...payload })
    .setProtectedHeader({ alg: 'HS256' })
    .setIssuedAt()
    .setExpirationTime(`${IMPERSONATION_TTL_SECONDS}s`)
    .sign(secretKey)
}

/** Verify an impersonation JWT. Throws on invalid/expired/tampered token. */
export async function verifyImpersonationToken(
  token: string,
  secret: string,
): Promise<ImpersonationTokenPayload> {
  const secretKey = new TextEncoder().encode(secret)
  const { payload } = await jwtVerify(token, secretKey)
  return payload as unknown as ImpersonationTokenPayload
}
