/**
 * Ingest envelope — the durable contract between the edge collector (producer)
 * and the canonical-store ingest-writer (consumer).
 *
 * The collector does NO database work. It captures the request at the edge,
 * wraps it in a {@link RawEventEnvelope}, and enqueues it to redis. All
 * enrichment (visitor identity, domain resolution, device fingerprint) and the
 * canonical `raw_events` write happen on the black-side ingest-writer when the
 * envelope is consumed. This makes the redis enqueue — not a synchronous DB
 * write — the durability boundary, so a canonical-store outage spools events
 * locally instead of dropping them.
 */

/** BullMQ queue shared by the collector (producer) and the black-side workers. */
export const EVENTS_QUEUE = 'analytics-events';

/**
 * Job name for a captured-at-edge event awaiting enrichment + canonical write.
 * Consumed by the black-side ingest-writer.
 */
export const INGEST_EVENT_JOB = 'ingest-event';

/**
 * Job name for an already-written `raw_events` row awaiting aggregation.
 * Emitted by the ingest-writer, consumed by the existing aggregation processor.
 */
export const PROCESS_EVENT_JOB = 'process-event';

/** Which collector endpoint produced the envelope — selects the writer's branch. */
export type IngestKind =
  | 'view'
  | 'engagement'
  | 'interaction'
  | 'event'
  | 'batch'
  | 'conversion'
  | 'funnel'
  | 'registration-funnel';

/**
 * First-touch attribution inputs (UTM + referrer), carried verbatim from the
 * edge for views. Resolved into a traffic source on the black-side writer.
 */
export interface AttributionInputData {
  readonly utmSource?: string;
  readonly utmMedium?: string;
  readonly utmCampaign?: string;
  readonly utmContent?: string;
  readonly utmTerm?: string;
  readonly referrer?: string;
}

/**
 * A single event, fully normalized at the edge into the generic shape the
 * canonical writer persists. All per-endpoint mapping (engagement → eventType,
 * funnel → eventType, batch expansion, etc.) happens on the collector because
 * it is pure; only DB-bound enrichment (device, identity, fingerprint) is
 * deferred to the writer.
 */
export interface NormalizedEvent {
  /** Canonical event type, e.g. 'pageview' | 'conversion' | 'engagement_like'. */
  readonly eventType: string;
  readonly sessionId: string;
  readonly userId?: string | null;
  readonly pageUrl?: string | null;
  readonly referrer?: string | null;
  readonly metadata?: Record<string, unknown> | null;
  /** Client-supplied event time (ms since epoch). Writer falls back to `receivedAt`. */
  readonly timestamp?: number | null;
  /**
   * Whether this event runs the full view pipeline on the writer: device
   * enrichment, session fingerprint upsert, and cross-domain visitor identity.
   * Mirrors the legacy split where only page views were enriched.
   */
  readonly isView: boolean;
  /** Client device signals (views only). */
  readonly clientDevice?: Record<string, unknown> | null;
  /** Attribution inputs (views only). */
  readonly attribution?: AttributionInputData | null;
}

/** Edge-captured request context the black-side writer needs for enrichment. */
export interface EdgeContext {
  /** Client IP extracted at the edge (x-real-ip / x-forwarded-for / socket). */
  readonly ip?: string;
  /** User-Agent header. */
  readonly ua?: string;
  /** Accept-Language header. */
  readonly lang?: string;
  /**
   * Headers the writer needs for domain resolution (origin / referer / host).
   * Lowercased keys; only the subset required downstream is carried.
   */
  readonly headers: Record<string, string | undefined>;
}

/**
 * A single captured analytics event, durable in redis from the moment the
 * collector enqueues it.
 *
 * `eventId` is minted at the edge and used as BOTH the BullMQ `jobId` AND the
 * `raw_events` primary key, so re-drains and duplicates are idempotent
 * (`INSERT ... ON CONFLICT (id) DO NOTHING`).
 *
 * `receivedAt` is the EDGE clock (ISO-8601). The writer MUST persist this value
 * as the row's `receivedAt` — never `now()` — or an outage backdates every
 * spooled event to drain time and smears time-bucketed aggregates.
 */
export interface RawEventEnvelope {
  readonly eventId: string;
  readonly kind: IngestKind;
  readonly receivedAt: string;
  readonly edge: EdgeContext;
  /** The normalized event the writer persists. */
  readonly payload: NormalizedEvent;
}

/** BullMQ job payload emitted by the ingest-writer for the aggregation stage. */
export interface ProcessEventJob {
  readonly eventId: string;
  readonly eventType: string;
  readonly sessionId: string;
}
