import { CommunicationTypeEnum } from 'src/common/enum/communication-type.enum';
import { CommunicationTemplateAccessKeyEnum } from 'src/common/enum/communication-template-access-key.enum';
import { SessionCommunicationPurposeEnum } from 'src/common/enum/session-communication-purpose.enum';

/**
 * Which template variant a session uses for occurrence-dependent purposes:
 * - REGULAR:   any session except the last two (first + middle share one template)
 * - PRE_FINAL: the last but one (penultimate) session of the program
 * - FINAL:     the last session of the program
 * Value Card and Welcome are occurrence-independent.
 *
 * PRE_FINAL is currently only wired for the ABSENT purpose; INVITE still uses REGULAR for the
 * penultimate session (no pre-final Invite template exists).
 */
export enum SessionOccurrence {
  REGULAR = 'REGULAR',
  PRE_FINAL = 'PRE_FINAL',
  FINAL = 'FINAL',
}

/**
 * A channel to fire, with the template access key used to resolve it.
 */
export interface ChannelConfig {
  channel: CommunicationTypeEnum;
  accessKey: CommunicationTemplateAccessKeyEnum;
}

/**
 * One row per (purpose, occurrence, channel) → template access key. Single source
 * of truth for both sending (resolveChannels) and the summary (deriving a
 * communication's purpose/occurrence back from its recorded template).
 *
 * `occurrence: null` means the template does not depend on session position
 * (Welcome, Value Card). Absent/Invite have distinct regular- and final-session templates.
 */
export interface TemplateDescriptor {
  purpose: SessionCommunicationPurposeEnum;
  occurrence: SessionOccurrence | null;
  channel: CommunicationTypeEnum;
  accessKey: CommunicationTemplateAccessKeyEnum;
}

const A = CommunicationTemplateAccessKeyEnum;

