/**
 * Zoom keys registrants by email per resource: registering a second person with
 * an email that already exists updates the first registrant and hands back the
 * SAME join link. Since one real email can back several distinct registrations
 * (proxy/child regs), we must give Zoom a per-registration-UNIQUE address so
 * each gets its own registrant and join link.
 *
 * This is applied to every registration: all Zoom-sent email is suppressed on
 * the resource (the platform is the sole sender of join links), so the derived
 * address is a uniqueness key, never a delivery target — provider sub-addressing
 * support and deliverability are irrelevant, Zoom just accepts and stores it.
 *
 * Rule: keep the domain, take the local part before the FIRST '+' (collapsing
 * any existing tag so we never double-tag), and append a single deterministic
 * '+reg<registrationId>' tag. The registration id guarantees uniqueness; the
 * derivation is deterministic so it re-derives identically for later lookups
 * (e.g. resolving a panelist's join URL by email).
 *
 * Examples:
 *   ('madhuri.karedla@divami.com', 1234)    -> 'madhuri.karedla+reg1234@divami.com'
 *   ('madhuri.karedla+1@divami.com', 2322)  -> 'madhuri.karedla+reg2322@divami.com'
 */
export function buildZoomRegistrantEmail(email: string, registrationId: number): string {
  return buildZoomTaggedEmail(email, `reg${registrationId}`);
}

/**
 * Same per-recipient-uniqueness scheme as `buildZoomRegistrantEmail`, keyed by an
 * admin/coordinator's own user id and the program instead of a registrationId — for
 * pre-generating a Zoom general link for a real staff user who has no
 * `ProgramRegistration` row at all (see `ZoomGeneratedLinkService`).
 */
export function buildZoomUserRegistrantEmail(email: string, userId: number, programId: number): string {
  return buildZoomTaggedEmail(email, `gen${userId}p${programId}`);
}

/**
 * Same tagging scheme as `buildZoomUserRegistrantEmail`, for a PLACEHOLDER slot instead of a
 * real user — keyed by its sequence number and the program instead of a userId (see
 * `ZoomGeneratedLinkService`).
 */
export function buildZoomPlaceholderRegistrantEmail(
  slug: string,
  domain: string,
  sequenceNumber: number,
  programId: number,
): string {
  return buildZoomTaggedEmail(`${slug}@${domain}`, `gen${sequenceNumber}p${programId}`);
}

/**
 * Shared primitive behind `buildZoomRegistrantEmail`/`buildZoomUserRegistrantEmail`:
 * keep the domain, take the local part before the FIRST '+' (collapsing any
 * existing tag so we never double-tag), and append `+<tag>`, trimmed to stay
 * within the RFC 5321 64-char local-part limit without ever truncating the tag
 * itself (the tag is the uniqueness key).
 */
function buildZoomTaggedEmail(email: string, tag: string): string {
  const trimmed = (email ?? '').trim();
  const at = trimmed.lastIndexOf('@');
  // Not a usable address (no local part or no '@') — leave untouched rather than
  // fabricate one; the caller falls back to the raw value.
  if (at <= 0 || at === trimmed.length - 1) return trimmed;

  const local = trimmed.slice(0, at);
  const domain = trimmed.slice(at + 1);
  const base = local.split('+')[0] || local;
  const fullTag = `+${tag}`;

  const maxBaseLength = Math.max(1, 64 - fullTag.length);
  const safeBase = base.length > maxBaseLength ? base.slice(0, maxBaseLength) : base;

  return `${safeBase}${fullTag}@${domain}`;
}

/**
 * Reverses `buildZoomRegistrantEmail`'s tag: Zoom's webhooks/reports always
 * report back the derived `+reg<registrationId>` address, not the seeker's
 * original registered email — so anything matching a Zoom-reported email
 * against our own registration records must match on this id, not on raw
 * email equality. Returns null when the address carries no (parseable) tag,
 * e.g. a non-panelist join or a dashboard-poll correction.
 */
export function parseZoomRegistrantRegistrationId(email: string | null | undefined): number | null {
  const trimmed = (email ?? '').trim();
  const at = trimmed.lastIndexOf('@');
  if (at <= 0) return null;

  const local = trimmed.slice(0, at);
  const match = /\+reg(\d+)$/.exec(local);
  if (!match) return null;

  const registrationId = Number(match[1]);
  return Number.isSafeInteger(registrationId) ? registrationId : null;
}
