/**
 * Constants for the Zoom integration: env var keys, defaults,
 * and Swagger/log messages. Error strings live in the global error registries.
 */

import { RegistrationStatusEnum } from 'src/common/enum/registration-status.enum';

export const ZOOM_ENV_KEYS = {
  ENABLE: 'ENABLE_ZOOM',
  ACCOUNT_ID: 'ZOOM_ACCOUNT_ID',
  CLIENT_ID: 'ZOOM_CLIENT_ID',
  CLIENT_SECRET: 'ZOOM_CLIENT_SECRET',
  BASE_URL: 'ZOOM_BASE_URL',
  AUTH_URL: 'ZOOM_AUTH_URL',
  ADMIN_EMAIL: 'ZOOM_ADMIN_EMAIL',
  SDK_KEY: 'ZOOM_SDK_KEY',
  SDK_SECRET: 'ZOOM_SDK_SECRET',
  WEBHOOK_SECRET_TOKEN: 'ZOOM_WEBHOOK_SECRET_TOKEN',
  RECONCILIATION_CRON: 'ZOOM_RECONCILIATION_CRON',
} as const;

export const ZOOM_DEFAULTS = {
  BASE_URL: 'https://api.zoom.us/v2',
  AUTH_URL: 'https://zoom.us/oauth/token',
  TIMEZONE: 'Asia/Calcutta',
  RECONCILIATION_CRON: '0 * * * *',
  // Refresh the cached OAuth token this many seconds before its real expiry.
  TOKEN_EXPIRY_SKEW_SECONDS: 60,
  // SDK join token validity window (2 days), matching the serverless behaviour.
  JOIN_TOKEN_TTL_SECONDS: 172800,
} as const;

/**
 * Set to `false` to revert registrant removal to a hard delete instead of
 * Zoom's cancel-status action (see `ZoomSessionBase.cancelInsteadOfDeleteRegistrant`).
 * A code-level switch, not an env var — flipping it means a redeploy.
 */
export const ZOOM_CANCEL_INSTEAD_OF_DELETE_REGISTRANT = false;

/**
 * Set to `true` to require manual registrant approval on newly created
 * webinars/meetings (see `ZoomSessionBase.manualApprovalEnabled`). When on,
 * resources are created with `approval_type` MANUAL, but every registrant OUR
 * backend adds — a real ProgramRegistration signup (`ZoomRegistrationService`)
 * or a generated/staff link (`ZoomGeneratedLinkService`) — is approved
 * immediately after creation via the `autoApprove` flag on
 * `ZoomSessionHandler.addParticipant`. The `pending` state this setting
 * introduces only ever applies to a registrant Zoom accepts through some path
 * OTHER than our `addParticipant` (e.g. someone hitting the resource's
 * `registration_url` directly) — that registrant needs a manual approve in the
 * Zoom dashboard/API before their join_url works. When `false` (default),
 * behaviour is unchanged from before this flag existed: `approval_type`
 * AUTOMATIC everywhere, no approve call is ever issued. A code-level switch,
 * not an env var — flipping it means a redeploy.
 */
export const ZOOM_MANUAL_APPROVAL_REGISTRANTS = false;

/**
 * Zoom resource `type` values used in create payloads.
 * See https://developers.zoom.us/docs/api/ (Meetings / Webinars create).
 */
export const ZOOM_RESOURCE_TYPE = {
  SCHEDULED_MEETING: 2,
  SCHEDULED_WEBINAR: 5,
  /** Recurring meeting with a fixed time/recurrence rule — backs "same link" meeting programs. */
  RECURRING_FIXED_MEETING: 8,
  /** Recurring webinar with a fixed time/recurrence rule — backs "same link" webinar programs. */
  RECURRING_FIXED_WEBINAR: 9,
} as const;

/**
 * Zoom recurrence `type` values (recurrence.type in create payloads).
 * 1 = daily, 2 = weekly, 3 = monthly.
 */
export const ZOOM_RECURRENCE_TYPE = {
  DAILY: 1,
  WEEKLY: 2,
  MONTHLY: 3,
} as const;

/**
 * Zoom `settings.approval_type` for registration behaviour.
 * 0 = automatically approve, 1 = manually approve (registration on),
 * 2 = no registration required.
 */
export const ZOOM_APPROVAL_TYPE = {
  AUTOMATIC: 0,
  MANUAL: 1,
  NONE: 2,
} as const;

/**
 * Settings that silence EVERY Zoom-sent email for a resource. Applied on create
 * whenever a program pins an online template (the platform is then the sole
 * sender of join links; see buildZoomRegistrantEmail). Made explicit in the
 * create body — rather than relying only on the minted template's captured
 * settings — so a template that was misconfigured can never leak provider email.
 */