export const SESSION_COMMUNICATION_TEMPLATES: TemplateDescriptor[] = [
  // Welcome — program-level, before the sessions start (occurrence-independent)
  {
    purpose: SessionCommunicationPurposeEnum.WELCOME,
    occurrence: null,
    channel: CommunicationTypeEnum.EMAIL,
    accessKey: A.WELCOME_EMAIL_SEEKER,
  },
  {
    purpose: SessionCommunicationPurposeEnum.WELCOME,
    occurrence: null,
    channel: CommunicationTypeEnum.WHATSAPP,
    accessKey: A.WELCOME_WATI_SEEKER,
  },
  // Absent — regular session (all sessions except the last)
  {
    purpose: SessionCommunicationPurposeEnum.ABSENT,
    occurrence: SessionOccurrence.REGULAR,
    channel: CommunicationTypeEnum.EMAIL,
    accessKey: A.ABSENT_REGULAR_SESSION_EMAIL_SEEKER,
  },
  {
    purpose: SessionCommunicationPurposeEnum.ABSENT,
    occurrence: SessionOccurrence.REGULAR,
    channel: CommunicationTypeEnum.WHATSAPP,
    accessKey: A.ABSENT_REGULAR_SESSION_WATI_SEEKER,
  },
  // Absent — pre-final session (the last but one / penultimate session)
  {
    purpose: SessionCommunicationPurposeEnum.ABSENT,
    occurrence: SessionOccurrence.PRE_FINAL,
    channel: CommunicationTypeEnum.EMAIL,
    accessKey: A.ABSENT_PRE_FINAL_SESSION_EMAIL_SEEKER,
  },
  {
    purpose: SessionCommunicationPurposeEnum.ABSENT,
    occurrence: SessionOccurrence.PRE_FINAL,
    channel: CommunicationTypeEnum.WHATSAPP,
    accessKey: A.ABSENT_PRE_FINAL_SESSION_WATI_SEEKER,
  },
  // Absent — final session
  {
    purpose: SessionCommunicationPurposeEnum.ABSENT,
    occurrence: SessionOccurrence.FINAL,
    channel: CommunicationTypeEnum.EMAIL,
    accessKey: A.ABSENT_FINAL_SESSION_EMAIL_SEEKER,
  },
  {
    purpose: SessionCommunicationPurposeEnum.ABSENT,
    occurrence: SessionOccurrence.FINAL,
    channel: CommunicationTypeEnum.WHATSAPP,
    accessKey: A.ABSENT_FINAL_SESSION_WATI_SEEKER,
  },
  // Invite — regular session (all sessions except the last)
  {
    purpose: SessionCommunicationPurposeEnum.INVITE,
    occurrence: SessionOccurrence.REGULAR,
    channel: CommunicationTypeEnum.EMAIL,
    accessKey: A.INVITE_REGULAR_SESSION_EMAIL_SEEKER,
  },
  {
    purpose: SessionCommunicationPurposeEnum.INVITE,
    occurrence: SessionOccurrence.REGULAR,
    channel: CommunicationTypeEnum.WHATSAPP,
    accessKey: A.INVITE_REGULAR_SESSION_WATI_SEEKER,
  },
  // Invite — final session
  {
    purpose: SessionCommunicationPurposeEnum.INVITE,
    occurrence: SessionOccurrence.FINAL,
    channel: CommunicationTypeEnum.EMAIL,
    accessKey: A.INVITE_FINAL_SESSION_EMAIL_SEEKER,
  },
  {
    purpose: SessionCommunicationPurposeEnum.INVITE,
    occurrence: SessionOccurrence.FINAL,
    channel: CommunicationTypeEnum.WHATSAPP,
    accessKey: A.INVITE_FINAL_SESSION_WATI_SEEKER,
  },
  // Value card — email only, occurrence-independent
  {
    purpose: SessionCommunicationPurposeEnum.VALUE_CARD,
    occurrence: null,
    channel: CommunicationTypeEnum.EMAIL,
    accessKey: A.VALUE_CARD_EMAIL_SEEKER,
  },
  // Program completion — occurrence-independent
  {
    purpose: SessionCommunicationPurposeEnum.PROGRAM_COMPLETION,
    occurrence: null,
    channel: CommunicationTypeEnum.EMAIL,
    accessKey: A.PROGRAM_COMPLETION_EMAIL_SEEKER,
  },
  {
    purpose: SessionCommunicationPurposeEnum.PROGRAM_COMPLETION,
    occurrence: null,
    channel: CommunicationTypeEnum.WHATSAPP,
    accessKey: A.PROGRAM_COMPLETION_WATI_SEEKER,
  },
  // Common invite — program-level, occurrence-independent (staff issued a common Zoom link)
  {
    purpose: SessionCommunicationPurposeEnum.COMMON_INVITE,
    occurrence: null,
    channel: CommunicationTypeEnum.EMAIL,
    accessKey: A.COMMON_INVITE_EMAIL_SEEKER,
  },
  {
    purpose: SessionCommunicationPurposeEnum.COMMON_INVITE,
    occurrence: null,
    channel: CommunicationTypeEnum.WHATSAPP,
    accessKey: A.COMMON_INVITE_WATI_SEEKER,
  },
  // System links — program-level, email only, occurrence-independent (admin-only)
  {
    purpose: SessionCommunicationPurposeEnum.SYSTEM_LINKS,
    occurrence: null,
    channel: CommunicationTypeEnum.EMAIL,
    accessKey: A.SYSTEM_LINKS_EMAIL_ADMIN,
  },
];

/**
 * General-link purposes (Welcome/Invite/Absent/Program-completion) reuse the SAME templates
 * (access keys) as their seeker counterparts — no separate GENERAL_LINK_* template set. The
 * merge-data service resolves the recipient-identity fields (reg_name/zoom_join_link/meeting_id/
 * meeting_passcode) via the common_* extraMergeContext instead of a registrationId lookup
 * whenever registrationId is absent (see CommunicationMergeDataService's
 * REGISTRATION_LESS_FIELD_ALIASES), which is exactly the context processCommonInvite already
 * builds for these registration-less recipients (commonInviteMergeContext). This mapping is the
 * only thing that ties a GENERAL_LINK_* purpose to its underlying seeker purpose/templates.
 */
