import { NextResponse } from "next/server";
import { parseICS } from "node-ical";
import { isAdminSession, requireSession } from "../../../../lib/auth-helpers";
import { prisma } from "../../../../lib/prisma";
import { checkRateLimit, getRateLimitConfig } from "../../../../lib/rate-limit";
import { getClientIp } from "../../../../lib/request";

const MAX_FILE_SIZE = 5 * 1024 * 1024;

const asText = (value: unknown) => {
  if (value === null || value === undefined) return "";
  if (typeof value === "string") return value.trim();
  return String(value).trim();
};

const parseGeo = (value: unknown) => {
  if (!value) return null;
  if (typeof value === "string") {
    const cleaned = value.trim();
    if (!cleaned) return null;
    const parts = cleaned.split(/[;,]/).map((part) => part.trim());
    if (parts.length >= 2) {
      const lat = Number(parts[0]);
      const lng = Number(parts[1]);
      if (!Number.isNaN(lat) && !Number.isNaN(lng)) {
        return { lat, lng };
      }
    }
    return null;
  }
  if (typeof value === "object") {
    const record = value as Record<string, unknown>;
    const lat = Number(record.lat ?? record.latitude);
    const lng = Number(record.lon ?? record.lng ?? record.longitude);
    if (!Number.isNaN(lat) && !Number.isNaN(lng)) {
      return { lat, lng };
    }
  }
  return null;
};

export async function POST(request: Request) {
  const { session } = await requireSession();
  if (!session) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }
  if (!isAdminSession(session)) {
    return NextResponse.json({ error: "Nur für Admins." }, { status: 403 });
  }

  const ip = getClientIp(request);
  const email = session.user?.email || "unknown";
  const rateKey = `icalimport:${email}:${ip}`;
  const rateConfig = getRateLimitConfig("RATE_LIMIT_ICAL_IMPORT", 5);
  const rate = await checkRateLimit({
    key: rateKey,
    limit: rateConfig.limit,
    windowMs: rateConfig.windowMs
  });
  if (!rate.ok) {
    return NextResponse.json(
      { error: "Zu viele Importe. Bitte später erneut versuchen." },
      { status: 429 }
    );
  }

  const formData = await request.formData();
  const file = formData.get("file");
  const categoryId = asText(formData.get("categoryId"));

  if (!(file instanceof File)) {
    return NextResponse.json(
      { error: "Bitte eine iCal-Datei hochladen." },
      { status: 400 }
    );
  }

  if (!categoryId) {
    return NextResponse.json(
      { error: "Bitte eine Kategorie auswählen." },
      { status: 400 }
    );
  }

  const category = await prisma.category.findUnique({
    where: { id: categoryId }
  });
  if (!category) {
    return NextResponse.json(
      { error: "Kategorie nicht gefunden." },
      { status: 404 }
    );
  }

  if (file.size > MAX_FILE_SIZE) {
    return NextResponse.json(
      { error: "Datei ist zu groß (max. 5 MB)." },
      { status: 400 }
    );
  }

  let parsed: Record<string, any>;
  try {
    const raw = await file.text();
    parsed = parseICS(raw);
  } catch (err) {
    return NextResponse.json(
      { error: "iCal-Datei konnte nicht gelesen werden." },
      { status: 400 }
    );
  }

  const entries = Object.values(parsed).filter(
    (entry) => entry && entry.type === "VEVENT"
  );

  if (entries.length === 0) {
    return NextResponse.json(
      { error: "Keine Termine in der iCal-Datei gefunden." },
      { status: 400 }
    );
  }

  let created = 0;
  let duplicates = 0;
  let skipped = 0;
  let recurringSkipped = 0;

  const creatorEmail = session.user?.email || "";

  for (const entry of entries) {
    if (entry.rrule) {
      recurringSkipped += 1;
      continue;
    }

    const title = asText(entry.summary);
    const start = entry.start instanceof Date ? entry.start : null;
    if (!title || !start || Number.isNaN(start.getTime())) {
      skipped += 1;
      continue;
    }

    const end =
      entry.end instanceof Date && !Number.isNaN(entry.end.getTime())
        ? entry.end
        : new Date(start.getTime() + 3 * 60 * 60 * 1000);
    const location = asText(entry.location) || null;
    const description = asText(entry.description) || null;
    const geo = parseGeo(entry.geo);

    const existing = await prisma.event.findFirst({
      where: {
        title,
        startAt: start,
        location,
        categoryId
      }
    });

    if (existing) {
      duplicates += 1;
      continue;
    }

    await prisma.event.create({
      data: {
        title,
        description,
        location,
        locationLat: geo ? geo.lat : null,
        locationLng: geo ? geo.lng : null,
        startAt: start,
        endAt: end,
        status: "APPROVED",
        createdBy: { connect: { email: creatorEmail } },
        category: { connect: { id: categoryId } }
      }
    });

    created += 1;
  }

  return NextResponse.json({
    created,
    duplicates,
    skipped,
    recurringSkipped
  });
}
