import { ProgramSession } from 'src/common/entities';
import { ZoomAnalyticsResourceType } from 'src/common/enum/zoom-analytics-resource-type.enum';
import {
  ZoomSessionKpis,
  ZoomGeneralAttendeeKpis,
  ZoomLiveStatus,
  ZoomAttendeeQuery,
  PaginatedZoomAttendeeRows,
  ZoomSessionDashboard,
  PaginatedZoomFollowUpRows,
} from './zoom-analytics.interface';

/**
 * The port every Zoom resource type (Webinar, Meeting, anything future)
 * implements. `ZoomAnalyticsProviderRegistry` resolves the one matching a
 * specific session's `onlineType` or a specific webhook event's own
 * `webinar.`/`meeting.` prefix — never a global config value — so both
 * resource types are handled correctly in the same deployment. See
 * ZoomWebinarAnalyticsProvider / ZoomMeetingAnalyticsProvider.
 */
export interface ZoomAnalyticsProvider {
  readonly key: ZoomAnalyticsResourceType;

  /** Post-session: pulls the provider's report/participant data and upserts the KPI + attendee summaries. Idempotent. */
  reconcile(session: ProgramSession): Promise<void>;

  /** `rmContactId`, when passed, scopes KPIs to the roster of that RM's own seekers (see fetchRoster). */
  getKpis(session: ProgramSession, rmContactId?: number): Promise<ZoomSessionKpis>;

  /** The general-attendee (shared/common-link joiner) counterpart to `getKpis` — no `rmContactId`: a general row has no registration, so it has no RM owner to scope by. */
  getGeneralKpis(session: ProgramSession): Promise<ZoomGeneralAttendeeKpis>;

  /** `rmContactId`, when passed, scopes the attendee table to that RM's own seekers (see fetchRoster). */
  getAttendeeTable(
    session: ProgramSession,
    query: ZoomAttendeeQuery,
    rmContactId?: number,
  ): Promise<PaginatedZoomAttendeeRows>;

  getLiveStatus(session: ProgramSession): Promise<ZoomLiveStatus>;

  /** Post-session dashboard (journey, drop-off reasons, participation activity) — reads only locally stored data, never Zoom. */
  getDashboard(session: ProgramSession): Promise<ZoomSessionDashboard>;

  /** Paginated "Seekers Who Need Attention" follow-up table — reads only locally stored data, never Zoom. */
  getFollowUps(session: ProgramSession, query: ZoomAttendeeQuery): Promise<PaginatedZoomFollowUpRows>;

  /**
   * Re-syncs every roster/general attendee-summary row from the live event log alone — the
   * same read-time refresh `getAttendeeTable` runs on every one of its own calls, exposed here
   * so a caller reading the attendee-summary table WITHOUT going through `getAttendeeTable`
   * first (e.g. a program-wide aggregate over many sessions at once) can still get live data
   * instead of whatever `reconcile()`/an admin's last Attendees-tab visit happened to leave
   * behind. `rmContactId`, when passed, scopes the synced roster the same way `getAttendeeTable` does.
   */
  syncAttendeeSummaries(session: ProgramSession, rmContactId?: number): Promise<void>;

  /** Live webhook `*.started`: marks the session as in-progress for getLiveStatus's isLive check. Idempotent. */
  markStarted(session: ProgramSession, startedAt: Date): Promise<void>;

  /** Live webhook `*.ended`: marks the session as finished, synchronously — independent of reconcile()'s Report API lag. Idempotent. */
  markEnded(session: ProgramSession, endedAt: Date): Promise<void>;

  /**
   * Live webhook: records a single observed join event. `occurrenceId`
   * disambiguates shared/recurring webinar siblings when Zoom sends one;
   * `meetingStartTime` (the instance's own reported actual start time, stable
   * across every event in its lifecycle) is a fallback tiebreaker for when it
   * doesn't — Zoom's `meeting.*` events often omit `occurrenceId` entirely.
   */
  recordParticipantJoined(
    externalId: string,
    occurrenceId: string | null,
    email: string,
    occurredAt: Date,
    participantId: string | null,
    zoomDisplayName: string | null,
    meetingStartTime?: Date | null,
  ): Promise<void>;

  /** Live webhook: records a single observed leave event. See recordParticipantJoined for `occurrenceId`/`meetingStartTime`; `leaveReason` is Zoom's reported reason the connection ended, for drop-off analysis. */
  recordParticipantLeft(
    externalId: string,
    occurrenceId: string | null,
    email: string,
    occurredAt: Date,
    participantId: string | null,
    zoomDisplayName: string | null,
    leaveReason: string | null,
    meetingStartTime?: Date | null,
  ): Promise<void>;

  /**
   * Dashboard-API self-heal: reconciles live state against a snapshot of
   * currently-present participants (email -> Zoom-reported name, or null if
   * the poll didn't return one), and refreshes a present seeker's stored name
   * if Zoom now reports a different one — the only signal available for an
   * in-session rename.
   */
  correctLiveState(session: ProgramSession, currentlyPresent: Map<string, string | null>): Promise<void>;
}