export const GENERAL_LINK_TO_SEEKER_PURPOSE: Partial<
  Record<SessionCommunicationPurposeEnum, SessionCommunicationPurposeEnum>
> = {
  [SessionCommunicationPurposeEnum.GENERAL_LINK_WELCOME]: SessionCommunicationPurposeEnum.WELCOME,
  [SessionCommunicationPurposeEnum.GENERAL_LINK_INVITE]: SessionCommunicationPurposeEnum.INVITE,
  [SessionCommunicationPurposeEnum.GENERAL_LINK_ABSENT]: SessionCommunicationPurposeEnum.ABSENT,
  [SessionCommunicationPurposeEnum.GENERAL_LINK_PROGRAM_COMPLETION]:
    SessionCommunicationPurposeEnum.PROGRAM_COMPLETION,
  [SessionCommunicationPurposeEnum.GENERAL_LINK_VALUE_CARD]:
    SessionCommunicationPurposeEnum.VALUE_CARD,
};

/**
 * The general-link purpose subset exposed by /session-communication/general-link/bulk and
 * /session-communication/general-link/single.
 * WELCOME/PROGRAM_COMPLETION are program-level (no sessionId); INVITE/ABSENT are session-scoped.
 */
export const GENERAL_LINK_PURPOSES: SessionCommunicationPurposeEnum[] = [
  SessionCommunicationPurposeEnum.GENERAL_LINK_WELCOME,
  SessionCommunicationPurposeEnum.GENERAL_LINK_INVITE,
  SessionCommunicationPurposeEnum.GENERAL_LINK_ABSENT,
  SessionCommunicationPurposeEnum.GENERAL_LINK_PROGRAM_COMPLETION,
];

/**
 * GENERAL_LINK_VALUE_CARD is deliberately NOT in GENERAL_LINK_PURPOSES above: it needs a
 * description and file attachments, which the generic general-link endpoints have no fields for.
 * It gets its own pair of routes instead, exactly as the seeker VALUE_CARD does
 * (/session-communication/value-card/{bulk,single}).
 */

export const GENERAL_LINK_PROGRAM_LEVEL_PURPOSES: SessionCommunicationPurposeEnum[] = [
  SessionCommunicationPurposeEnum.GENERAL_LINK_WELCOME,
  SessionCommunicationPurposeEnum.GENERAL_LINK_PROGRAM_COMPLETION,
];

/** True when a general-link purpose targets the whole program (no session id required). */
export function isGeneralLinkProgramLevelPurpose(purpose: SessionCommunicationPurposeEnum): boolean {
  return GENERAL_LINK_PROGRAM_LEVEL_PURPOSES.includes(purpose);
}

/**
 * True when a general-link single send requires an explicit REGULAR/FINAL occurrence — derived
 * from whether the underlying seeker purpose (INVITE/ABSENT) itself needs an occurrence.
 */
export function generalLinkPurposeNeedsOccurrence(purpose: SessionCommunicationPurposeEnum): boolean {
  const seekerPurpose = GENERAL_LINK_TO_SEEKER_PURPOSE[purpose];
  return seekerPurpose ? purposeNeedsOccurrence(seekerPurpose) : false;
}

/**
 * Channels + access keys to fire for a general-link purpose, resolved through the seeker
 * purpose's own templates (see GENERAL_LINK_TO_SEEKER_PURPOSE) — same access keys, same
 * hdb_communication_templates rows as the seeker sends. Bulk resolves the real REGULAR/
 * PRE_FINAL/FINAL occurrence the same way seeker sends do (via resolveOccurrence); single passes
 * the caller-supplied REGULAR/FINAL manually.
 */
export function resolveGeneralLinkChannels(
  purpose: SessionCommunicationPurposeEnum,
  occurrence: SessionOccurrence | null = null,
): ChannelConfig[] {
  const seekerPurpose = GENERAL_LINK_TO_SEEKER_PURPOSE[purpose];
  return seekerPurpose ? resolveChannels(seekerPurpose, occurrence) : [];
}

