import { connectDB } from "@/lib/db";
import { Convention } from "@/models/Convention";
import { WaitlistEntry } from "@/models/WaitlistEntry";
import { Client } from "@/models/Client";
import { Order } from "@/models/Order";

// Extrahiert aus admin/conventions/page.tsx (Tab "Conventions") — Team vor
// Ort + offene Warteliste-Anzahl je Convention, plus Foto/Logo. Die
// Warteliste selbst (mit Genehmigen-Formular + Kollisions-Check) ist ein
// eigener Helfer, siehe getConventionWaitlistList() unten — anderer
// Zuschnitt (über alle Conventions hinweg, eigener Tab).
export async function getConventionList() {
  await connectDB();
  const items = await Convention.find({}).sort({ startDate: 1 }).lean();

  const allEntries = await WaitlistEntry.find({ conventionId: { $in: items.map((i) => i._id) } }).lean();
  // "auf Warteliste" = noch nicht genehmigt (kein verknüpfter Auftrag).
  const countByConvention = new Map<string, number>();
  for (const e of allEntries) {
    if (e.orderId) continue;
    const key = String(e.conventionId);
    countByConvention.set(key, (countByConvention.get(key) ?? 0) + 1);
  }

  const staff = await Client.find({ role: "staff" }).select("name").sort({ name: 1 }).lean();
  const staffOptions = staff.map((s) => ({ id: String(s._id), name: s.name }));
  const staffById = new Map(staffOptions.map((s) => [s.id, s]));

  const items_ = items.map((item) => ({
    _id: String(item._id),
    name: item.name,
    location: item.location,
    startDate: new Date(item.startDate).toISOString(),
    endDate: new Date(item.endDate).toISOString(),
    infoUrl: item.infoUrl,
    slotsAvailable: item.slotsAvailable,
    notes: item.notes,
    notesEn: item.notesEn,
    image: item.image || "",
    logo: item.logo || "",
    visible: item.visible,
    waitlistCount: countByConvention.get(String(item._id)) || 0,
    assignedStaff: (item.assignedStaffIds || [])
      .map((id) => staffById.get(String(id)))
      .filter((s): s is { id: string; name: string } => Boolean(s)),
  }));

  return { items: items_, staffOptions };
}

const fmtDay = (d: Date) => new Date(d).toLocaleDateString("de-DE", { day: "numeric", month: "long", year: "numeric" });

function dateRangeLabel(start: Date, end: Date) {
  const s = new Date(start);
  const e = new Date(end);
  if (s.toDateString() === e.toDateString()) return fmtDay(s);
  if (s.getMonth() === e.getMonth() && s.getFullYear() === e.getFullYear()) {
    return `${s.getDate()}.–${e.getDate()}. ${s.toLocaleDateString("de-DE", { month: "long", year: "numeric" })}`;
  }
  return `${fmtDay(s)} – ${fmtDay(e)}`;
}

// Feste Convention-Tage als Dropdown-Optionen (value = YYYY-MM-DD, wie es
// grantWaitlistEntryAction erwartet). UTC-Rechnung, da die Daten als
// Mitternacht-UTC gespeichert sind.
function conventionDayOptions(start: Date, end: Date) {
  const out: { value: string; label: string }[] = [];
  const d = new Date(start);
  d.setUTCHours(0, 0, 0, 0);
  const last = new Date(end);
  last.setUTCHours(0, 0, 0, 0);
  for (let guard = 0; d <= last && guard < 31; guard++) {
    out.push({
      value: d.toISOString().slice(0, 10),
      label: d.toLocaleDateString("de-DE", { weekday: "short", day: "numeric", month: "long", year: "numeric", timeZone: "UTC" }),
    });
    d.setUTCDate(d.getUTCDate() + 1);
  }
  return out;
}

// Extrahiert aus admin/conventions/page.tsx (Tab "Warteliste") — alle
// Slot-Anfragen über alle Conventions hinweg an einem Ort, plus
// Kollisions-Daten (bereits terminierte Shoots je Kalendertag) fürs
// Genehmigen-Formular. Rein informativ/nicht blockierend, wie im Web.
export async function getConventionWaitlistList() {
  await connectDB();
  const items = await Convention.find({}).sort({ startDate: 1 }).lean();
  const conventionById = new Map(items.map((c) => [String(c._id), c]));

  const allEntries = await WaitlistEntry.find({ conventionId: { $in: items.map((i) => i._id) } })
    .sort({ createdAt: 1 })
    .lean();
  const entryClientIds = [...new Set(allEntries.map((e) => String(e.clientId)))];
  const entryClients = await Client.find({ _id: { $in: entryClientIds } }).select("name email").lean();
  const entryClientById = new Map(entryClients.map((c) => [String(c._id), c]));

  const orderIds = allEntries.map((e) => e.orderId).filter(Boolean).map(String);
  const grantedOrders = orderIds.length ? await Order.find({ _id: { $in: orderIds } }).select("shootDate shootEndDate").lean() : [];
  const orderById = new Map(grantedOrders.map((o) => [String(o._id), o]));

  type DayBooking = { start: string; end: string; label: string };
  const bookingsByDay: Record<string, DayBooking[]> = {};
  if (items.length) {
    const minStart = new Date(Math.min(...items.map((c) => +new Date(c.startDate))));
    minStart.setHours(0, 0, 0, 0);
    const maxEnd = new Date(Math.max(...items.map((c) => +new Date(c.endDate))));
    maxEnd.setHours(23, 59, 59, 999);
    const scheduled = await Order.find({
      shootDate: { $gte: minStart, $lte: maxEnd },
      rejected: { $ne: true },
      archived: { $ne: true },
    })
      .select("shootDate shootEndDate characterOrConcept")
      .lean();
    for (const o of scheduled) {
      if (!o.shootDate) continue;
      const d = new Date(o.shootDate);
      const key = d.toLocaleDateString("en-CA");
      (bookingsByDay[key] ??= []).push({
        start: d.toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit" }),
        end: o.shootEndDate ? new Date(o.shootEndDate).toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit" }) : "",
        label: (o.characterOrConcept || "").split("\n")[0].slice(0, 60),
      });
    }
    for (const k of Object.keys(bookingsByDay)) bookingsByDay[k].sort((a, b) => a.start.localeCompare(b.start));
  }

  const fmtAppointment = (shootDate?: Date | null, shootEndDate?: Date | null) =>
    shootDate
      ? new Date(shootDate).toLocaleString("de-DE", { dateStyle: "medium", timeStyle: "short" }) +
        (shootEndDate ? `–${new Date(shootEndDate).toLocaleTimeString("de-DE", { hour: "2-digit", minute: "2-digit" })}` : "")
      : null;

  const entries = allEntries.map((e) => {
    const convention = conventionById.get(String(e.conventionId));
    const client = entryClientById.get(String(e.clientId));
    const order = e.orderId ? orderById.get(String(e.orderId)) : null;
    return {
      id: String(e._id),
      conventionId: String(e.conventionId),
      conventionName: convention?.name || "Unbekannte Convention",
      conventionDateLabel: convention ? dateRangeLabel(convention.startDate, convention.endDate) : "",
      conventionLocation: convention?.location || "",
      conventionDays: convention ? conventionDayOptions(convention.startDate, convention.endDate) : [],
      clientName: client?.name || "?",
      clientEmail: client?.email || "",
      message: e.message,
      joinedAt: fmtDay(e.createdAt),
      orderId: e.orderId ? String(e.orderId) : null,
      appointment: order ? fmtAppointment(order.shootDate, order.shootEndDate) : null,
    };
  });

  return { entries, bookingsByDay };
}
