import {
  type CommonReservedFields,
  type NoReservedFields,
  defaultGqlPermission,
  defaultPermission,
} from "@tailor-platform/erp-kit/core";
import { db, type TailorAnyDBField } from "@tailor-platform/sdk";
import { notificationEventLifecycle } from "./notificationEvent.lifecycle.generated";

/** Processing states of a NotificationEvent ingress row (PENDING, DISPATCHED, NO_DELIVERY). */
export const NOTIFICATION_EVENT_STATUSES = notificationEventLifecycle.states;

const builtins = {
  eventType: db
    .string()
    .description("Event discriminator resolved to a NotificationCategory at dispatch time"),
  sourceType: db
    .string()
    .description("Polymorphic discriminator of the source aggregate (e.g. PURCHASE_ORDER)"),
  sourceId: db.string().description("Polymorphic id of the source aggregate (no FK constraint)"),
  actorUserId: db
    .uuid({ optional: true })
    .description("User who caused the event (used for actor self-suppression)"),
  payload: db.string().description("Stringified JSON snapshot: recipient hints + template vars"),
  payloadHash: db.string().description("Stable hash of the normalized payload (idempotency)"),
  status: db
    .enum(NOTIFICATION_EVENT_STATUSES)
    .description("Dispatch lifecycle: PENDING -> DISPATCHED | NO_DELIVERY"),
  dispatchedAt: db
    .datetime({ optional: true })
    .description("When dispatch completed (null while PENDING)"),
};

type ReservedFields = keyof typeof builtins | CommonReservedFields;

export interface CreateNotificationEventTypeParams<F extends Record<string, TailorAnyDBField>> {
  fields?: NoReservedFields<F, ReservedFields>;
}

/**
 * NotificationEvent — append-only ingress record for the notification pipeline.
 *
 * Source modules append one PENDING row per domain change (in the same
 * transaction as the change). The insert is observed by the
 * `dispatch-notification-events` executor (CDC `recordCreatedTrigger`) which
 * drives `dispatchNotification`. The event is the trigger surface, not the
 * delivery engine — see the notification-delivery feature.
 *
 * Idempotency boundary: `(eventType, sourceType, sourceId, payloadHash)`.
 * `eventType` / `sourceType` are stored as opaque strings (no module-owned
 * vocabulary); the dispatcher resolves `eventType` against the Event Catalog.
 */
export function createNotificationEventType<const F extends Record<string, TailorAnyDBField>>(
  params: CreateNotificationEventTypeParams<F>,
) {
  return db
    .type("NotificationEvent", {
      ...builtins,
      ...((params.fields ?? {}) as F),
      ...db.fields.timestamps(),
    })
    .indexes({
      fields: ["eventType", "sourceType", "sourceId", "payloadHash"],
      unique: true,
      name: "notification_event_idempotency_idx",
    })
    .permission(defaultPermission)
    .gqlPermission(defaultGqlPermission);
}

export const notificationEvent = createNotificationEventType({});
