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

/** Post-attempt lifecycle states of a DestinationDeliveryLog (PENDING, SENT, FAILED). */
export const DESTINATION_DELIVERY_LOG_STATUSES = destinationDeliveryLogLifecycle.states;

const builtins = {
  eventType: db.string().description("Event type key resolved at plan time"),
  sourceType: db
    .string()
    .description("Polymorphic source-record discriminator of the originating event"),
  sourceId: db.string().description("Polymorphic source-record id of the originating event"),
  targetType: db
    .string()
    .description("Destination binding target type (from ChannelRoutingBinding)"),
  targetId: db.string().description("Destination binding target id (from ChannelRoutingBinding)"),
  channelId: db.string().description("DESTINATION channel key the post was routed to (e.g. SLACK)"),
  externalChannelRef: db
    .string()
    .description("Opaque destination reference posted to (e.g. a Slack channel id)"),
  idempotencyKey: db
    .string()
    .description(
      "Event logical-identity key (eventType:sourceType:sourceId:payloadHash); dedup is keyed on (idempotencyKey, channelId, externalChannelRef) so a re-planned event reuses this log instead of double-posting",
    ),
  subject: db
    .string()
    .description("Rendered subject captured at plan time, posted by the delivery worker"),
  body: db
    .string()
    .description("Rendered body captured at plan time, posted by the delivery worker"),
  status: db
    .enum(DESTINATION_DELIVERY_LOG_STATUSES)
    .description("Post attempt lifecycle status (PENDING, SENT, FAILED)"),
  providerMessageId: db
    .string({ optional: true })
    .description("Provider-side message id (e.g. the Slack ts)"),
  providerResponse: db
    .string({ optional: true })
    .description(
      "Redacted provider response { ok, ts, channel, error }; cleared by the retention sweep",
    ),
  failureReason: db
    .string({ optional: true })
    .description("Normalized failure reason; cleared by the retention sweep"),
  retryCount: db.int().description("Retry counter"),
  attemptedAt: db.datetime().description("Attempt timestamp"),
  completedAt: db.datetime({ optional: true }).description("Terminal completion timestamp"),
};

type ReservedFields = keyof typeof builtins | CommonReservedFields;

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

export function createDestinationDeliveryLogType<const F extends Record<string, TailorAnyDBField>>(
  params: CreateDestinationDeliveryLogTypeParams<F>,
) {
  return (
    db
      .type("DestinationDeliveryLog", {
        ...builtins,
        ...((params.fields ?? {}) as F),
        ...db.fields.timestamps(),
      })
      // Close the GraphQL gateway's auto-CRUD entirely. The dispatcher writes
      // these rows through the in-transaction db handle (not the gateway), so
      // leaving auto-CRUD open only risks cross-tenant read exposure; closing
      // it removes that exposure without losing any functionality.
      .features({
        gqlOperations: { create: false, read: false, update: false, delete: false },
      })
      .permission(defaultPermission)
      .gqlPermission(defaultGqlPermission)
  );
}

export const destinationDeliveryLog = createDestinationDeliveryLogType({});