/**
 * Per-session variant of the COMMON_INVITE templates. Kept OUT of SESSION_COMMUNICATION_TEMPLATES
 * so `resolveChannels(COMMON_INVITE, null)` keeps returning only the program-level pair — the
 * per-session pair is chosen explicitly by `resolveCommonInviteChannels` when a send targets one
 * session. Same purpose/occurrence as the program-level rows; they differ only in which template
 * (and thus which session-scoped merge fields) is used. Folded into ACCESS_KEY_TO_DESCRIPTOR and
 * SESSION_COMMUNICATION_ACCESS_KEYS below so the summary still maps their track rows to COMMON_INVITE.
 */
export const COMMON_INVITE_PER_SESSION_TEMPLATES: TemplateDescriptor[] = [
  {
    purpose: SessionCommunicationPurposeEnum.COMMON_INVITE,
    occurrence: null,
    channel: CommunicationTypeEnum.EMAIL,
    accessKey: A.COMMON_INVITE_PER_SESSION_EMAIL_SEEKER,
  },
  {
    purpose: SessionCommunicationPurposeEnum.COMMON_INVITE,
    occurrence: null,
    channel: CommunicationTypeEnum.WHATSAPP,
    accessKey: A.COMMON_INVITE_PER_SESSION_WATI_SEEKER,
  },
];

/**
 * Per-session variant of the SYSTEM_LINKS template. Same shape/rationale as
 * COMMON_INVITE_PER_SESSION_TEMPLATES: kept out of SESSION_COMMUNICATION_TEMPLATES so
 * `resolveChannels(SYSTEM_LINKS, null)` keeps returning only the program-level template; the
 * per-session template is chosen explicitly by `resolveSystemLinksChannels` when a send targets
 * one session. Email only. Folded into ACCESS_KEY_TO_DESCRIPTOR / SESSION_COMMUNICATION_ACCESS_KEYS.
 */
export const SYSTEM_LINKS_PER_SESSION_TEMPLATES: TemplateDescriptor[] = [
  {
    purpose: SessionCommunicationPurposeEnum.SYSTEM_LINKS,
    occurrence: null,
    channel: CommunicationTypeEnum.EMAIL,
    accessKey: A.SYSTEM_LINKS_PER_SESSION_EMAIL_ADMIN,
  },
];

/**
 * Program-level purposes are sent for the whole program rather than a specific session:
 * Welcome (before the program starts) and Program completion (after it ends). They carry
 * no sessionId and their merge fields resolve at the program level.
 */
export const PROGRAM_LEVEL_PURPOSES: SessionCommunicationPurposeEnum[] = [
  SessionCommunicationPurposeEnum.WELCOME,
  SessionCommunicationPurposeEnum.PROGRAM_COMPLETION,
  SessionCommunicationPurposeEnum.COMMON_INVITE,
  SessionCommunicationPurposeEnum.SYSTEM_LINKS,
];

/**
 * True when a purpose targets the whole program (no session id required).
 */
export function isProgramLevelPurpose(purpose: SessionCommunicationPurposeEnum): boolean {
  return PROGRAM_LEVEL_PURPOSES.includes(purpose);
}

/**
 * True when a purpose's template set depends on whether the session is first/final.
 * (Absent/Invite do; Value Card does not.)
 */
export function purposeNeedsOccurrence(purpose: SessionCommunicationPurposeEnum): boolean {
  return SESSION_COMMUNICATION_TEMPLATES.some(
    (t) => t.purpose === purpose && t.occurrence !== null,
  );
}

/**
 * True when a purpose has a template configured for the given occurrence. Used to gate
 * occurrence resolution — e.g. only ABSENT has a PRE_FINAL variant, so the penultimate
 * session must fall back to REGULAR for every other purpose (INVITE), otherwise it would
 * resolve to an occurrence with no channels and silently send nothing.
 */
export function purposeSupportsOccurrence(
  purpose: SessionCommunicationPurposeEnum,
  occurrence: SessionOccurrence,
): boolean {
  return SESSION_COMMUNICATION_TEMPLATES.some(
    (t) => t.purpose === purpose && t.occurrence === occurrence,
  );
}

