import { revalidatePath } from "next/cache";
import { connectDB } from "@/lib/db";
import { Order } from "@/models/Order";
import { Invoice } from "@/models/Invoice";
import { Reminder } from "@/models/Reminder";
import { Client } from "@/models/Client";
import { getSettings } from "@/lib/settings";
import { logActivity } from "@/lib/orderActivity";
import { notifyClientReminderSent } from "@/lib/orderEmails";
import { notifyRecipient } from "@/lib/notifications";
import { formatEUR, formatDateDE } from "@/lib/orderConstants";

// Gemeinsamer Kern für manuelles Auslösen (sendReminderAction, ein Klick im
// Admin-Bereich) UND automatische Eskalation (runReminderEscalationSweep,
// Cron) — beide sollen exakt dieselbe Gebühren-/Betrags-/Benachrichtigungs-
// Logik durchlaufen, damit sie nicht auseinanderlaufen.
export async function createReminderForOrder(
  orderId: string,
  note: string,
  automatic: boolean
): Promise<string | null> {
  await connectDB();
  const order = await Order.findById(orderId);
  if (!order || order.paid) return null;

  const invoice = await Invoice.findOne({ orderId }).lean();
  if (!invoice) return null;

  const settings = await getSettings();
  const { levelFees, daysBetweenLevels } = settings.reminderEscalationSettings;

  const existingCount = await Reminder.countDocuments({ invoiceId: invoice._id });
  const level = existingCount + 1;
  const fee = levelFees.length > 0 ? levelFees[Math.min(level - 1, levelFees.length - 1)] || 0 : 0;
  const newFeeTotal = Math.round(((order.reminderFeeTotal || 0) + fee) * 100) / 100;
  const amountDue = Math.round((invoice.totalAmount + newFeeTotal) * 100) / 100;
  const dueDate = new Date(Date.now() + daysBetweenLevels * 24 * 60 * 60 * 1000);

  let reminder;
  try {
    reminder = await Reminder.create({
      invoiceId: invoice._id,
      orderId,
      clientId: order.clientId,
      level,
      invoiceNumber: invoice.number,
      fee,
      amountDue,
      dueDate,
      note: note.trim(),
      automatic,
    });
  } catch (err) {
    // Zwei nahezu gleichzeitige Aufrufe (Doppelklick, überlappender Cron-Lauf)
    // haben denselben existingCount gesehen und wollen dieselbe Stufe
    // anlegen — der unique Index (invoiceId, level) lässt nur den ersten zu.
    // Sauber abbrechen statt eine doppelte Mahnmail zu verschicken oder mit
    // einem Fehler abzustürzen.
    if (err && typeof err === "object" && "code" in err && err.code === 11000) return null;
    throw err;
  }

  order.reminderFeeTotal = newFeeTotal;
  await order.save();

  await logActivity(
    orderId,
    "reminder_sent",
    "admin",
    `Mahnstufe ${level} zu Rechnung ${invoice.number} erstellt${fee > 0 ? ` (+${formatEUR(fee)} Gebühr)` : ""}${automatic ? " (automatisch)" : ""}`
  );

  const client = await Client.findById(order.clientId).select("email name firstName nickname locale").lean();
  if (client) {
    const notifyClient = { email: client.email, name: client.nickname || client.firstName || client.name, locale: client.locale };
    const notifyOrder = { id: orderId, characterOrConcept: order.characterOrConcept };
    await notifyClientReminderSent(notifyClient, notifyOrder, level, amountDue, fee, dueDate);
    await notifyRecipient(String(order.clientId), {
      type: "payment_reminder",
      title: `Zahlungserinnerung — ${order.characterOrConcept}`,
      body: `Offener Betrag: ${formatEUR(amountDue)}, fällig bis ${formatDateDE(dueDate)}`,
      link: `/${client.locale}/account/orders/${orderId}`,
    });
  }

  revalidatePath("/de/admin/orders");
  revalidatePath("/[locale]/admin/orders/[orderId]", "page");
  revalidatePath(`/de/admin/orders/${orderId}`);
  revalidatePath("/[locale]/account/orders/[orderId]", "page");

  return String(reminder._id);
}

// Täglicher Cron-Lauf (siehe api/cron/reminder-escalation) — nur aktiv, wenn
// unter Einstellungen ausdrücklich eingeschaltet (Standard: aus). Erste Stufe
// (Zahlungserinnerung) greift ab Rechnungsdatum + daysUntilFirstReminder,
// jede weitere Stufe ab Fälligkeit der vorherigen + daysBetweenLevels.
export async function runReminderEscalationSweep() {
  await connectDB();
  const settings = await getSettings();
  const { autoEnabled, daysUntilFirstReminder } = settings.reminderEscalationSettings;
  if (!autoEnabled) return { created: 0, skipped: "disabled" as const };

  const now = new Date();
  const unpaidOrders = await Order.find({ compensationType: "paid", paid: false, rejected: false, archived: false })
    .select("_id")
    .lean();
  if (unpaidOrders.length === 0) return { created: 0 };

  const orderIds = unpaidOrders.map((o) => o._id);
  const invoices = await Invoice.find({ orderId: { $in: orderIds } }).select("orderId issueDate").lean();
  const invoiceByOrderId = new Map(invoices.map((i) => [String(i.orderId), i]));

  const lastReminders = await Reminder.aggregate<{ _id: string; dueDate: Date }>([
    { $match: { orderId: { $in: orderIds } } },
    { $sort: { createdAt: -1 } },
    { $group: { _id: "$orderId", dueDate: { $first: "$dueDate" } } },
  ]);
  const lastReminderByOrderId = new Map(lastReminders.map((r) => [String(r._id), r.dueDate]));

  let created = 0;
  for (const orderId of orderIds) {
    const invoice = invoiceByOrderId.get(String(orderId));
    if (!invoice) continue;

    const lastDueDate = lastReminderByOrderId.get(String(orderId));
    const nextTriggerAt = lastDueDate
      ? lastDueDate
      : new Date(new Date(invoice.issueDate).getTime() + daysUntilFirstReminder * 24 * 60 * 60 * 1000);

    if (now >= nextTriggerAt) {
      const result = await createReminderForOrder(String(orderId), "", true);
      if (result) created++;
    }
  }

  return { created };
}
