/**
 * Quotation Reminders Service
 * Process due reminders for quotations based on user settings
 */

import { prisma } from '@/lib/db';
import { logger } from '@/lib/logger';
import { createId } from '@paralleldrive/cuid2';
import { enqueueJob } from '@/lib/queue/job-queue';
import { notifyQuotationCustomer } from '@/lib/services/quotation-notifier.service';
import { quotationRemindersAdapter } from '@/lib/adapters/quotations';

const log = logger.scope('QuotationRemindersService');

// ═══════════════════════════════════════════════════════════════════════
// Types
// ═══════════════════════════════════════════════════════════════════════

interface ReminderScheduleEntry {
  trigger: 'before_due' | 'on_due' | 'after_due';
  dayOffset: number;
}

// Hard-coded 3-step reminder sequence — fires for ALL unpaid quotations with a due date.
// Step 1: on the due date itself
// Step 2: 2 days after due
// Step 3: 5 days after due (escalated to overdue notice)
const REMINDER_SEQUENCE: ReminderScheduleEntry[] = [
  { trigger: 'on_due',    dayOffset: 0 },
  { trigger: 'after_due', dayOffset: 2 },
  { trigger: 'after_due', dayOffset: 5 },
];

// ═══════════════════════════════════════════════════════════════════════
// Send Reminder
// ═══════════════════════════════════════════════════════════════════════

export async function sendQuotationReminder(
  quotationId: string,
  trigger: string,
  dayOffset: number,
  channel: string,
): Promise<void> {
  // Derive overdue threshold from the last step in the sequence
  const notificationType = dayOffset >= REMINDER_SEQUENCE[REMINDER_SEQUENCE.length - 1].dayOffset ? 'overdue' : 'reminder';

  const quotation = await prisma.quotations.findUnique({
    where: { id: quotationId },
    include: {
      customer: true,
      items: { orderBy: { sortOrder: 'asc' } },
      users: { select: { email: true, name: true } },
    },
  });

  let emailSent = false;
  let whatsappSent = false;

  if (quotation) {
    try {
      const notify = await notifyQuotationCustomer(quotation, notificationType);
      emailSent = notify.emailQueued;
      whatsappSent = notify.whatsappSent;
    } catch (err) {
      log.error('Failed to notify customer for reminder', { quotationId, err });
      // Do NOT write dedup record — allow retry on next cron run
      return;
    }
  }

  // Write dedup record AFTER notification attempt (success or partial)
  await quotationRemindersAdapter.create({
    data: {
      id: createId(),
      quotationId,
      channel,
      trigger,
      dayOffset,
      sentAt: new Date(),
      emailSent,
      whatsappSent,
    },
  });

  log.info('Quotation reminder sent', {
    quotationId,
    trigger,
    dayOffset,
    channel,
    emailSent,
    whatsappSent,
  });
}

// ═══════════════════════════════════════════════════════════════════════
// Process Due Reminders
// ═══════════════════════════════════════════════════════════════════════

export async function processDueReminders(): Promise<{ processed: number; sent: number; skipped: number }> {
  const now = new Date();
  const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());

  // Find quotations with unpaid/partial/overdue status that have a due date set
  const quotations = await prisma.quotations.findMany({
    where: {
      status: { in: ['SENT', 'VIEWED', 'ACCEPTED'] },
      paymentStatus: { in: ['UNPAID', 'PARTIAL', 'OVERDUE'] },
      dueDate: { not: null },
    },
    select: {
      id: true,
      userId: true,
      dueDate: true,
    },
  });

  let sent = 0;
  let skipped = 0;

  for (const quotation of quotations) {
    if (!quotation.dueDate) continue;

    for (const entry of REMINDER_SEQUENCE) {
      // Calculate when this reminder should fire
      let reminderDate: Date;
      const dueDate = new Date(quotation.dueDate);

      switch (entry.trigger) {
        case 'on_due':
          reminderDate = dueDate;
          break;
        case 'after_due':
          reminderDate = new Date(dueDate);
          reminderDate.setDate(reminderDate.getDate() + entry.dayOffset);
          break;
        default:
          continue;
      }

      // Normalize to date comparison (ignore time)
      const reminderDay = new Date(reminderDate.getFullYear(), reminderDate.getMonth(), reminderDate.getDate());

      if (reminderDay.getTime() !== today.getTime()) continue;

      // Check if already sent (dedup via unique constraint)
      const existingReminder = await quotationRemindersAdapter.findUnique({
        where: {
          quotationId_trigger_dayOffset: {
            quotationId: quotation.id,
            trigger: entry.trigger,
            dayOffset: entry.dayOffset,
          },
        },
      });

      if (existingReminder) {
        skipped++;
        continue;
      }

      // Enqueue the reminder job
      try {
        await enqueueJob('quotation_reminder', {
          quotationId: quotation.id,
          trigger: entry.trigger,
          dayOffset: entry.dayOffset,
          channel: 'both',
        }, {});
        sent++;
      } catch (error) {
        log.error('Failed to enqueue quotation reminder', {
          quotationId: quotation.id,
          trigger: entry.trigger,
          error,
        });
      }
    }
  }

  log.info('Processed due quotation reminders', { processed: quotations.length, sent, skipped });
  return { processed: quotations.length, sent, skipped };
}
