import {
  Entity,
  PrimaryGeneratedColumn,
  Column,
  CreateDateColumn,
  UpdateDateColumn,
  DeleteDateColumn,
  OneToOne,
  JoinColumn,
  Index,
} from 'typeorm';
import { OnlineTypeEnum } from '../enum/online-type.enum';
import { SessionProviderType } from '../enum/session-provider.enum';
import { ZoomWebinarStatus } from '../enum/zoom-webinar-status.enum';
import { SessionLaunchMode } from '../enum/session-launch-mode.enum';
import { SessionLinkModeEnum } from '../enum/session-link-mode.enum';
import { Program } from './program.entity';
import { ProgramSession } from './program-session.entity';

/**
 * Canonical online-session record for a program (template defaults) or a program
 * session (the live, provisioned resource). Replaces the meeting/webinar/stream
 * JSONB columns that previously lived on `program_v1` and `program_session`.
 *
 * A program-template row sets `programId` only (`programSessionId` null). A live
 * session row sets `programSessionId` (unique → 1:1 with the session) and also
 * carries `programId` as a denormalized "which program" reference. `programId` is
 * therefore unique only among template rows (see the partial index below).
 * `type` discriminates meeting vs webinar vs live stream; provider-specific
 * extras that don't warrant a column live in `details`.
 */
@Entity('hdb_online_session')
export class OnlineSession {
  @PrimaryGeneratedColumn('increment', { type: 'bigint' })
  id: number;

  // ---- Owners ----
  // Template uniqueness: at most one template row per program (program_session_id
  // IS NULL). Session rows also carry program_id and are excluded from this index.
  @Index('uq_online_session_program_id_template', {
    unique: true,
    where: '"program_id" IS NOT NULL AND "program_session_id" IS NULL AND "deleted_at" IS NULL',
  })
  // Plain lookup index for "all online sessions of a program".
  @Index('idx_online_session_program_id')
  @Column({ name: 'program_id', type: 'int', nullable: true })
  programId: number | null;

  @OneToOne(() => Program, (program) => program.onlineSession, { nullable: true })
  @JoinColumn({ name: 'program_id' })
  program: Program | null;

  @Index({ unique: true, where: '"program_session_id" IS NOT NULL AND "deleted_at" IS NULL' })
  @Column({ name: 'program_session_id', type: 'int', nullable: true })
  programSessionId: number | null;

  @OneToOne(() => ProgramSession, (session) => session.onlineSession, { nullable: true })
  @JoinColumn({ name: 'program_session_id' })
  programSession: ProgramSession | null;

  // ---- Discriminator + provider ----
  @Column({ name: 'type', type: 'varchar', length: 50, default: OnlineTypeEnum.NA })
  type: OnlineTypeEnum;

  @Column({ name: 'provider', type: 'varchar', length: 50, nullable: true })
  provider: SessionProviderType | null;

  // ---- Provider resource (meeting/webinar) ----
  /** External provider id (Zoom meetingId / webinarId). Indexed for webhook lookups. */
  @Index()
  @Column({ name: 'external_id', type: 'varchar', length: 255, nullable: true })
  externalId: string | null;

  /** Attendee join link (meetingLink / webinarLink). */
  @Column({ name: 'join_url', type: 'text', nullable: true })
  joinUrl: string | null;

  @Column({ name: 'password', type: 'varchar', length: 255, nullable: true })
  password: string | null;

  @Column({ name: 'registration_url', type: 'text', nullable: true })
  registrationUrl: string | null;

  /** Webinar-only panelist link. */
  @Column({ name: 'panelist_url', type: 'text', nullable: true })
  panelistUrl: string | null;

  @Column({ name: 'host_email', type: 'varchar', length: 255, nullable: true })
  hostEmail: string | null;

  /** Host start URL — sensitive, never exposed to attendee-facing responses. */
  @Column({ name: 'start_url', type: 'text', nullable: true })
  startUrl: string | null;

  @Index()
  @Column({ name: 'status', type: 'enum', enum: ZoomWebinarStatus, nullable: true })
  status: ZoomWebinarStatus | null;

