/**
 * EmailDispatch Model
 *
 * Durable send queue for automation emails. This collection IS the queue — one
 * document per email an automation workflow wants to send. A background worker
 * (services/email/emailDispatchWorker.ts) drains it, so sends survive restarts
 * and get retried with backoff instead of being fired synchronously inside node
 * execution.
 *
 * Modelled on SocialMediaPublication (the social publish queue): same
 * status / attemptCount / nextAttemptAt / workerLockedAt / lastError /
 * errorHistory lifecycle fields, and the same MongoDB-only (Redis-free) design.
 *
 * Idempotency: the unique (companyId, idempotencyKey) index — where
 * idempotencyKey = `${instanceId}:${nodeId}` — guarantees a workflow node that
 * re-runs (e.g. after a crash/replay) enqueues the email at most once.
 */

import mongoose, { Schema, Document } from 'mongoose';

export type EmailDispatchStatus =
  | 'queued'
  | 'sending'
  | 'sent'        // provider accepted the message (API returned success)
  | 'delivered'   // provider confirmed delivery to the recipient (webhook)
  | 'opened'      // recipient opened the message (webhook)
  | 'bounced'     // provider reported a hard/soft bounce or block (webhook)
  | 'failed'      // send never succeeded (provider rejected / not configured)
  | 'cancelled';

export interface IEmailDispatchError {
  code: string;
  message: string;
  at: Date;
}

/**
 * Snapshot of the resolved send parameters, captured at enqueue time so the
 * worker can send without re-resolving templates/config later.
 */
export interface IEmailDispatchPayload {
  provider?: string;
  subject?: string;
  htmlContent?: string;
  htmlUrl?: string;
  templateId?: number;
  senderId?: number;
  senderEmail?: string;
  senderName?: string;
  replyTo?: string;
  listIds?: number[];
}

export interface IEmailDispatch extends Document {
  companyId: string;

  // Provenance
  workflowId?: string;
  instanceId?: string;
  nodeId?: string;
  contactId?: string;

  // Idempotency (unique per company)
  idempotencyKey: string;

  // Recipient + payload snapshot
  recipientEmail: string;
  payload: IEmailDispatchPayload;

  // Queue lifecycle
  status: EmailDispatchStatus;
  attemptCount: number;
  nextAttemptAt: Date | null;
  workerLockedAt: Date | null;

  // Result
  providerMessageId?: string;
  sentAt?: Date;

  // Provider delivery lifecycle (populated from provider webhooks, correlated
  // by providerMessageId). These are additive — the send path never sets them.
  deliveredAt?: Date | null;
  openedAt?: Date | null;
  bouncedAt?: Date | null;
  bounceType?: 'hard' | 'soft' | 'blocked' | null;
  bounceReason?: string | null;

  // Diagnostics
  lastError: IEmailDispatchError | null;
  errorHistory: IEmailDispatchError[];

  createdAt: Date;
  updatedAt: Date;
}

const DispatchErrorSchema = new Schema<IEmailDispatchError>(
  {
    code: { type: String, required: true },
    message: { type: String, required: true },
    at: { type: Date, required: true },
  },
  { _id: false }
);

const DispatchPayloadSchema = new Schema<IEmailDispatchPayload>(
  {
    provider: String,
    subject: String,
    htmlContent: String,
    htmlUrl: String,
    templateId: Number,
    senderId: Number,
    senderEmail: String,
    senderName: String,
    replyTo: String,
    listIds: [Number],
  },
  { _id: false }
);

const EmailDispatchSchema = new Schema<IEmailDispatch>(
  {
    companyId: {
      type: String,
      required: [true, 'Company ID is required'],
      index: true,
    },

    workflowId: String,
    instanceId: String,
    nodeId: String,
    contactId: String,

    idempotencyKey: {
      type: String,
      required: true,
    },

    recipientEmail: {
      type: String,
      required: true,
    },
    payload: {
      type: DispatchPayloadSchema,
      default: {},
    },

    status: {
      type: String,
      enum: ['queued', 'sending', 'sent', 'delivered', 'opened', 'bounced', 'failed', 'cancelled'],
      default: 'queued',
      index: true,
    },
    attemptCount: { type: Number, default: 0 },
    nextAttemptAt: { type: Date, default: null },
    workerLockedAt: { type: Date, default: null },

    providerMessageId: { type: String, default: null },
    sentAt: { type: Date, default: null },

    deliveredAt: { type: Date, default: null },
    openedAt: { type: Date, default: null },
    bouncedAt: { type: Date, default: null },
    bounceType: { type: String, enum: ['hard', 'soft', 'blocked', null], default: null },
    bounceReason: { type: String, default: null },

    lastError: { type: DispatchErrorSchema, default: null },
    errorHistory: { type: [DispatchErrorSchema], default: [] },
  },
  { timestamps: true }
);

// One dispatch per workflow-node per instance — makes enqueue idempotent.
EmailDispatchSchema.index({ companyId: 1, idempotencyKey: 1 }, { unique: true });
// Worker scan: find due jobs by status + backoff gate.
EmailDispatchSchema.index({ status: 1, nextAttemptAt: 1 });
// Webhook correlation: map an inbound provider event's message id back to its
// dispatch so delivery/open/bounce events can advance the record's status.
EmailDispatchSchema.index({ companyId: 1, providerMessageId: 1 });

export const EmailDispatch = mongoose.model<IEmailDispatch>(
  'EmailDispatch',
  EmailDispatchSchema
);
