import { prisma } from "./db";
import { sendMessage, sendButtons, renderTemplate } from "./waha";
import crypto from "crypto";

// ─── Upgrade #4: Timezone-aware helpers ─────────────────────────────

/**
 * Get current date/time in a specific timezone
 */
function nowInTimezone(tz: string): Date {
  const str = new Date().toLocaleString("en-US", { timeZone: tz });
  return new Date(str);
}

/**
 * Get the hour (0-23) in a specific timezone
 */
function getHourInTimezone(date: Date, tz: string): number {
  return parseInt(
    new Intl.DateTimeFormat("en-US", { timeZone: tz, hour: "numeric", hour12: false }).format(date)
  );
}

/**
 * Format a date in the clinic's timezone
 */
function formatDateInTimezone(date: Date, tz: string): string {
  return new Intl.DateTimeFormat("en-US", {
    timeZone: tz,
    weekday: "long",
    month: "long",
    day: "numeric",
  }).format(date);
}

function formatTimeInTimezone(date: Date, tz: string): string {
  return new Intl.DateTimeFormat("en-US", {
    timeZone: tz,
    hour: "numeric",
    minute: "2-digit",
    hour12: true,
  }).format(date);
}

// ─── Upgrade #6: Secure confirm link ────────────────────────────────

function getConfirmLink(appointmentId: string, token: string): string {
  const baseUrl = process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000";
  return `${baseUrl}/api/confirm/${appointmentId}?token=${token}`;
}

/**
 * Generate a cryptographically secure confirm token
 */
export function generateConfirmToken(): string {
  return crypto.randomBytes(32).toString("hex");
}

// ─── Upgrade #1: Retry config ───────────────────────────────────────

const MAX_RETRIES = 3;
const RETRY_DELAYS = [2 * 60_000, 10 * 60_000, 60 * 60_000]; // 2min, 10min, 1hr

function getNextRetryAt(retryCount: number): Date | null {
  if (retryCount >= MAX_RETRIES) return null;
  return new Date(Date.now() + RETRY_DELAYS[retryCount]);
}

// ─── Upgrade #9: Template validation ────────────────────────────────

const VALID_VARS = [
  "name", "treatment", "clinic", "date", "time",
  "prepInstructions", "aftercareInstructions",
  "address", "confirmLink",
];

/**
 * Validate a template message — strip unresolved {{vars}}, warn about unknown ones
 */
export function validateTemplate(message: string): { clean: string; warnings: string[] } {
  const warnings: string[] = [];
  let clean = message;

  // Find all {{var}} placeholders
  const matches = message.match(/\{\{(\w+)\}\}/g) || [];
  for (const match of matches) {
    const varName = match.replace(/\{\{|\}\}/g, "");
    if (!VALID_VARS.includes(varName)) {
      warnings.push(`Unknown variable: ${match}`);
    }
  }

  // Strip any unresolved {{...}} after rendering (safety net)
  clean = clean.replace(/\{\{(\w+)\}\}/g, (match, varName) => {
    if (VALID_VARS.includes(varName)) return match; // Keep valid ones for rendering
    return ""; // Strip unknown ones
  });

  return { clean: clean.trim(), warnings };
}

/**
 * Clean rendered message — remove any leftover {{...}} that weren't replaced
 */
function cleanRenderedMessage(message: string): string {
  return message.replace(/\{\{\w+\}\}/g, "").replace(/\n{3,}/g, "\n\n").trim();
}

// ─── Default templates (Upgrade #8: includes reschedule_followup) ───