export const ZOOM_NO_COMMS_WEBINAR_SETTINGS = {
  registrants_confirmation_email: false,
  registrants_email_notification: false,
  attendees_and_panelists_reminder_email: { enable: false },
  follow_up_attendees_email: { enable: false },
  follow_up_absentees_email: { enable: false },
} as const;

/** Meeting-side equivalent of {@link ZOOM_NO_COMMS_WEBINAR_SETTINGS}. */
export const ZOOM_NO_COMMS_MEETING_SETTINGS = {
  registrants_confirmation_email: false,
  registrants_email_notification: false,
} as const;

/**
 * Zoom `settings.registration_type` for a recurring ("same link") webinar/meeting.
 * 1 = register once, attend any occurrence — required for the "register once,
 * covers all occurrences" behaviour; Zoom otherwise falls back to the account's
 * own default, which is not guaranteed to be 1.
 */
export const ZOOM_REGISTRATION_TYPE = {
  REGISTER_ONCE_ATTEND_ANY: 1,
} as const;

/** Pagination defaults for Zoom list endpoints. */
export const ZOOM_PAGINATION = {
  DEFAULT_PAGE_SIZE: 300,
} as const;

/** JSON array keys under which Zoom list endpoints nest their collections. */
export const ZOOM_COLLECTION_KEY = {
  REGISTRANTS: 'registrants',
  PARTICIPANTS: 'participants',
  PANELISTS: 'panelists',
  ABSENTEES: 'absentees',
} as const;

/** HTTP status codes the Zoom client special-cases. */
export const ZOOM_HTTP_STATUS = {
  UNAUTHORIZED: 401,
  NOT_FOUND: 404,
  TOO_MANY_REQUESTS: 429,
  /** Lower bound of the 5xx server-error range treated as transient. */
  SERVER_ERROR_MIN: 500,
} as const;

/**
 * Transient-failure retry policy for the Zoom REST client. A 401 is handled
 * separately (single token refresh); these govern 429 (rate-limit) and 5xx
 * (transient server) responses with exponential backoff capped at MAX_DELAY_MS.
 */
export const ZOOM_RETRY = {
  /** Total attempts for a transient (429/5xx) failure, including the first try. */
  MAX_ATTEMPTS: 3,
  BASE_DELAY_MS: 500,
  MAX_DELAY_MS: 8000,
} as const;

/**
 * HTTP methods safe to replay on a transient 5xx. POST is excluded — replaying a
 * create (webinar/meeting/registrant) risks duplicate resources. A 429 is always
 * retried regardless of method, since the request was rejected before processing.
 */
export const ZOOM_IDEMPOTENT_METHODS: readonly string[] = ['GET', 'PUT', 'PATCH', 'DELETE'];

/** Zoom application error codes the client special-cases. */
export const ZOOM_API_CODE = {
  /** Past-webinar/meeting report not generated yet — treat as empty, not an error. */
  PAST_DATA_NOT_READY: 3001,
} as const;

export const ZOOM_API_PATHS = {
  USER_WEBINARS: (email: string) => `/users/${email}/webinars`,
  USER_WEBINAR_TEMPLATES: (email: string) => `/users/${email}/webinar_templates`,
  WEBINAR: (webinarId: string) => `/webinars/${webinarId}`,
  WEBINAR_PANELISTS: (webinarId: string) => `/webinars/${webinarId}/panelists`,
  WEBINAR_REGISTRANTS: (webinarId: string) => `/webinars/${webinarId}/registrants`,
  WEBINAR_REGISTRANTS_STATUS: (webinarId: string) => `/webinars/${webinarId}/registrants/status`,
  PAST_WEBINAR_PARTICIPANTS: (webinarId: string) => `/past_webinars/${webinarId}/participants`,
  PAST_WEBINAR_ABSENTEES: (webinarId: string) => `/past_webinars/${webinarId}/absentees`,
  // ---- Meetings ----
  USER_MEETINGS: (email: string) => `/users/${email}/meetings`,
  USER_MEETING_TEMPLATES: (email: string) => `/users/${email}/meeting_templates`,
  MEETING: (meetingId: string) => `/meetings/${meetingId}`,
  MEETING_REGISTRANTS: (meetingId: string) => `/meetings/${meetingId}/registrants`,
  MEETING_REGISTRANTS_STATUS: (meetingId: string) => `/meetings/${meetingId}/registrants/status`,
  PAST_MEETING_PARTICIPANTS: (meetingUuidOrId: string) =>
    `/past_meetings/${meetingUuidOrId}/participants`,
} as const;