  /** Meetings only — per-user registrants vs a single shared join link. */
  @Column({ name: 'require_registration', type: 'boolean', nullable: true })
  requireRegistration: boolean | null;

  /**
   * Whether this session owns its provider resource (`PER_SESSION`, the default)
   * or shares one recurring resource with sibling sessions (`SHARED`). In SHARED
   * mode every sibling row carries the same `external_id`/`join_url`, so a
   * registrant registered once holds a single link valid for all of them. Null is
   * read as `PER_SESSION`.
   */
  @Column({ name: 'link_mode', type: 'varchar', length: 30, nullable: true })
  linkMode: SessionLinkModeEnum | null;

  /**
   * Provider occurrence id this session maps to within a SHARED recurring
   * resource (Zoom occurrence_id). Lets attendance sync scope participants to the
   * one occurrence backing this session. Null for PER_SESSION resources.
   */
  @Column({ name: 'occurrence_id', type: 'varchar', length: 255, nullable: true })
  occurrenceId: string | null;

  /** Where attendees launch from: embedded SDK vs Zoom client. Null → SDK at read time. */
  @Column({ name: 'launch_mode', type: 'varchar', length: 30, nullable: true })
  launchMode: SessionLaunchMode | null;

  /**
   * How many minutes before the session start the join link opens. Null → the
   * platform default (15) is applied when the join window is enforced.
   */
  @Column({ name: 'join_opens_minutes_before', type: 'smallint', nullable: true })
  joinOpensMinutesBefore: number | null;

  /**
   * How many minutes before the session start the host/admin may start the session.
   * Store-only: the frontend uses it to enable the "Start" action from that time.
   * Null → no early-start window configured.
   */
  @Column({ name: 'host_start_opens_minutes_before', type: 'smallint', nullable: true })
  hostStartOpensMinutesBefore: number | null;

  /** Actual end time reported by the provider's `*.ended` webhook. */
  @Column({ name: 'actual_meeting_ends_at', type: 'timestamptz', nullable: true })
  actualMeetingEndsAt: Date | null;

  /**
   * Live count of `hdb_program_registration_online_session` rows for this session
   * with `status = 'registered'` and `activation_status = 'active'`. Maintained
   * atomically alongside every activation toggle rather than computed on read.
   */
  @Column({ name: 'eligible_active_count', type: 'int', default: 0 })
  eligibleActiveCount: number;

  /**
   * Whether this session has been run through the final-session-confirm flow
   * (see ZoomFinalSessionConfirmService). Set true when that API is called for
   * the program's last session; never set for any other session.
   */
  @Column({ name: 'finalized', type: 'boolean', default: false })
  finalized: boolean;

  // ---- Live stream ----
  @Column({ name: 'stream_url', type: 'text', nullable: true })
  streamUrl: string | null;

  @Column({ name: 'backup_stream_url', type: 'text', nullable: true })
  backupStreamUrl: string | null;

  @Column({ name: 'chat_url', type: 'text', nullable: true })
  chatUrl: string | null;

  /** Provider-specific extras that don't warrant a dedicated column (e.g. webinar video limits). */
  @Column({ name: 'details', type: 'jsonb', nullable: true })
  details: Record<string, unknown> | null;

  // ---- Audit ----
  @CreateDateColumn({ name: 'created_at', type: 'timestamptz', default: () => 'CURRENT_TIMESTAMP' })
  createdAt: Date;

  @UpdateDateColumn({ name: 'updated_at', type: 'timestamptz', default: () => 'CURRENT_TIMESTAMP', onUpdate: 'CURRENT_TIMESTAMP' })
  updatedAt: Date;

  @DeleteDateColumn({ name: 'deleted_at', type: 'timestamptz', nullable: true, default: null })
  deletedAt: Date;

  @Column({ name: 'created_by', type: 'int', nullable: true })
  createdBy: number | null;

  @Column({ name: 'updated_by', type: 'int', nullable: true })
  updatedBy: number | null;

  constructor(partial?: Partial<OnlineSession>) {
    Object.assign(this, partial ?? {});
  }
}