export const DEFAULT_TEMPLATES = [
  {
    trigger: "booking_confirmed",
    offsetMinutes: 0,
    sortOrder: 1,
    message: `Hi {{name}}! ✅

Your {{treatment}} appointment at {{clinic}} is confirmed:

📅 {{date}}
🕐 {{time}}

✅ *Tap here to confirm:*
{{confirmLink}}

Or simply reply *YES* to confirm.

If you need to reschedule, reply *RESCHEDULE* and we'll find a new time for you.

We're looking forward to seeing you! 😊`,
  },
  {
    trigger: "3_days_before",
    offsetMinutes: -4320,
    sortOrder: 2,
    message: `Hi {{name}}! 👋

Your {{treatment}} appointment is in 3 days:
📅 {{date}} at {{time}}

Here's how to prepare:
{{prepInstructions}}

Any questions? Just reply to this message and our team will help you right away.

See you soon! 😊`,
  },
  {
    trigger: "1_day_before",
    offsetMinutes: -1440,
    sortOrder: 3,
    message: `Hi {{name}}! Your appointment is *tomorrow*:

📅 {{date}} at {{time}}
📍 {{clinic}}

Please arrive 10 minutes early.

✅ *Tap here to confirm:*
{{confirmLink}}

Or reply *CONFIRM* if you're all set ✅
Reply *RESCHEDULE* if you need to change 📅

We're looking forward to seeing you!`,
  },
  {
    trigger: "2_hours_before",
    offsetMinutes: -120,
    sortOrder: 4,
    message: `See you in 2 hours, {{name}}! 🙌

Your {{treatment}} appointment:
🕐 {{time}} at {{clinic}}

Safe travels! 😊`,
  },
  {
    trigger: "30_min_before",
    offsetMinutes: -30,
    sortOrder: 5,
    message: `{{name}}, we're ready for you! 😊

Your appointment starts in 30 minutes at {{time}}.

See you very soon! ✨`,
  },
  {
    trigger: "1_hour_after_noshow",
    offsetMinutes: 60,
    sortOrder: 6,
    message: `Hi {{name}}, we missed you at your appointment today. Everything okay? 💙

If something came up, no worries at all. We'd love to reschedule for you — just reply with a day and time that works better.

No extra charges. Same great care. We're here whenever you're ready! 😊`,
  },
  {
    trigger: "1_day_after_noshow",
    offsetMinutes: 1440,
    sortOrder: 7,
    message: `Hi {{name}}! Just checking in 😊

We had you down for {{treatment}} yesterday and missed you. We totally understand that life gets busy!

We have some openings this week — would you like to rebook?

Just reply with a day that works and we'll set it up for you. 💫`,
  },
  {
    trigger: "post_treatment_same_day",
    offsetMinutes: 180,
    sortOrder: 8,
    message: `Hi {{name}}! Thank you for visiting {{clinic}} today! 💫

Here are your aftercare instructions:
{{aftercareInstructions}}

If you have any questions or concerns at all, just reply to this message. We're here for you! 😊`,
  },
  {
    trigger: "post_treatment_3_days",
    offsetMinutes: 4320,
    sortOrder: 9,
    message: `Hi {{name}}! It's been a few days since your {{treatment}} — how are you feeling? 😊

Everything healing well?

If you have any questions about your results or aftercare, we're just a message away.

Also — if you're happy with your experience, we'd really appreciate a quick Google review ⭐. It helps other patients find quality care!

Thank you for choosing {{clinic}}! 💙`,
  },
  // Upgrade #8: Reschedule follow-up (48h after rescheduling if no new booking)
  {
    trigger: "reschedule_followup",
    offsetMinutes: 2880, // 48 hours
    sortOrder: 10,
    message: `Hi {{name}}! 😊

You mentioned wanting to reschedule your {{treatment}} appointment, but we haven't heard back yet.

We'd love to find a new time that works for you! Just reply with your preferred day and time.

Our available slots fill up quickly — book now to get your preferred time! 💫`,
  },
];

/**
 * Seed default reminder templates for a new clinic
 */
export async function seedDefaultTemplates(clinicId: string) {
  const existing = await prisma.reminderTemplate.count({ where: { clinicId } });
  if (existing > 0) {
    console.log(`⚠️ Templates already exist for clinic ${clinicId}, skipping seed`);
    return;
  }

  for (const t of DEFAULT_TEMPLATES) {
    await prisma.reminderTemplate.create({
      data: { ...t, clinicId },
    });
  }
  console.log(`✅ Seeded ${DEFAULT_TEMPLATES.length} reminder templates for clinic ${clinicId}`);
}

/**
 * Schedule all reminders for a new appointment.
 * Upgrade #4: Timezone-aware scheduling
 * Upgrade #6: Uses confirmToken
 */