export const ZOOM_SWAGGER = {
  TAG: 'zoom',
  CREATE_WEBINAR: 'Create a Zoom session (webinar or meeting)',
  LIST_TEMPLATES:
    'Admin: list existing Zoom webinar templates (id + name)',
  UPDATE_WEBINAR: 'Update a Zoom session (webinar or meeting)',
  DELETE_WEBINAR: 'Delete a Zoom session (webinar or meeting)',
  GET_WEBINAR: 'Get a Zoom session by ID',
  LIST_WEBINARS: 'List Zoom sessions (webinars and meetings)',
  REGISTER: 'Register/unregister/downgrade a user for a webinar or meeting',
  BULK_REGISTER:
    'Admin: bulk-register all confirmed registrants (by programId or sessionId) to Zoom as a background job',
  BULK_REGISTER_STATUS: 'Admin: poll the status/progress of a bulk Zoom registration job',
  LIST_REGISTRATIONS:
    'Admin: list a session\'s eligible registrants with their Zoom join URL (paginated/searchable; download=true for Excel)',
  SYNC_TRACKING: 'Admin: pull latest attendance & analytics from Zoom for a session (reconcile)',
  GET_ANALYTICS: 'Admin: get the reconciled Zoom attendance & analytics for a session',
  JOIN_TOKEN: 'Generate an SDK join token for a meeting/webinar',
  WEBHOOK: 'Receive Zoom webhook events',
} as const;

export const ZOOM_LOG = {
  WEBINAR_CREATED: 'Zoom webinar created',
  WEBINAR_UPDATED: 'Zoom webinar updated',
  WEBINAR_DELETED: 'Zoom webinar deleted',
  MEETING_CREATED: 'Zoom meeting created',
  MEETING_UPDATED: 'Zoom meeting updated',
  MEETING_DELETED: 'Zoom meeting deleted',
  WEBHOOK_RECEIVED: 'Zoom webhook received',
  TRACKING_SYNCED: 'Zoom tracking synced',
  RECONCILIATION_STARTED: 'Zoom reconciliation cron started',
  RECONCILIATION_COMPLETED: 'Zoom reconciliation cron completed',
  // ---- Session lifecycle error/diagnostic logs ----
  API_REQUEST_FAILED: 'Zoom API request failed',
  API_RETRY_SUCCEEDED: 'Zoom API request succeeded after token refresh',
  API_RETRY_SCHEDULED: 'Zoom API request hit a transient failure; retrying after backoff',
  PAST_DATA_UNAVAILABLE: 'Zoom past-webinar data not available yet',
  SESSION_PERSIST_FAILED: 'Error persisting zoom session details',
  ROLLBACK_FAILED: 'Failed to roll back orphaned zoom resource',
  WEBINAR_UPDATE_FAILED: 'Error updating zoom webinar',
  MEETING_UPDATE_FAILED: 'Error updating zoom meeting',
  PANELIST_RESOLVE_FAILED: 'Failed to resolve panelist join details',
} as const;

/**
 * Registration statuses that are NOT eligible to be pushed to Zoom or shown in
 * the join-URL view. Eligibility = seat allocated, not soft-deleted, and status
 * outside this set.
 */
export const REGISTRATION_INELIGIBLE_STATUSES: RegistrationStatusEnum[] = [
  RegistrationStatusEnum.REJECTED,
  RegistrationStatusEnum.CANCELLED,
  RegistrationStatusEnum.SAVE_AS_DRAFT,
  RegistrationStatusEnum.ARCHIVED,
];

/** Attendance verdict stored on each reconciled analytics row. */
export const JOIN_STATUS = { ATTENDED: 'attended', ABSENT: 'absent' } as const;

/**
 * A participant whose watched duration is below this fraction of the planned
 * session length is flagged as a drop-off.
 */
export const DROPOFF_ATTENDANCE_RATIO = 0.8;

/** Number of registrants processed per batch in a bulk Zoom registration job. */
export const DEFAULT_BATCH_SIZE = 10;

/**
 * How many panelists to add per `POST /webinars/{id}/panelists` array call.
 * Zoom caps a webinar at 100 panelists; a smaller chunk keeps the failure
 * blast-radius of any single array call small.
 */
export const PANELIST_ADD_BATCH_SIZE = 30;

/**
 * Pause between batches in a bulk Zoom registration job, to stay under Zoom's
 * per-account API rate limits when registering large programs.
 */
export const BULK_BATCH_DELAY_MS = 1000;

/** Upper bound when scanning a program for its single provisioned session. */
export const PROVISIONED_SESSION_SCAN_LIMIT = 1000;

/**
 * How many attendees to mark concurrently during reconciliation. A large session
 * (1–2k participants) would otherwise mark attendance one serial DB round-trip at
 * a time; chunked parallelism bounds both wall-clock time and connection-pool use.
 */
export const ATTENDANCE_MARK_CONCURRENCY = 25;
