import { connectDB } from "@/lib/db";
import { Calendar } from "@/models/Calendar";
import { CalendarEvent } from "@/models/CalendarEvent";
import { Order } from "@/models/Order";
import { Client } from "@/models/Client";
import { pushEventToGoogle, fetchGoogleEvent } from "@/lib/googleCalendar";
import { effectiveEventAddress } from "@/lib/orderConstants";
import { getSettings } from "@/lib/settings";

// Kein "use server" hier bewusst — enthält keine eigene Berechtigungsprüfung
// (die IDs/Autorisierung kommen vom Aufrufer: der Server-Action im Admin-UI,
// die eine Session prüft, oder dem Cron-Endpunkt, der ein Secret prüft).
// Überträgt alle Termine ohne googleEventId — idempotent, beliebig oft
// wiederholbar, fängt sowohl vor der Verbindung entstandene Termine als
// auch einzelne, zuvor fehlgeschlagene Pushes wieder ein.
export async function runGoogleBackfillForCalendar(calendarId: string): Promise<void> {
  await connectDB();
  const cal = await Calendar.findById(calendarId).lean();
  if (!cal) return;

  if (cal.kind === "orders") {
    const orders = await Order.find({ shootDate: { $ne: null }, googleEventId: "", rejected: false }).lean();
    const clientIds = [...new Set(orders.map((o) => String(o.clientId)))];
    const clients = clientIds.length ? await Client.find({ _id: { $in: clientIds } }).lean() : [];
    const clientById = new Map(clients.map((c) => [String(c._id), c]));

    for (const o of orders) {
      const googleEventId = await pushEventToGoogle(calendarId, "", {
        title: `${o.characterOrConcept} · ${clientById.get(String(o.clientId))?.name ?? "Kunde"}`,
        location: effectiveEventAddress(o),
        start: new Date(o.shootDate as Date),
        end: o.shootEndDate ? new Date(o.shootEndDate) : undefined,
      });
      if (googleEventId) await Order.findByIdAndUpdate(o._id, { googleEventId });
    }
  } else {
    const events = await CalendarEvent.find({ calendarId, googleEventId: "" }).lean();
    for (const e of events) {
      const googleEventId = await pushEventToGoogle(calendarId, "", {
        title: e.title,
        description: e.description,
        start: e.start,
        end: e.end,
        allDay: e.allDay,
      });
      if (googleEventId) await CalendarEvent.findByIdAndUpdate(e._id, { googleEventId });
    }
  }
}

export async function runGoogleBackfillForAllConnectedCalendars(): Promise<{ calendarId: string; name: string }[]> {
  await connectDB();
  const connected = await Calendar.find({ googleRefreshToken: { $ne: "" } }).select("name").lean();
  for (const cal of connected) {
    await runGoogleBackfillForCalendar(String(cal._id));
  }
  return connected.map((c) => ({ calendarId: String(c._id), name: c.name }));
}

// Rückrichtung: Zeiten, die direkt in Google geändert wurden, in die eigenen
// Datensätze übernehmen — nur bei bereits verknüpften Terminen (googleEventId
// gesetzt), nur Start-/Endzeit. Bewusst NICHT: in Google gelöschte Termine
// auch bei uns löschen, und NICHT Titel/Beschreibung übernehmen — beides zu
// riskant für einen automatischen Hintergrundlauf ohne Bestätigung.
export async function pullGoogleChangesForCalendar(calendarId: string): Promise<{ updated: number; deleted: number }> {
  await connectDB();
  const cal = await Calendar.findById(calendarId).lean();
  if (!cal || !cal.googleRefreshToken) return { updated: 0, deleted: 0 };

  // Opt-in (siehe googleSyncSettings.reflectDeletionsFromGoogle) — im
  // unbeaufsichtigten Cron-Lauf standardmäßig weiterhin so vorsichtig wie
  // ursprünglich: eine in Google abgesagte/gelöschte Terminserie wird nur
  // dann auch lokal entfernt, wenn das Team das bewusst aktiviert hat. Nur
  // remote.cancelled === true zählt als sicheres Löschsignal — ein `null`-
  // Rückgabewert (Netzwerk-/Auth-Fehler) wird nie als Löschung gewertet.
  const settings = await getSettings();
  const reflectDeletions = settings.googleSyncSettings.reflectDeletionsFromGoogle;

  let updated = 0;
  let deleted = 0;

  if (cal.kind === "orders") {
    const orders = await Order.find({ googleEventId: { $ne: "" }, rejected: false }).select("shootDate shootEndDate googleEventId").lean();
    for (const o of orders) {
      const remote = await fetchGoogleEvent(calendarId, o.googleEventId);
      if (!remote) continue;
      if (remote.cancelled) {
        if (reflectDeletions) {
          await Order.findByIdAndUpdate(o._id, { shootDate: null, shootEndDate: null, googleEventId: "" });
          deleted += 1;
        }
        continue;
      }
      const localStart = new Date(o.shootDate as Date).getTime();
      const localEnd = o.shootEndDate ? new Date(o.shootEndDate).getTime() : null;
      const remoteEnd = remote.end ? remote.end.getTime() : null;
      if (remote.start.getTime() !== localStart || remoteEnd !== localEnd) {
        await Order.findByIdAndUpdate(o._id, { shootDate: remote.start, shootEndDate: remote.end });
        updated += 1;
      }
    }
  } else {
    const events = await CalendarEvent.find({ calendarId, googleEventId: { $ne: "" } }).select("start end googleEventId").lean();
    for (const e of events) {
      const remote = await fetchGoogleEvent(calendarId, e.googleEventId);
      if (!remote) continue;
      if (remote.cancelled) {
        if (reflectDeletions) {
          await CalendarEvent.findByIdAndDelete(e._id);
          deleted += 1;
        }
        continue;
      }
      const localStart = new Date(e.start).getTime();
      const localEnd = e.end ? new Date(e.end).getTime() : null;
      const remoteEnd = remote.end ? remote.end.getTime() : null;
      if (remote.start.getTime() !== localStart || remoteEnd !== localEnd) {
        await CalendarEvent.findByIdAndUpdate(e._id, { start: remote.start, end: remote.end, allDay: remote.allDay });
        updated += 1;
      }
    }
  }

  return { updated, deleted };
}

export async function pullGoogleChangesForAllConnectedCalendars(): Promise<{ updated: number; deleted: number }> {
  await connectDB();
  const connected = await Calendar.find({ googleRefreshToken: { $ne: "" } }).select("_id").lean();
  let updated = 0;
  let deleted = 0;
  for (const cal of connected) {
    const result = await pullGoogleChangesForCalendar(String(cal._id));
    updated += result.updated;
    deleted += result.deleted;
  }
  return { updated, deleted };
}