export async function scheduleReminders(appointmentId: string) {
  const appointment = await prisma.appointment.findUnique({
    where: { id: appointmentId },
    include: { clinic: true, lead: true, treatment: true },
  });

  if (!appointment) {
    console.error(`❌ scheduleReminders: Appointment ${appointmentId} not found`);
    return;
  }

  // Ensure appointment has a confirm token (Upgrade #6)
  if (!appointment.confirmToken) {
    const token = generateConfirmToken();
    await prisma.appointment.update({ where: { id: appointmentId }, data: { confirmToken: token } });
  }

  const templates = await prisma.reminderTemplate.findMany({
    where: {
      clinicId: appointment.clinicId,
      isActive: true,
      trigger: {
        notIn: [
          "1_hour_after_noshow", "1_day_after_noshow",
          "post_treatment_same_day", "post_treatment_3_days",
          "reschedule_followup",
        ],
      },
    },
    orderBy: { sortOrder: "asc" },
  });

  if (templates.length === 0) {
    console.warn(`⚠️ No active pre-appointment templates found for clinic ${appointment.clinicId}`);
    return;
  }

  const now = new Date();
  const apptTime = new Date(appointment.dateTime);
  const tz = appointment.clinic.timezone || "Asia/Riyadh";
  let scheduled = 0;
  let skipped = 0;

  // Smart timing: high-value treatments get earlier reminders
  const price = appointment.treatment.price || 0;
  const timingMultiplier = price >= 500 ? 1.5 : price >= 200 ? 1.2 : 1.0;

  for (const template of templates) {
    let scheduledFor: Date;

    if (template.trigger === "booking_confirmed") {
      scheduledFor = new Date(now.getTime() + 5000);
    } else {
      // For pre-appointment reminders, scale how early they fire based on treatment value
      const scaledOffset = template.offsetMinutes < 0
        ? Math.round(template.offsetMinutes * timingMultiplier)
        : template.offsetMinutes;
      scheduledFor = new Date(apptTime.getTime() + scaledOffset * 60000);
    }

    // Don't schedule reminders in the past
    if (scheduledFor < now && template.trigger !== "booking_confirmed") {
      skipped++;
      continue;
    }

    // Upgrade #4: Timezone-aware quiet hours
    scheduledFor = respectQuietHours(scheduledFor, appointment.clinic.quietHoursStart, appointment.clinic.quietHoursEnd, tz);

    // Prevent duplicate reminders
    const existing = await prisma.scheduledReminder.findFirst({
      where: { appointmentId, templateId: template.id },
    });
    if (existing) {
      skipped++;
      continue;
    }

    await prisma.scheduledReminder.create({
      data: { appointmentId, templateId: template.id, scheduledFor },
    });
    scheduled++;
  }

  console.log(`📅 Scheduled ${scheduled} reminders for appointment ${appointmentId} (${skipped} skipped)`);
}

/**
 * Schedule no-show reminders
 */
export async function scheduleNoShowReminders(appointmentId: string) {
  const appointment = await prisma.appointment.findUnique({
    where: { id: appointmentId },
    include: { clinic: true },
  });

  if (!appointment) {
    console.error(`❌ scheduleNoShowReminders: Appointment ${appointmentId} not found`);
    return;
  }

  const templates = await prisma.reminderTemplate.findMany({
    where: {
      clinicId: appointment.clinicId,
      isActive: true,
      trigger: { in: ["1_hour_after_noshow", "1_day_after_noshow"] },
    },
  });

  if (templates.length === 0) return;

  const now = new Date();
  const tz = appointment.clinic.timezone || "Asia/Riyadh";
  let scheduled = 0;

  for (const template of templates) {
    const existing = await prisma.scheduledReminder.findFirst({
      where: { appointmentId, templateId: template.id, status: "pending" },
    });
    if (existing) continue;

    const scheduledFor = respectQuietHours(
      new Date(now.getTime() + template.offsetMinutes * 60000),
      appointment.clinic.quietHoursStart, appointment.clinic.quietHoursEnd, tz
    );

    await prisma.scheduledReminder.create({
      data: { appointmentId, templateId: template.id, scheduledFor },
    });
    scheduled++;
  }

  console.log(`😟 Scheduled ${scheduled} no-show reminders for appointment ${appointmentId}`);
}

