import { connectDB } from "@/lib/db";
import { Order } from "@/models/Order";
import { Client } from "@/models/Client";
import { getSettings } from "@/lib/settings";
import { sendMailIfEnabled } from "@/lib/mail";
import { emailShell } from "@/lib/emailTemplate";
import { notifyRecipient } from "@/lib/notifications";
import { geocodeCity, getDailyForecast, isBadWeatherLikely, describeWeatherCode } from "@/lib/weather";

async function safeSend(fn: () => Promise<void>) {
  try {
    await fn();
  } catch (err) {
    console.error("Wetter-Hinweis-Mail fehlgeschlagen:", err);
  }
}

function sendWeatherWarningMail(
  client: { id: string; email: string; name: string; locale: "de" | "en" },
  order: { characterOrConcept: string; shootDate: Date },
  weatherLabel: string,
  precipitationProbability: number
) {
  return safeSend(async () => {
    const isEn = client.locale === "en";
    const dateStr = order.shootDate.toLocaleString(isEn ? "en-GB" : "de-DE", { dateStyle: "full", timeStyle: "short" });
    const html = await emailShell({
      heading: `Hi ${client.name},`,
      bodyHtml: isEn
        ? `<p style="margin:0 0 8px;">Heads up — the current forecast for our outdoor shoot "${order.characterOrConcept}" on <strong>${dateStr}</strong> looks like <strong>${weatherLabel}</strong> (${precipitationProbability}% chance of precipitation).</p>
           <p style="margin:0;">No need to panic — forecasts can still change. But if you'd rather reschedule just in case, message me any time and we'll find a new date.</p>`
        : `<p style="margin:0 0 8px;">Kurzer Hinweis — die aktuelle Vorhersage für unser Outdoor-Shooting „${order.characterOrConcept}" am <strong>${dateStr}</strong> sieht nach <strong>${weatherLabel}</strong> aus (${precipitationProbability}% Regenwahrscheinlichkeit).</p>
           <p style="margin:0;">Muss nichts heißen — die Vorhersage kann sich noch ändern. Falls du aber lieber vorsorglich umplanen möchtest, schreib mir einfach, dann finden wir einen neuen Termin.</p>`,
      buttonLabel: isEn ? "View order" : "Auftrag ansehen",
      buttonUrl: `${process.env.SITE_URL}/${client.locale}/account/orders`,
      footerNote: isEn
        ? "You're receiving this because your outdoor shoot is coming up soon."
        : "Du erhältst diese E-Mail, weil dein Outdoor-Shooting bald ansteht.",
    });
    await sendMailIfEnabled(
      client.id,
      client.email,
      isEn ? `Weather heads-up for your shoot — Rakku Photography` : `Wetter-Hinweis zu deinem Shooting — Rakku Photography`,
      html
    );
  });
}

// Cron-Raster wie preshootReminder (siehe api/cron/weather-check) — prüft
// Outdoor-Aufträge mit Termin im konfigurierten Vorlauf-Fenster, deren
// Veranstaltungsort sich geocodieren lässt, und warnt bei absehbar
// schlechtem Wetter einmalig vor (kein Dauerspam bei wiederholten
// Cron-Läufen, siehe weatherWarningSentAt).
export async function runWeatherCheckSweep() {
  await connectDB();
  const settings = await getSettings();
  if (!settings.weatherCheckSettings.enabled) return { sent: 0, skipped: "disabled" as const };

  const now = new Date();
  const windowEnd = new Date(now.getTime() + settings.weatherCheckSettings.hoursBefore * 60 * 60 * 1000);

  const candidates = await Order.find({
    shootingType: "Outdoor",
    shootDate: { $gte: now, $lte: windowEnd },
    weatherWarningSentAt: null,
    rejected: false,
    archived: false,
  }).select("clientId characterOrConcept shootDate eventCity eventCountryCode");

  let sent = 0;
  let checked = 0;
  for (const order of candidates) {
    if (!order.eventCity) continue;
    const location = await geocodeCity(order.eventCity, order.eventCountryCode);
    if (!location || !order.shootDate) continue;
    checked += 1;

    const forecast = await getDailyForecast(location, order.shootDate);
    if (!forecast) continue;

    // Zu weit in der Zukunft geocodiert/geprüft, aber noch nicht warnungswürdig
    // — trotzdem als geprüft markieren wäre falsch, da sich die Vorhersage bis
    // zum nächsten Cron-Lauf noch ändern kann. Erst bei tatsächlich schlechtem
    // Wetter wird weatherWarningSentAt gesetzt (und damit endgültig geprüft).
    if (!isBadWeatherLikely(forecast)) continue;

    const client = await Client.findById(order.clientId).select("email name firstName nickname locale").lean();
    if (!client) continue;

    const isEn = client.locale === "en";
    const weatherLabel = describeWeatherCode(forecast.weatherCode, isEn);
    await sendWeatherWarningMail(
      {
        id: String(order.clientId),
        email: client.email,
        name: client.nickname || client.firstName || client.name,
        locale: client.locale,
      },
      { characterOrConcept: order.characterOrConcept, shootDate: order.shootDate },
      weatherLabel,
      forecast.precipitationProbabilityMax
    );
    // Ab hier separat abgesichert (wie schon der Mailversand über
    // safeSend) — ein Fehler bei EINEM Auftrag (z. B. kurzer DB-Hänger) soll
    // nicht den ganzen restlichen Cron-Durchlauf für alle anderen Kandidaten
    // abbrechen.
    try {
      await notifyRecipient(String(order.clientId), {
        type: "weather_warning",
        title: isEn ? `Weather heads-up — ${order.characterOrConcept}` : `Wetter-Hinweis — ${order.characterOrConcept}`,
        body: isEn ? `Forecast: ${weatherLabel}` : `Vorhersage: ${weatherLabel}`,
        link: `/${client.locale}/account/orders/${order._id}`,
      });
      order.weatherWarningSentAt = now;
      await order.save();
      sent += 1;
    } catch (err) {
      console.error("Wetter-Hinweis (Benachrichtigung/Speichern) fehlgeschlagen:", err);
    }
  }

  return { sent, checked, candidates: candidates.length };
}
