import { prisma } from '@documenso/prisma';
import { DocumentDistributionMethod, RecipientRole, SendStatus, SigningStatus } from '@prisma/client';

import { resolveNextReminderAt, ZEnvelopeReminderSettings } from '../../constants/envelope-reminder';

/**
 * Compute and store `nextReminderAt` for a single recipient.
 *
 * Pass `resetReminderCount: true` to restart the reminder cycle (e.g. on a
 * manual resend): the count is zeroed and the schedule recomputed as if the
 * request was freshly sent at `sentAt`.
 */
export const updateRecipientNextReminder = async (options: {
  recipientId: number;
  envelopeId: string;
  sentAt: Date;
  lastReminderSentAt: Date | null;
  reminderCount?: number;
  resetReminderCount?: boolean;
  reminderSettings?: ReturnType<typeof ZEnvelopeReminderSettings.parse> | null;
}) => {
  const { recipientId, envelopeId, sentAt, lastReminderSentAt, reminderCount = 0, resetReminderCount } = options;

  let settings = options.reminderSettings;

  if (settings === undefined) {
    const envelope = await prisma.envelope.findFirst({
      where: { id: envelopeId },
      select: { documentMeta: { select: { reminderSettings: true } } },
    });

    settings = envelope?.documentMeta?.reminderSettings
      ? ZEnvelopeReminderSettings.parse(envelope.documentMeta.reminderSettings)
      : null;
  }

  const nextReminderAt = resolveNextReminderAt({
    config: settings,
    sentAt,
    lastReminderSentAt,
    reminderCount: resetReminderCount ? 0 : reminderCount,
  });

  await prisma.recipient.update({
    where: { id: recipientId },
    data: {
      nextReminderAt,
      ...(resetReminderCount ? { reminderCount: 0 } : {}),
    },
  });
};

/**
 * Recompute `nextReminderAt` for all active (unsigned, sent) recipients
 * of a given envelope. Call when document-level reminder settings change.
 */
export const recomputeNextReminderForEnvelope = async (envelopeId: string) => {
  const envelope = await prisma.envelope.findFirst({
    where: { id: envelopeId },
    select: {
      documentMeta: {
        select: { reminderSettings: true, distributionMethod: true },
      },
    },
  });

  // No reminders for manually distributed documents.
  const isEmailDistribution = envelope?.documentMeta?.distributionMethod !== DocumentDistributionMethod.NONE;

  const settings =
    isEmailDistribution && envelope?.documentMeta?.reminderSettings
      ? ZEnvelopeReminderSettings.parse(envelope.documentMeta.reminderSettings)
      : null;

  const now = new Date();

  const recipients = await prisma.recipient.findMany({
    where: {
      envelopeId,
      signingStatus: SigningStatus.NOT_SIGNED,
      sendStatus: SendStatus.SENT,
      sentAt: { not: null },
      role: { not: RecipientRole.CC },
      // Don't reschedule reminders for recipients whose deadline has passed.
      OR: [{ expiresAt: null }, { expiresAt: { gt: now } }],
    },
    select: { id: true, sentAt: true, lastReminderSentAt: true, reminderCount: true },
  });

  await Promise.all(
    recipients.map(async (recipient) => {
      if (!recipient.sentAt) {
        return;
      }

      const nextReminderAt = resolveNextReminderAt({
        config: settings,
        sentAt: recipient.sentAt,
        lastReminderSentAt: recipient.lastReminderSentAt,
        reminderCount: recipient.reminderCount,
      });

      await prisma.recipient.update({
        where: { id: recipient.id },
        data: { nextReminderAt },
      });
    }),
  );
};