/**
 * Schedule post-treatment reminders
 */
export async function schedulePostTreatmentReminders(appointmentId: string) {
  const appointment = await prisma.appointment.findUnique({
    where: { id: appointmentId },
    include: { clinic: true },
  });

  if (!appointment) {
    console.error(`❌ schedulePostTreatmentReminders: Appointment ${appointmentId} not found`);
    return;
  }

  const templates = await prisma.reminderTemplate.findMany({
    where: {
      clinicId: appointment.clinicId,
      isActive: true,
      trigger: { in: ["post_treatment_same_day", "post_treatment_3_days"] },
    },
  });

  if (templates.length === 0) return;

  const now = new Date();
  const tz = appointment.clinic.timezone || "Asia/Riyadh";
  let scheduled = 0;

  for (const template of templates) {
    const existing = await prisma.scheduledReminder.findFirst({
      where: { appointmentId, templateId: template.id, status: "pending" },
    });
    if (existing) continue;

    const scheduledFor = respectQuietHours(
      new Date(now.getTime() + template.offsetMinutes * 60000),
      appointment.clinic.quietHoursStart, appointment.clinic.quietHoursEnd, tz
    );

    await prisma.scheduledReminder.create({
      data: { appointmentId, templateId: template.id, scheduledFor },
    });
    scheduled++;
  }

  console.log(`💊 Scheduled ${scheduled} post-treatment reminders for appointment ${appointmentId}`);
}

/**
 * Upgrade #8: Schedule reschedule follow-up when appointment is rescheduled
 */
export async function scheduleRescheduleFollowup(appointmentId: string) {
  const appointment = await prisma.appointment.findUnique({
    where: { id: appointmentId },
    include: { clinic: true },
  });

  if (!appointment) return;

  const template = await prisma.reminderTemplate.findFirst({
    where: {
      clinicId: appointment.clinicId,
      isActive: true,
      trigger: "reschedule_followup",
    },
  });

  if (!template) return;

  const existing = await prisma.scheduledReminder.findFirst({
    where: { appointmentId, templateId: template.id, status: "pending" },
  });
  if (existing) return;

  const tz = appointment.clinic.timezone || "Asia/Riyadh";
  const scheduledFor = respectQuietHours(
    new Date(Date.now() + template.offsetMinutes * 60000),
    appointment.clinic.quietHoursStart, appointment.clinic.quietHoursEnd, tz
  );

  await prisma.scheduledReminder.create({
    data: { appointmentId, templateId: template.id, scheduledFor },
  });

  console.log(`🔄 Scheduled reschedule follow-up for appointment ${appointmentId}`);
}

/**
 * Upgrade #4: Timezone-aware quiet hours
 */
function respectQuietHours(date: Date, quietStart: number, quietEnd: number, tz: string): Date {
  const result = new Date(date);
  const hour = getHourInTimezone(result, tz);

  // Quiet hours span midnight: e.g. 22:00 - 08:00
  if (quietStart > quietEnd) {
    if (hour >= quietStart || hour < quietEnd) {
      if (hour >= quietStart) {
        result.setDate(result.getDate() + 1);
      }
      // Set to quiet end hour in the target timezone
      const targetHour = quietEnd;
      const currentHour = getHourInTimezone(result, tz);
      const diff = targetHour - currentHour;
      result.setHours(result.getHours() + diff, 0, 0, 0);
    }
  } else {
    if (hour >= quietStart && hour < quietEnd) {
      const diff = quietEnd - hour;
      result.setHours(result.getHours() + diff, 0, 0, 0);
    }
  }

  return result;
}

/**
 * Build template variables — Upgrade #4: timezone-aware formatting, #6: secure token
 */
