import { z } from 'zod';

// JSN-side server-to-server call: ask cmlite to mint a one-time-use SSO token.
// The token is included in a URL JSN then opens in the user's browser via
// window.open(...) so the user lands on cmlite already logged in.
export const issueSsoTokenSchema = z.object({
  email: z.string().email(),
  // Path within cmlite to land on after consuming the token. Sanitized below
  // before being used as a redirect target.
  redirectTo: z.string().max(512).default('/volunteer'),
});

export type IssueSsoTokenInput = z.infer<typeof issueSsoTokenSchema>;

// Querystring on the consume endpoint (GET /api/auth/bridge/sso?token=...&redirectTo=...).
export const consumeSsoTokenSchema = z.object({
  token: z.string().min(20).max(4096),
  redirectTo: z.string().max(512).default('/volunteer'),
});

export type ConsumeSsoTokenInput = z.infer<typeof consumeSsoTokenSchema>;

// Reject non-same-origin paths, protocol-relative tricks, traversal, control
// chars. Same defense pattern as JSN's sanitizeReturnTo in apps/api/src/routes/
// auth.ts — copied here so cmlite has the same protection on its own redirect.
export function sanitizeRedirectTo(raw: string | undefined): string {
  const FALLBACK = '/volunteer';
  if (typeof raw !== 'string' || raw.length === 0 || raw.length > 512) return FALLBACK;
  if (!raw.startsWith('/')) return FALLBACK;
  if (raw.startsWith('//')) return FALLBACK; // protocol-relative
  if (raw.startsWith('/\\')) return FALLBACK;
  if (raw.includes('..')) return FALLBACK;
  // eslint-disable-next-line no-control-regex
  if (/[\x00-\x1f\s]/.test(raw)) return FALLBACK;
  return raw;
}
