import { connectDB } from "@/lib/db";
import { Order } from "@/models/Order";
import { Client } from "@/models/Client";
import { Calendar } from "@/models/Calendar";
import { CalendarEvent } from "@/models/CalendarEvent";
import { Absence } from "@/models/Absence";
import { getOrCreateOrdersCalendar, getOrCreateAbsencesCalendar } from "@/lib/actions/calendars";
import { canViewCalendar, canEditCalendar } from "@/lib/calendars";
import { ABSENCE_TYPE_LABELS, type AbsenceType } from "@/lib/absenceConstants";
import { ORDER_TAG_COLORS, ORDER_TAG_LABELS, effectiveEventAddress, type OrderTag } from "@/lib/orderConstants";
import type { Permission } from "@/lib/permissions";

function dayKey(d: Date) {
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
}

type Item = {
  id: string;
  kind: "order" | "event" | "absence";
  start: string;
  end: string | null;
  title: string;
  color: string;
  tooltip: string;
  deletable: boolean;
  orderId?: string;
};

// Schlankere Variante von admin/calendar/page.tsx: dieselbe Monatsraster-
// Logik (Aufträge + eigene Termine + Abwesenheiten zusammengeführt), aber
// ohne die "cals"-Sichtbarkeits-Query (App zeigt immer alle sichtbaren
// Kalender) und ohne Google-Connect/Teilen-Verwaltung (bleibt der
// Web-Ansicht vorbehalten, siehe "Kalender verwalten im Browser" in der App).
export async function getCalendarMonthData(clientId: string, permissions: Record<Permission, boolean>, year: number, monthIndex: number) {
  const hasOrders = permissions.orders_view;
  const hasCalendars = permissions.calendars_view;
  const hasCalendarsEdit = permissions.calendars_edit;

  const monthStart = new Date(year, monthIndex, 1);
  const firstWeekday = (monthStart.getDay() + 6) % 7;
  const gridStart = new Date(year, monthIndex, 1 - firstWeekday);
  const lastDayOfMonth = new Date(year, monthIndex + 1, 0);
  const lastWeekday = (lastDayOfMonth.getDay() + 6) % 7;
  const gridEnd = new Date(year, monthIndex + 1, 0 + (6 - lastWeekday) + 1);

  const days: Date[] = [];
  for (let d = new Date(gridStart); d < gridEnd; d.setDate(d.getDate() + 1)) days.push(new Date(d));

  await connectDB();
  const now = new Date();

  const [ordersCalendar, absencesCalendar, allCustomCalendars, allStaffDocs] = await Promise.all([
    hasOrders ? getOrCreateOrdersCalendar() : null,
    getOrCreateAbsencesCalendar(),
    Calendar.find({ kind: "custom" }).lean(),
    Client.find({ role: "staff" }).select("name").lean(),
  ]);

  const viewableCustom = allCustomCalendars.filter((c) => canViewCalendar(c, clientId, hasOrders, hasCalendars));
  const clientById = new Map(allStaffDocs.map((c) => [String(c._id), c]));

  const calendars = [
    ...(ordersCalendar ? [{ id: String(ordersCalendar._id), name: ordersCalendar.name, color: ordersCalendar.color, kind: "orders" as const, canManage: false }] : []),
    ...(absencesCalendar ? [{ id: String(absencesCalendar._id), name: absencesCalendar.name, color: absencesCalendar.color, kind: "absences" as const, canManage: false }] : []),
    ...viewableCustom.map((c) => ({ id: String(c._id), name: c.name, color: c.color, kind: "custom" as const, canManage: canEditCalendar(c, clientId, hasCalendarsEdit) })),
  ];
  const colorByCalendarId = new Map(calendars.map((c) => [c.id, c.color]));
  const editableCalendarIds = new Set(calendars.filter((c) => c.canManage).map((c) => c.id));
  const visibleCustomIds = viewableCustom.map((c) => c._id);

  const [monthOrders, monthEvents, monthAbsences, upcomingOrders, upcomingEvents, upcomingAbsences] = await Promise.all([
    hasOrders ? Order.find({ shootDate: { $gte: gridStart, $lt: gridEnd }, rejected: false }).lean() : Order.find({ _id: null }).lean(),
    visibleCustomIds.length
      ? CalendarEvent.find({
          calendarId: { $in: visibleCustomIds },
          start: { $lt: gridEnd },
          $or: [{ end: { $gte: gridStart } }, { end: null, start: { $gte: gridStart } }],
        }).lean()
      : CalendarEvent.find({ _id: null }).lean(),
    Absence.find({ startDate: { $lt: gridEnd }, endDate: { $gte: gridStart } }).lean(),
    hasOrders ? Order.find({ shootDate: { $gte: now }, rejected: false }).sort({ shootDate: 1 }).limit(8).lean() : Order.find({ _id: null }).lean(),
    visibleCustomIds.length ? CalendarEvent.find({ calendarId: { $in: visibleCustomIds }, start: { $gte: now } }).sort({ start: 1 }).limit(8).lean() : CalendarEvent.find({ _id: null }).lean(),
    Absence.find({ endDate: { $gte: now } }).sort({ startDate: 1 }).limit(8).lean(),
  ]);

  const orderClientIds = [...new Set(monthOrders.concat(upcomingOrders).map((o) => String(o.clientId)))];
  const orderClients = orderClientIds.length ? await Client.find({ _id: { $in: orderClientIds } }).lean() : [];
  const orderClientById = new Map(orderClients.map((c) => [String(c._id), c]));

  const ordersToItems = (list: typeof monthOrders): Item[] =>
    list.map((o) => ({
      id: `order-${o._id}`,
      kind: "order" as const,
      start: new Date(o.shootDate as Date).toISOString(),
      end: o.shootEndDate ? new Date(o.shootEndDate).toISOString() : null,
      title: orderClientById.get(String(o.clientId))?.name ?? o.characterOrConcept,
      color: ORDER_TAG_COLORS[o.tag as OrderTag],
      tooltip: `${o.characterOrConcept} · ${orderClientById.get(String(o.clientId))?.name ?? ""} · ${ORDER_TAG_LABELS[o.tag as OrderTag]}${effectiveEventAddress(o) ? ` · 📍 ${effectiveEventAddress(o)}` : ""}`,
      deletable: false,
      orderId: String(o._id),
    }));

  const absencesToItems = (list: typeof monthAbsences): Item[] =>
    list.map((a) => ({
      id: `absence-${a._id}`,
      kind: "absence" as const,
      start: new Date(a.startDate).toISOString(),
      end: new Date(a.endDate).toISOString(),
      title: `${clientById.get(String(a.clientId))?.name ?? "Team"} – ${ABSENCE_TYPE_LABELS[a.type as AbsenceType]}`,
      color: absencesCalendar?.color ?? "#f472b6",
      tooltip: `${clientById.get(String(a.clientId))?.name ?? ""} · ${ABSENCE_TYPE_LABELS[a.type as AbsenceType]}${a.note ? " · " + a.note : ""}`,
      deletable: false,
    }));

  const eventsToItems = (list: typeof monthEvents): Item[] =>
    list.map((e) => ({
      id: String(e._id),
      kind: "event" as const,
      start: new Date(e.start).toISOString(),
      end: e.end ? new Date(e.end).toISOString() : null,
      title: e.title,
      color: colorByCalendarId.get(String(e.calendarId)) ?? "#a78bfa",
      tooltip: `${e.title}${e.description ? " · " + e.description : ""}`,
      deletable: editableCalendarIds.has(String(e.calendarId)),
    }));

  const allMonthItems = [...ordersToItems(monthOrders), ...eventsToItems(monthEvents), ...absencesToItems(monthAbsences)];
  const itemsByDay = new Map<string, Item[]>();
  for (const item of allMonthItems) {
    const start = new Date(item.start);
    const end = item.end && new Date(item.end).getTime() > start.getTime() ? new Date(item.end) : start;
    for (let d = new Date(start); dayKey(d) <= dayKey(end); d.setDate(d.getDate() + 1)) {
      const key = dayKey(d);
      if (!itemsByDay.has(key)) itemsByDay.set(key, []);
      itemsByDay.get(key)!.push(item);
    }
  }
  for (const list of itemsByDay.values()) list.sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime());

  const todayKey = dayKey(now);
  const gridDays = days.map((d) => ({
    date: dayKey(d),
    inMonth: d.getMonth() === monthIndex,
    isToday: dayKey(d) === todayKey,
    items: itemsByDay.get(dayKey(d)) ?? [],
  }));

  const upcoming = [...ordersToItems(upcomingOrders), ...eventsToItems(upcomingEvents), ...absencesToItems(upcomingAbsences)]
    .sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime())
    .slice(0, 8);

  return {
    year,
    monthIndex,
    calendars,
    editableCalendars: calendars.filter((c) => c.canManage).map((c) => ({ id: c.id, name: c.name })),
    days: gridDays,
    upcoming,
  };
}