function buildTemplateVars(appointment: {
  id: string;
  confirmToken: string | null;
  dateTime: Date;
  lead: { name: string };
  treatment: { name: string; prepInstructions: string | null; aftercareInstructions: string | null };
  clinic: { name: string; timezone: string };
}): Record<string, string> {
  const tz = appointment.clinic.timezone || "Asia/Riyadh";
  const apptDate = new Date(appointment.dateTime);
  const token = appointment.confirmToken || "";

  return {
    name: appointment.lead.name.split(" ")[0],
    treatment: appointment.treatment.name,
    clinic: appointment.clinic.name,
    date: formatDateInTimezone(apptDate, tz),
    time: formatTimeInTimezone(apptDate, tz),
    prepInstructions: appointment.treatment.prepInstructions || "No special preparation needed. Just come as you are!",
    aftercareInstructions: appointment.treatment.aftercareInstructions || "Follow your doctor's instructions. If you have any concerns, contact us immediately.",
    address: "Our clinic",
    confirmLink: getConfirmLink(appointment.id, token),
  };
}

/**
 * Send booking confirmation immediately
 */
export async function sendBookingConfirmation(appointmentId: string) {
  const appointment = await prisma.appointment.findUnique({
    where: { id: appointmentId },
    include: { clinic: true, lead: true, treatment: true },
  });

  if (!appointment) {
    console.error(`❌ sendBookingConfirmation: Appointment ${appointmentId} not found`);
    return;
  }

  // Ensure confirm token exists (Upgrade #6)
  let apptWithToken = appointment;
  if (!appointment.confirmToken) {
    const token = generateConfirmToken();
    apptWithToken = await prisma.appointment.update({
      where: { id: appointmentId },
      data: { confirmToken: token },
      include: { clinic: true, lead: true, treatment: true },
    });
  }

  const template = await prisma.reminderTemplate.findFirst({
    where: { clinicId: appointment.clinicId, trigger: "booking_confirmed", isActive: true },
  });

  if (!template) {
    console.warn("⚠️ No active booking_confirmed template found — skipping confirmation");
    return;
  }

  if (appointment.clinic.wahaStatus !== "connected") {
    console.warn(`⚠️ WhatsApp not connected for clinic ${appointment.clinic.name} — skipping booking confirmation`);
    return;
  }

  const vars = buildTemplateVars(apptWithToken);
  const rawMessage = renderTemplate(template.message, vars);
  const message = cleanRenderedMessage(rawMessage); // Upgrade #9

  try {
    const wahaResponse = await sendButtons(
      appointment.lead.phone,
      message,
      [
        { id: "confirm", text: "✅ Confirm" },
        { id: "reschedule", text: "📅 Reschedule" },
        { id: "cancel", text: "❌ Cancel" },
      ],
      appointment.clinic.name
    );

    await prisma.scheduledReminder.updateMany({
      where: { appointmentId, template: { trigger: "booking_confirmed" }, status: "pending" },
      data: { status: "sent", sentAt: new Date() },
    });

    await prisma.messageLog.create({
      data: {
        clinicId: appointment.clinicId,
        appointmentId: appointment.id,
        phone: appointment.lead.phone,
        direction: "outgoing",
        message,
        status: "sent",
        wahaMessageId: (wahaResponse as Record<string, Record<string, string>>)?.key?.id || null,
        sentAt: new Date(),
      },
    });

    console.log(`✅ Booking confirmation sent to ${appointment.lead.name} (${appointment.lead.phone})`);
  } catch (error) {
    console.error(`❌ Failed to send booking confirmation to ${appointment.lead.phone}:`, error);

    await prisma.scheduledReminder.updateMany({
      where: { appointmentId, template: { trigger: "booking_confirmed" }, status: "pending" },
      data: {
        status: "failed",
        failureReason: error instanceof Error ? error.message : "Unknown error",
        retryCount: 1,
        nextRetryAt: getNextRetryAt(0),
      },
    });
  }
}

/**
 * MAIN CRON: Process due reminders
 *
 * Upgrade #1: Retry failed reminders with exponential backoff
 * Upgrade #4: Timezone-aware formatting
 * Upgrade #6: Secure confirm links
 * Upgrade #9: Clean unresolved template vars
 */