/**
 * Channels + access keys to fire for a purpose at a given occurrence.
 * Occurrence-independent templates (Value Card) are always included; occurrence-
 * specific templates are included only when they match the resolved occurrence.
 */
export function resolveChannels(
  purpose: SessionCommunicationPurposeEnum,
  occurrence: SessionOccurrence | null,
): ChannelConfig[] {
  return SESSION_COMMUNICATION_TEMPLATES.filter(
    (t) => t.purpose === purpose && (t.occurrence === null || t.occurrence === occurrence),
  ).map((t) => ({ channel: t.channel, accessKey: t.accessKey }));
}

/**
 * Channels + access keys for a Common-Invite send. When the send targets one session
 * (`perSession` — a sessionId is present) it uses the per-session template pair, whose merge
 * fields resolve that session's name/date/time; otherwise it uses the program-level pair.
 */
export function resolveCommonInviteChannels(perSession: boolean): ChannelConfig[] {
  const descriptors = perSession
    ? COMMON_INVITE_PER_SESSION_TEMPLATES
    : SESSION_COMMUNICATION_TEMPLATES.filter(
        (t) => t.purpose === SessionCommunicationPurposeEnum.COMMON_INVITE,
      );
  return descriptors.map((t) => ({ channel: t.channel, accessKey: t.accessKey }));
}

/**
 * Channels + access keys for a System-Links send (email only). Uses the per-session template when
 * the send targets one session (its merge fields resolve that session's name/date/time), else the
 * program-level template.
 */
export function resolveSystemLinksChannels(perSession: boolean): ChannelConfig[] {
  const descriptors = perSession
    ? SYSTEM_LINKS_PER_SESSION_TEMPLATES
    : SESSION_COMMUNICATION_TEMPLATES.filter(
        (t) => t.purpose === SessionCommunicationPurposeEnum.SYSTEM_LINKS,
      );
  return descriptors.map((t) => ({ channel: t.channel, accessKey: t.accessKey }));
}

/**
 * Reverse lookup: template access key -> { purpose, occurrence }. Used to derive a
 * communication's purpose/occurrence from the template recorded on a track row. Includes the
 * per-session COMMON_INVITE variant so its track rows also map back to COMMON_INVITE.
 *
 * General-link sends reuse the seeker WELCOME/INVITE/ABSENT/PROGRAM_COMPLETION access keys
 * (see GENERAL_LINK_TO_SEEKER_PURPOSE) rather than owning distinct template rows, so no separate
 * entries are needed here — their track rows already map back to the seeker purpose via
 * SESSION_COMMUNICATION_TEMPLATES, and are told apart from real seeker sends by having no
 * registration_id on the track row.
 */
export const ACCESS_KEY_TO_DESCRIPTOR: Partial<
  Record<
    CommunicationTemplateAccessKeyEnum,
    { purpose: SessionCommunicationPurposeEnum; occurrence: SessionOccurrence | null }
  >
> = [
  ...SESSION_COMMUNICATION_TEMPLATES,
  ...COMMON_INVITE_PER_SESSION_TEMPLATES,
  ...SYSTEM_LINKS_PER_SESSION_TEMPLATES,
].reduce(
  (acc, t) => {
    acc[t.accessKey] = { purpose: t.purpose, occurrence: t.occurrence };
    return acc;
  },
  {} as Partial<
    Record<
      CommunicationTemplateAccessKeyEnum,
      { purpose: SessionCommunicationPurposeEnum; occurrence: SessionOccurrence | null }
    >
  >,
);

/**
 * All template access keys owned by this feature — used to scope summary queries
 * against the shared hdb_communication_track table.
 */
export const SESSION_COMMUNICATION_ACCESS_KEYS: CommunicationTemplateAccessKeyEnum[] = [
  ...SESSION_COMMUNICATION_TEMPLATES,
  ...COMMON_INVITE_PER_SESSION_TEMPLATES,
  ...SYSTEM_LINKS_PER_SESSION_TEMPLATES,
].map((t) => t.accessKey);