export async function processDueReminders() {
  const now = new Date();

  // Find due pending reminders + retryable failed reminders (Upgrade #1)
  const dueReminders = await prisma.scheduledReminder.findMany({
    where: {
      OR: [
        // Normal pending reminders
        { status: "pending", scheduledFor: { lte: now } },
        // Failed reminders ready for retry
        { status: "failed", retryCount: { lt: MAX_RETRIES }, nextRetryAt: { lte: now } },
      ],
    },
    include: {
      template: true,
      appointment: { include: { clinic: true, lead: true, treatment: true } },
    },
    take: 50,
    orderBy: { scheduledFor: "asc" },
  });

  if (dueReminders.length === 0) return { sent: 0, skipped: 0, failed: 0, retried: 0 };

  const results = { sent: 0, skipped: 0, failed: 0, retried: 0 };

  for (const reminder of dueReminders) {
    const { appointment, template } = reminder;
    const isRetry = reminder.status === "failed";

    if (isRetry) results.retried++;

    try {
      // ── Guard 1: Terminal state ──
      if (["cancelled", "rescheduled"].includes(appointment.status)) {
        // Exception: allow reschedule_followup even if rescheduled
        if (template.trigger !== "reschedule_followup") {
          await markSkipped(reminder.id, `Appointment is ${appointment.status}`);
          results.skipped++;
          continue;
        }
        // For reschedule_followup, check if lead has a NEW booking → skip
        if (template.trigger === "reschedule_followup") {
          const newBooking = await prisma.appointment.findFirst({
            where: {
              leadId: appointment.leadId,
              status: { in: ["scheduled", "confirmed", "booked"] },
              dateTime: { gte: now },
              id: { not: appointment.id },
            },
          });
          if (newBooking) {
            await markSkipped(reminder.id, "Patient already rebooked");
            results.skipped++;
            continue;
          }
        }
      }

      // ── Guard 2: Completed for pre-appointment ──
      if (appointment.status === "completed" && template.offsetMinutes <= 0 && template.trigger !== "booking_confirmed") {
        await markSkipped(reminder.id, "Appointment already completed");
        results.skipped++;
        continue;
      }

      // ── Guard 3: Already arrived ──
      if (["arrived", "in_progress"].includes(appointment.status) && template.offsetMinutes < 0) {
        await markSkipped(reminder.id, `Patient already ${appointment.status}`);
        results.skipped++;
        continue;
      }

      // ── Guard 4: 30-min if confirmed ──
      if (template.trigger === "30_min_before" && appointment.status === "confirmed") {
        await markSkipped(reminder.id, "Patient already confirmed");
        results.skipped++;
        continue;
      }

      // ── Guard 5: WhatsApp connected ──
      if (appointment.clinic.wahaStatus !== "connected") {
        // Upgrade #1: retry later instead of permanent fail
        const nextRetry = getNextRetryAt(reminder.retryCount);
        if (nextRetry) {
          await prisma.scheduledReminder.update({
            where: { id: reminder.id },
            data: {
              status: "failed",
              failureReason: "WhatsApp not connected",
              retryCount: reminder.retryCount + 1,
              nextRetryAt: nextRetry,
            },
          });
        } else {
          await markFailed(reminder.id, "WhatsApp not connected after all retries");
        }
        results.failed++;
        continue;
      }

      // ── Guard 6: Valid phone ──
      if (!appointment.lead.phone || appointment.lead.phone.trim().length < 5) {
        await markFailed(reminder.id, "Invalid phone number");
        results.failed++;
        continue;
      }

      // ── Ensure confirm token (Upgrade #6) ──
      let appt = appointment;
      if (!appointment.confirmToken) {
        const token = generateConfirmToken();
        appt = await prisma.appointment.update({
          where: { id: appointment.id },
          data: { confirmToken: token },
          include: { clinic: true, lead: true, treatment: true },
        });
      }

      // ── Render message (Upgrade #9: clean unresolved) ──
      const vars = buildTemplateVars(appt);
      const rawMessage = renderTemplate(template.message, vars);
      const message = cleanRenderedMessage(rawMessage);

      if (!message || message.trim().length === 0) {
        await markFailed(reminder.id, "Rendered message is empty");
        results.failed++;
        continue;
      }

      // ── Build buttons ──
      const buttons = getButtonsForTrigger(template.trigger);

      // ── Send via WhatsApp ──
      const wahaResponse = await sendButtons(
        appointment.lead.phone,
        message,
        buttons,
        appointment.clinic.name
      );

      // ── Mark as sent ──
      await prisma.scheduledReminder.update({
        where: { id: reminder.id },
        data: { status: "sent", sentAt: new Date(), failureReason: null },
      });

      // ── Log the message ──
      await prisma.messageLog.create({
        data: {
          clinicId: appointment.clinicId,
          appointmentId: appointment.id,
          phone: appointment.lead.phone,
          direction: "outgoing",
          message,
          status: "sent",
          wahaMessageId: (wahaResponse as Record<string, Record<string, string>>)?.key?.id || null,
          sentAt: new Date(),
        },
      });

      results.sent++;
      console.log(`📤 Reminder sent: ${template.trigger} → ${appointment.lead.name} (${appointment.lead.phone})${isRetry ? " [RETRY]" : ""}`);

    } catch (error) {
      const errMsg = error instanceof Error ? error.message : "Unknown error";
      console.error(`❌ Reminder ${reminder.id} failed:`, errMsg);

      // Upgrade #1: Schedule retry instead of permanent failure
      const nextRetry = getNextRetryAt(reminder.retryCount);
      if (nextRetry) {
        await prisma.scheduledReminder.update({
          where: { id: reminder.id },
          data: {
            status: "failed",
            failureReason: errMsg,
            retryCount: reminder.retryCount + 1,
            nextRetryAt: nextRetry,
          },
        });
      } else {
        await markFailed(reminder.id, `${errMsg} (after ${MAX_RETRIES} retries)`);
      }
      results.failed++;
    }
  }

  if (results.sent > 0 || results.failed > 0) {
    console.log(`📊 Cron results: ${results.sent} sent, ${results.skipped} skipped, ${results.failed} failed, ${results.retried} retried`);
  }

  return results;
}

/* ── Helpers ── */

async function markSkipped(id: string, reason?: string) {
  await prisma.scheduledReminder.update({
    where: { id },
    data: { status: "skipped", failureReason: reason || null },
  });
}

async function markFailed(id: string, reason: string) {
  await prisma.scheduledReminder.update({
    where: { id },
    data: { status: "failed", failureReason: reason, retryCount: MAX_RETRIES },
  });
}

function getButtonsForTrigger(trigger: string): { id: string; text: string }[] {
  const map: Record<string, { id: string; text: string }[]> = {
    booking_confirmed: [
      { id: "confirm", text: "✅ Confirm" },
      { id: "reschedule", text: "📅 Reschedule" },
      { id: "cancel", text: "❌ Cancel" },
    ],
    "3_days_before": [
      { id: "confirm", text: "✅ Confirm" },
      { id: "reschedule", text: "📅 Reschedule" },
      { id: "help", text: "📋 Menu" },
    ],
    "1_day_before": [
      { id: "confirm", text: "✅ Confirm" },
      { id: "reschedule", text: "📅 Reschedule" },
      { id: "cancel", text: "❌ Cancel" },
    ],
    "2_hours_before": [
      { id: "arrived", text: "🏥 I'm here" },
      { id: "confirm", text: "✅ On my way" },
      { id: "reschedule", text: "📅 Reschedule" },
    ],
    "30_min_before": [
      { id: "arrived", text: "🏥 I'm here" },
      { id: "confirm", text: "✅ Almost there" },
    ],
    "1_hour_after_noshow": [
      { id: "reschedule", text: "📅 Reschedule" },
      { id: "help", text: "📋 Menu" },
    ],
    "1_day_after_noshow": [
      { id: "book", text: "📅 Rebook" },
      { id: "help", text: "📋 Menu" },
    ],
    post_treatment_same_day: [
      { id: "help", text: "📋 Menu" },
    ],
    post_treatment_3_days: [
      { id: "book", text: "📅 Book Again" },
      { id: "help", text: "📋 Menu" },
    ],
    reschedule_followup: [
      { id: "book", text: "📅 Book Now" },
      { id: "help", text: "📋 Menu" },
    ],
  };

  return map[trigger] || [{ id: "help", text: "📋 Menu" }];
}
