"use client";

import { useEffect, useMemo, useRef, useState } from "react";
import { createPortal } from "react-dom";
import FullCalendar from "@fullcalendar/react";
import dayGridPlugin from "@fullcalendar/daygrid";
import timeGridPlugin from "@fullcalendar/timegrid";
import listPlugin from "@fullcalendar/list";
import interactionPlugin from "@fullcalendar/interaction";
import deLocale from "@fullcalendar/core/locales/de";
import { useSession } from "next-auth/react";
import { CircleMarker, MapContainer, Popup, TileLayer, useMap } from "react-leaflet";
import EventForm from "./EventForm";

type EventItem = {
  id: string;
  title: string;
  description?: string | null;
  location?: string | null;
  locationPlaceId?: string | null;
  locationLat?: number | null;
  locationLng?: number | null;
  startAt: string;
  endAt?: string | null;
  status: string;
  publicOverride?: boolean | null;
  category?: { id: string; name: string; isPublic?: boolean } | null;
};

type ViewItem = {
  id: string;
  token: string;
  items: { eventId: string }[];
  categories: { categoryId: string; category?: { name: string } }[];
  exclusions: { eventId: string }[];
};

type DateBucket = "past" | "today" | "tomorrow" | "future";
type Pane = "calendar" | "list" | "map";

const MAP_FILTER_STORAGE_KEY = "mapFilters";
const CALENDAR_VIEW_STORAGE_KEY = "calendarViewType";
const LIST_FILTER_STORAGE_KEY = "listQuickFilters";
const DEFAULT_CALENDAR_VIEW = "dayGridMonth";
const ALLOWED_CALENDAR_VIEWS = [
  "dayGridMonth",
  "timeGridWeek",
  "dayGridYear",
  "listWeek"
];

export default function CalendarBoard() {
  const { data, status } = useSession();
  const [events, setEvents] = useState<EventItem[]>([]);
  const [view, setView] = useState<ViewItem | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [loading, setLoading] = useState(false);
  const [query, setQuery] = useState("");
  const [categoryFilter, setCategoryFilter] = useState("ALL");
  const [sortKey, setSortKey] = useState("startAt");
  const [sortDir, setSortDir] = useState<"asc" | "desc">("asc");
  const [detailsEvent, setDetailsEvent] = useState<EventItem | null>(null);
  const [placesKey, setPlacesKey] = useState("");
  const [placesProvider, setPlacesProvider] = useState<"google" | "osm">("osm");
  const [categories, setCategories] = useState<{ id: string; name: string }[]>(
    []
  );
  const [editEvent, setEditEvent] = useState<EventItem | null>(null);
  const [isEditOpen, setIsEditOpen] = useState(false);
  const [editStatus, setEditStatus] = useState<string | null>(null);
  const [editError, setEditError] = useState<string | null>(null);
  const [bulkSelection, setBulkSelection] = useState<Set<string>>(new Set());
  const [mapFullscreen, setMapFullscreen] = useState(false);
  const [publicAccessEnabled, setPublicAccessEnabled] = useState<boolean | null>(null);
  const [isDarkTheme, setIsDarkTheme] = useState(false);
  const [mapDateFilter, setMapDateFilter] = useState<Set<DateBucket>>(
    new Set(["past", "today", "tomorrow", "future"])
  );
  const [mapCategoryFilter, setMapCategoryFilter] = useState<Set<string>>(
    new Set()
  );
  const [formOpen, setFormOpen] = useState(false);
  const [prefillStartAt, setPrefillStartAt] = useState<string | null>(null);
  const [viewOrder, setViewOrder] = useState<Array<Pane>>([
    "list",
    "calendar",
    "map"
  ]);
  const [collapsed, setCollapsed] = useState<Record<Pane, boolean>>({
    calendar: false,
    list: false,
    map: false
  });
  const [listQuickRange, setListQuickRange] = useState<
    "next7" | "next30" | "nextYear" | null
  >("next30");
  const [hidePastInList, setHidePastInList] = useState(false);
  const [initialView, setInitialView] = useState(() => {
    if (typeof window === "undefined") return DEFAULT_CALENDAR_VIEW;
    try {
      const stored = window.localStorage.getItem(CALENDAR_VIEW_STORAGE_KEY);
      if (stored && ALLOWED_CALENDAR_VIEWS.includes(stored)) {
        return stored;
      }
    } catch {
      // ignore
    }
    return DEFAULT_CALENDAR_VIEW;
  });
  const [dragSource, setDragSource] = useState<Pane | null>(null);
  const [dragOver, setDragOver] = useState<Pane | null>(null);
  const [isPointerDragging, setIsPointerDragging] = useState(false);
  const pointerDragRef = useRef<{ pointerId: number; source: Pane } | null>(null);
  const dragOverRef = useRef<Pane | null>(null);
  const dragHandleRef = useRef<HTMLDivElement | null>(null);
  const calendarRef = useRef<HTMLDivElement | null>(null);
  const listRef = useRef<HTMLDivElement | null>(null);
  const mapRef = useRef<HTMLDivElement | null>(null);
  const [portalRoot, setPortalRoot] = useState<HTMLElement | null>(null);
  const detailsModalRef = useRef<HTMLDivElement | null>(null);
  const lastActiveElementRef = useRef<HTMLElement | null>(null);
  const isAdmin =
    data?.user?.role === "ADMIN" || data?.user?.role === "SUPERADMIN";
  const canManageView = Boolean(view && data?.user);
  const showPublicControls = publicAccessEnabled === true;

  const fetchEvents = async (includeView: boolean) => {
    setLoading(true);
    setError(null);
    try {
      const requests = [fetch("/api/events")];
      if (includeView) {
        requests.push(fetch("/api/views/default"));
      }
      const [eventsResponse, viewResponse] = await Promise.all(requests);
      if (!eventsResponse.ok) {
        throw new Error("Events konnten nicht geladen werden.");
      }
      const payload = await eventsResponse.json();
      setEvents(payload);
      if (includeView && viewResponse?.ok) {
        setView(await viewResponse.json());
      } else {
        setView(null);
      }
    } catch (err) {
      setError((err as Error).message);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    if (status === "loading") return;
    fetchEvents(Boolean(data?.user));
  }, [data?.user, status]);

  useEffect(() => {
    const loadKey = async () => {
      try {
      const response = await fetch("/api/settings/system");
        if (!response.ok) return;
        const payload = await response.json();
        setPlacesKey(payload.apiKey || "");
        setPlacesProvider(payload.provider === "google" ? "google" : "osm");
        setPublicAccessEnabled(payload.publicAccessEnabled !== false);
      } catch {
        // ignore
      }
    };

    if (data?.user) {
      loadKey();
    }
  }, [data?.user]);

  useEffect(() => {
    if (!data?.user || !isAdmin) return;
    const loadCategories = async () => {
      try {
        const response = await fetch("/api/categories");
        if (!response.ok) return;
        const payload = await response.json();
        setCategories(payload);
      } catch {
        // ignore
      }
    };
    loadCategories();
  }, [data?.user, isAdmin]);

  useEffect(() => {
    const handler = () => {
      fetchEvents(Boolean(data?.user));
    };
    window.addEventListener("views-updated", handler);
    return () => window.removeEventListener("views-updated", handler);
  }, [data?.user]);

  useEffect(() => {
    if (typeof document === "undefined") return;
    setPortalRoot(document.body);
  }, []);

  useEffect(() => {
    try {
      const stored = window.localStorage.getItem(MAP_FILTER_STORAGE_KEY);
      if (!stored) return;
      const parsed = JSON.parse(stored);
      const allowed: DateBucket[] = ["past", "today", "tomorrow", "future"];
      if (parsed?.dates && Array.isArray(parsed.dates)) {
        const nextDates = parsed.dates.filter((value: string) =>
          allowed.includes(value as DateBucket)
        );
        if (nextDates.length > 0) {
          setMapDateFilter(new Set(nextDates as DateBucket[]));
        }
      }
      if (parsed?.categories && Array.isArray(parsed.categories)) {
        setMapCategoryFilter(new Set(parsed.categories.filter(Boolean)));
      }
    } catch {
      // ignore
    }
  }, []);

  useEffect(() => {
    try {
      const payload = {
        dates: Array.from(mapDateFilter),
        categories: Array.from(mapCategoryFilter)
      };
      window.localStorage.setItem(MAP_FILTER_STORAGE_KEY, JSON.stringify(payload));
    } catch {
      // ignore
    }
  }, [mapDateFilter, mapCategoryFilter]);

  useEffect(() => {
    if (typeof document === "undefined") return;
    const root = document.documentElement;
    const updateTheme = () => {
      setIsDarkTheme(root.dataset.theme === "dark");
    };
    updateTheme();
    const observer = new MutationObserver(updateTheme);
    observer.observe(root, { attributes: true, attributeFilter: ["data-theme"] });
    return () => observer.disconnect();
  }, []);

  useEffect(() => {
    if (!detailsEvent) return;
    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === "Escape") {
        setDetailsEvent(null);
      }
    };
    window.addEventListener("keydown", handleKeyDown);
    return () => window.removeEventListener("keydown", handleKeyDown);
  }, [detailsEvent]);

  useEffect(() => {
    if (!isEditOpen) return;
    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === "Escape") {
        setIsEditOpen(false);
        setEditEvent(null);
      }
    };
    window.addEventListener("keydown", handleKeyDown);
    return () => window.removeEventListener("keydown", handleKeyDown);
  }, [isEditOpen]);

  useEffect(() => {
    if (!mapFullscreen) return;
    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === "Escape") {
        setMapFullscreen(false);
      }
    };
    window.addEventListener("keydown", handleKeyDown);
    return () => window.removeEventListener("keydown", handleKeyDown);
  }, [mapFullscreen]);

  useEffect(() => {
    if (!detailsEvent) return;
    lastActiveElementRef.current = document.activeElement as HTMLElement | null;
    const node = detailsModalRef.current;
    if (!node) return;
    const focusable = node.querySelectorAll<HTMLElement>(
      'button,[href],input,select,textarea,[tabindex]:not([tabindex="-1"])'
    );
    const target = focusable[0] || node;
    target.focus();
  }, [detailsEvent]);

  useEffect(() => {
    if (detailsEvent) return;
    const previous = lastActiveElementRef.current;
    if (previous) {
      previous.focus();
      lastActiveElementRef.current = null;
    }
  }, [detailsEvent]);

  useEffect(() => {
    try {
      const stored = window.localStorage.getItem("listSort");
      if (!stored) return;
      const parsed = JSON.parse(stored);
      if (
        parsed &&
        typeof parsed === "object" &&
        typeof parsed.key === "string" &&
        (parsed.dir === "asc" || parsed.dir === "desc")
      ) {
        setSortKey(parsed.key);
        setSortDir(parsed.dir);
      }
    } catch {
      // ignore
    }
  }, []);

  useEffect(() => {
    try {
      const stored = window.localStorage.getItem(LIST_FILTER_STORAGE_KEY);
      if (!stored) return;
      const parsed = JSON.parse(stored);
      if (
        parsed &&
        typeof parsed === "object" &&
        (parsed.quickRange === "next7" ||
          parsed.quickRange === "next30" ||
          parsed.quickRange === "nextYear" ||
          parsed.quickRange === null) &&
        typeof parsed.hidePast === "boolean"
      ) {
        setListQuickRange(parsed.quickRange);
        setHidePastInList(parsed.hidePast);
      }
    } catch {
      // ignore
    }
  }, []);

  useEffect(() => {
    try {
      window.localStorage.setItem(
        LIST_FILTER_STORAGE_KEY,
        JSON.stringify({ quickRange: listQuickRange, hidePast: hidePastInList })
      );
    } catch {
      // ignore
    }
  }, [listQuickRange, hidePastInList]);

  useEffect(() => {
    try {
      window.localStorage.setItem(
        "listSort",
        JSON.stringify({ key: sortKey, dir: sortDir })
      );
    } catch {
      // ignore
    }
  }, [sortKey, sortDir]);

  useEffect(() => {
    try {
      const stored = window.localStorage.getItem("calendarViewOrder");
      if (!stored) return;
      const parsed = JSON.parse(stored);
      if (Array.isArray(parsed)) {
        const normalized = [
          ...new Set(
            parsed.filter((item) =>
              ["calendar", "list", "map"].includes(String(item))
            )
          )
        ] as Array<Pane>;
        if (normalized.includes("calendar") && normalized.includes("list")) {
          if (!normalized.includes("map")) {
            normalized.splice(1, 0, "map");
          }
          setViewOrder(normalized);
        }
      }
    } catch {
      // ignore
    }
  }, []);

  useEffect(() => {
    try {
      const stored = window.localStorage.getItem("calendarViewCollapsed");
      if (!stored) return;
      const parsed = JSON.parse(stored);
      if (
        parsed &&
        typeof parsed === "object" &&
        typeof parsed.calendar === "boolean" &&
        typeof parsed.list === "boolean"
      ) {
        setCollapsed({
          calendar: parsed.calendar,
          list: parsed.list,
          map: typeof parsed.map === "boolean" ? parsed.map : false
        });
      }
    } catch {
      // ignore
    }
  }, []);

  const persistOrder = (nextOrder: Array<Pane>) => {
    setViewOrder(nextOrder);
    try {
      window.localStorage.setItem("calendarViewOrder", JSON.stringify(nextOrder));
    } catch {
      // ignore
    }
  };

  const toggleCollapse = (section: Pane) => {
    const next = { ...collapsed, [section]: !collapsed[section] };
    setCollapsed(next);
    try {
      window.localStorage.setItem("calendarViewCollapsed", JSON.stringify(next));
    } catch {
      // ignore
    }
  };

  const swapOrder = (source: Pane, target: Pane) => {
    if (source === target) return;
    const next = [...viewOrder];
    const sourceIndex = next.indexOf(source);
    const targetIndex = next.indexOf(target);
    if (sourceIndex === -1 || targetIndex === -1) return;
    next[sourceIndex] = target;
    next[targetIndex] = source;
    persistOrder(next);
  };

  const onDragStart = (
    event: React.DragEvent<HTMLDivElement>,
    section: Pane
  ) => {
    event.dataTransfer.setData("text/plain", section);
    event.dataTransfer.effectAllowed = "move";
    setDragSource(section);
    setDragOver(section);
    const node =
      section === "calendar"
        ? calendarRef.current
        : section === "list"
        ? listRef.current
        : mapRef.current;
    if (node) {
      event.dataTransfer.setDragImage(node, 20, 20);
    }
  };

  const onDragEnter = (target: Pane) => {
    setDragOver(target);
  };

  const onDragEnd = () => {
    setDragSource(null);
    setDragOver(null);
  };

  const onDrop = (event: React.DragEvent<HTMLDivElement>, target: Pane) => {
    event.preventDefault();
    const source = event.dataTransfer.getData("text/plain") as
      | "calendar"
      | "list"
      | "map";
    if (source) {
      swapOrder(source, target);
    }
    setDragSource(null);
    setDragOver(null);
  };

  const getPaneFromPoint = (x: number, y: number) => {
    if (typeof document === "undefined") return null;
    const element = document.elementFromPoint(x, y);
    const card = element?.closest<HTMLElement>("[data-pane]");
    const pane = card?.dataset.pane as Pane | undefined;
    if (pane === "calendar" || pane === "list" || pane === "map") {
      return pane;
    }
    return null;
  };

  const finishPointerDrag = () => {
    const current = pointerDragRef.current;
    const handle = dragHandleRef.current;
    if (current && handle) {
      try {
        handle.releasePointerCapture(current.pointerId);
      } catch {
        // ignore
      }
    }
    pointerDragRef.current = null;
    dragOverRef.current = null;
    dragHandleRef.current = null;
    setDragSource(null);
    setDragOver(null);
    setIsPointerDragging(false);
  };

  const onPointerDragStart = (
    event: React.PointerEvent<HTMLDivElement>,
    section: Pane
  ) => {
    if (event.pointerType === "mouse") return;
    event.preventDefault();
    pointerDragRef.current = { pointerId: event.pointerId, source: section };
    dragOverRef.current = section;
    dragHandleRef.current = event.currentTarget;
    event.currentTarget.setPointerCapture(event.pointerId);
    setDragSource(section);
    setDragOver(section);
    setIsPointerDragging(true);
  };

  useEffect(() => {
    if (!isPointerDragging) return;
    const handleMove = (event: PointerEvent) => {
      const current = pointerDragRef.current;
      if (!current || event.pointerId !== current.pointerId) return;
      if (event.cancelable) {
        event.preventDefault();
      }
      const target = getPaneFromPoint(event.clientX, event.clientY);
      if (target !== dragOverRef.current) {
        dragOverRef.current = target;
        setDragOver(target);
      }
    };
    const handleUp = (event: PointerEvent) => {
      const current = pointerDragRef.current;
      if (!current || event.pointerId !== current.pointerId) return;
      const target = dragOverRef.current;
      if (target && target !== current.source) {
        swapOrder(current.source, target);
      }
      finishPointerDrag();
    };
    window.addEventListener("pointermove", handleMove);
    window.addEventListener("pointerup", handleUp);
    window.addEventListener("pointercancel", handleUp);
    return () => {
      window.removeEventListener("pointermove", handleMove);
      window.removeEventListener("pointerup", handleUp);
      window.removeEventListener("pointercancel", handleUp);
    };
  }, [isPointerDragging, swapOrder]);

  const openFormForDate = (date: Date | null) => {
    if (date) {
      const prefill = new Date(date);
      prefill.setHours(12, 0, 0, 0);
      setPrefillStartAt(prefill.toISOString());
    } else {
      setPrefillStartAt(null);
    }
    setFormOpen(true);
  };

  const applyEventTooltip = (root: HTMLElement, title: string) => {
    const elements = Array.from(
      root.querySelectorAll<HTMLElement>(".event-shell .truncate")
    );
    elements.forEach((element) => {
      element.removeAttribute("title");
    });
    root.setAttribute("title", title);
  };

  const calendarEvents = useMemo(
    () =>
      events.map((event) => ({
        id: event.id,
        title: event.title,
        start: event.startAt,
        end: event.endAt || undefined,
        extendedProps: {
          status: event.status,
          location: event.location,
          description: event.description,
          category: event.category?.name
        }
      })),
    [events]
  );

  const selectedEventIds = useMemo(() => {
    if (!view) return new Set<string>();
    const ids = new Set(view.items.map((item) => item.eventId));
    const excluded = new Set(view.exclusions.map((item) => item.eventId));
    const subscribedCategoryIds = new Set(
      view.categories.map((item) => item.categoryId)
    );
    events.forEach((event) => {
      if (event.category?.id && subscribedCategoryIds.has(event.category.id)) {
        ids.add(event.id);
      }
    });
    excluded.forEach((eventId) => ids.delete(eventId));
    return ids;
  }, [view, events]);

  const subscribedCategoryIds = useMemo(() => {
    return new Set(view?.categories.map((item) => item.categoryId) || []);
  }, [view]);

  const toggleEvent = async (eventId: string) => {
    if (!view) return;
    const isSelected = selectedEventIds.has(eventId);
    await fetch(`/api/views/${view.id}/items`, {
      method: isSelected ? "DELETE" : "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ eventId })
    });
    await fetchEvents(Boolean(data?.user));
    window.dispatchEvent(new Event("views-updated"));
  };

  const formatLocalDateTime = (value?: string | null) => {
    if (!value) return "";
    const date = new Date(value);
    const offset = date.getTimezoneOffset() * 60000;
    return new Date(date.getTime() - offset).toISOString().slice(0, 16);
  };

  const toIsoString = (value: FormDataEntryValue | null) => {
    if (!value) return null;
    const raw = String(value);
    if (!raw) return null;
    const date = new Date(raw);
    if (Number.isNaN(date.getTime())) return null;
    return date.toISOString();
  };

  const parsePublicOverride = (value: FormDataEntryValue | null) => {
    if (!value) return null;
    const raw = String(value);
    if (raw === "public") return true;
    if (raw === "private") return false;
    return null;
  };

  const updateEvent = async (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    setEditStatus(null);
    setEditError(null);
    if (!editEvent) return;

    const formData = new FormData(event.currentTarget);
    const payload: Record<string, unknown> = {
      title: formData.get("title"),
      description: formData.get("description"),
      location: formData.get("location"),
      locationPlaceId: formData.get("locationPlaceId"),
      locationLat: formData.get("locationLat"),
      locationLng: formData.get("locationLng"),
      startAt: toIsoString(formData.get("startAt")),
      endAt: toIsoString(formData.get("endAt")),
      categoryId: formData.get("categoryId")
    };
    if (showPublicControls) {
      payload.publicOverride = parsePublicOverride(
        formData.get("publicOverride")
      );
    }

    const response = await fetch(`/api/events/${editEvent.id}`, {
      method: "PATCH",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(payload)
    });

    if (!response.ok) {
      const data = await response.json();
      setEditError(data.error || "Termin konnte nicht aktualisiert werden.");
      return;
    }

    setEditStatus("Termin aktualisiert.");
    setIsEditOpen(false);
    setEditEvent(null);
    await fetchEvents(Boolean(data?.user));
  };

  const deleteEvent = async (eventId: string) => {
    const ok = window.confirm("Termin wirklich löschen?");
    if (!ok) return;
    const response = await fetch(`/api/events/${eventId}`, { method: "DELETE" });
    if (!response.ok) {
      setEditError("Termin konnte nicht gelöscht werden.");
      return;
    }
    setEditStatus(null);
    setEditError(null);
    setIsEditOpen(false);
    setEditEvent(null);
    await fetchEvents(Boolean(data?.user));
  };

  const toggleBulkSelection = (eventId: string) => {
    setBulkSelection((prev) => {
      const next = new Set(prev);
      if (next.has(eventId)) {
        next.delete(eventId);
      } else {
        next.add(eventId);
      }
      return next;
    });
  };

  const toggleSelectAllVisible = (checked: boolean) => {
    if (!checked) {
      setBulkSelection(new Set());
      return;
    }
    setBulkSelection(new Set(displayedEvents.map((event) => event.id)));
  };

  const deleteSelectedEvents = async () => {
    if (bulkSelection.size === 0) return;
    const ok = window.confirm(
      `Wirklich ${bulkSelection.size} Termin(e) löschen?`
    );
    if (!ok) return;
    await Promise.all(
      Array.from(bulkSelection).map((eventId) =>
        fetch(`/api/events/${eventId}`, { method: "DELETE" })
      )
    );
    setBulkSelection(new Set());
    await fetchEvents(Boolean(data?.user));
  };


  const filteredEvents = useMemo(() => {
    const normalizedQuery = query.trim().toLowerCase();
    const filtered = events.filter((event) => {
      const categoryName = event.category?.name || "Ohne Kategorie";
      if (categoryFilter !== "ALL" && categoryName !== categoryFilter) {
        return false;
      }
      if (!normalizedQuery) return true;
      return (
        event.title.toLowerCase().includes(normalizedQuery) ||
        (event.description || "").toLowerCase().includes(normalizedQuery) ||
        (event.location || "").toLowerCase().includes(normalizedQuery) ||
        categoryName.toLowerCase().includes(normalizedQuery)
      );
    });

    const sorted = [...filtered].sort((a, b) => {
      const dir = sortDir === "asc" ? 1 : -1;
      if (sortKey === "title") {
        return a.title.localeCompare(b.title) * dir;
      }
      if (sortKey === "category") {
        const aCat = a.category?.name || "Ohne Kategorie";
        const bCat = b.category?.name || "Ohne Kategorie";
        return aCat.localeCompare(bCat) * dir;
      }
      if (sortKey === "location") {
        const aLoc = a.location || "";
        const bLoc = b.location || "";
        return aLoc.localeCompare(bLoc) * dir;
      }
      if (sortKey === "status") {
        return a.status.localeCompare(b.status) * dir;
      }
      const aDate = new Date(a.startAt).getTime();
      const bDate = new Date(b.startAt).getTime();
      return (aDate - bDate) * dir;
    });

    return sorted;
  }, [events, query, categoryFilter, sortKey, sortDir]);

  const startOfDay = (value: Date) =>
    new Date(value.getFullYear(), value.getMonth(), value.getDate());

  const addDays = (value: Date, amount: number) => {
    const next = new Date(value);
    next.setDate(next.getDate() + amount);
    return next;
  };

  const listRange = useMemo(() => {
    if (!listQuickRange && !hidePastInList) return null;
    const today = startOfDay(new Date());
    const start = hidePastInList
      ? today
      : listQuickRange
      ? addDays(today, -1)
      : today;
    let end: Date | null = null;
    if (listQuickRange === "next7") {
      end = addDays(today, 7);
    } else if (listQuickRange === "next30") {
      end = addDays(today, 30);
    } else if (listQuickRange === "nextYear") {
      end = addDays(today, 365);
    }
    return { start, end };
  }, [listQuickRange, hidePastInList]);

  const filterButtonClass = (active: boolean) =>
    `rounded-full border px-3 py-1 text-xs font-semibold transition ${
      active
        ? "border-slate-900 bg-slate-900 text-white"
        : "border-slate-200 text-slate-700 hover:bg-slate-100"
    }`;

  const categoryOptions = useMemo(() => {
    const list = events
      .map((event) => event.category?.name || "Ohne Kategorie")
      .filter(Boolean);
    return Array.from(new Set(list)).sort();
  }, [events]);

  const getDateBucket = (value: string): DateBucket => {
    const date = new Date(value);
    if (Number.isNaN(date.getTime())) return "future";
    const now = new Date();
    const startToday = new Date(now.getFullYear(), now.getMonth(), now.getDate());
    const startTomorrow = new Date(startToday);
    startTomorrow.setDate(startTomorrow.getDate() + 1);
    const startAfterTomorrow = new Date(startTomorrow);
    startAfterTomorrow.setDate(startAfterTomorrow.getDate() + 1);

    if (date < startToday) return "past";
    if (date >= startToday && date < startTomorrow) return "today";
    if (date >= startTomorrow && date < startAfterTomorrow) return "tomorrow";
    return "future";
  };

  const getCategoryColor = (value?: string | null) => {
    const palette = [
      "#2563eb",
      "#7c3aed",
      "#db2777",
      "#ea580c",
      "#16a34a",
      "#0f766e",
      "#0ea5e9",
      "#65a30d"
    ];
    const name = (value || "Ohne Kategorie").toLowerCase();
    let hash = 0;
    for (let i = 0; i < name.length; i += 1) {
      hash = (hash * 31 + name.charCodeAt(i)) % 997;
    }
    return palette[hash % palette.length];
  };

  const mapEvents = useMemo(() => {
    const currentYear = new Date().getFullYear();
    return events
      .filter((event) => {
        if (!event.locationLat || !event.locationLng) return false;
        const start = new Date(event.startAt);
        return start.getFullYear() === currentYear;
      })
      .map((event) => ({
        ...event,
        bucket: getDateBucket(event.startAt),
        categoryName: event.category?.name || "Ohne Kategorie"
      }));
  }, [events]);

  const filteredMapEvents = useMemo(() => {
    return mapEvents.filter((event) => {
      if (!mapDateFilter.has(event.bucket)) return false;
      if (mapCategoryFilter.size === 0) return true;
      return mapCategoryFilter.has(event.categoryName);
    });
  }, [mapEvents, mapDateFilter, mapCategoryFilter]);

  const mapPoints = useMemo(
    () =>
      filteredMapEvents.map((event) => ({
        lat: event.locationLat as number,
        lng: event.locationLng as number
      })),
    [filteredMapEvents]
  );

  const mapCategoryOptions = useMemo(() => {
    return Array.from(
      new Set(mapEvents.map((event) => event.categoryName))
    ).sort((a, b) => a.localeCompare(b));
  }, [mapEvents]);

  const toggleMapDateFilter = (key: DateBucket) => {
    setMapDateFilter((prev) => {
      const next = new Set(prev);
      if (next.has(key)) {
        next.delete(key);
      } else {
        next.add(key);
      }
      return next;
    });
  };

  const toggleMapCategoryFilter = (category: string) => {
    setMapCategoryFilter((prev) => {
      const next = new Set(prev);
      if (next.has(category)) {
        next.delete(category);
      } else {
        next.add(category);
      }
      return next;
    });
  };

  const displayedEvents = useMemo(() => {
    if (!listRange) {
      return filteredEvents;
    }
    return filteredEvents.filter((event) => {
      const start = new Date(event.startAt);
      if (Number.isNaN(start.getTime())) return false;
      if (start < listRange.start) return false;
      if (listRange.end && start >= listRange.end) return false;
      return true;
    });
  }, [filteredEvents, listRange]);

  const tableWidths = isAdmin
    ? {
        checkbox: "w-[5%]",
        date: "w-[18%]",
        title: "w-[26%]",
        category: "w-[16%]",
        location: "w-[23%]",
        actions: "w-[12%]"
      }
    : {
        date: "w-[20%]",
        title: "w-[30%]",
        category: "w-[18%]",
        location: "w-[24%]",
        actions: "w-[8%]"
      };

  useEffect(() => {
    setBulkSelection((prev) => (prev.size > 0 ? new Set() : prev));
  }, [query, categoryFilter, listRange]);

  const toggleSort = (nextKey: string) => {
    let nextDir: "asc" | "desc" = "asc";
    if (sortKey === nextKey) {
      nextDir = sortDir === "asc" ? "desc" : "asc";
      setSortDir(nextDir);
    } else {
      setSortKey(nextKey);
      setSortDir("asc");
    }
    try {
      const key = sortKey === nextKey ? sortKey : nextKey;
      window.localStorage.setItem(
        "listSort",
        JSON.stringify({ key, dir: nextDir })
      );
    } catch {
      // ignore
    }
  };

  return (
    <section className="space-y-4 fade-up">
      <div className="flex items-center justify-between">
        <div>
          <p className="text-xs uppercase tracking-[0.2em] text-slate-500">Kalender</p>
        </div>
        <div className="flex items-center gap-2">
          {data?.user && (
            <button
              type="button"
              className="btn-accent inline-flex items-center gap-2"
              onClick={() => openFormForDate(null)}
            >
              <svg
                viewBox="0 0 24 24"
                className="h-4 w-4"
                fill="none"
                stroke="currentColor"
                strokeWidth="2"
              >
                <path d="M12 5v14M5 12h14" strokeLinecap="round" />
              </svg>
              Termin
            </button>
          )}
          <button
            type="button"
            onClick={() => fetchEvents(Boolean(data?.user))}
            className="btn-ghost p-2"
            aria-label="Aktualisieren"
            title="Aktualisieren"
          >
            <svg
              viewBox="0 0 24 24"
              className="h-4 w-4"
              fill="none"
              stroke="currentColor"
              strokeWidth="2"
            >
              <path
                d="M20 12a8 8 0 1 1-2.34-5.66"
                strokeLinecap="round"
              />
              <path d="M20 4v6h-6" strokeLinecap="round" />
            </svg>
          </button>
        </div>
      </div>
      {data?.user && (
        <EventForm
          variant="inline"
          showTrigger={false}
          open={formOpen}
          onOpenChange={(open) => {
            setFormOpen(open);
            if (!open) {
              setPrefillStartAt(null);
            }
          }}
          prefillStartAt={prefillStartAt}
        />
      )}
      {error && <p className="text-sm text-red-600">{error}</p>}
      {loading && <p className="text-sm text-slate-500">Lade Termine...</p>}
      {viewOrder.map((section) => {
        if (section === "calendar") {
          return (
            <div
              key="calendar"
              ref={calendarRef}
              data-pane="calendar"
              className={`card drag-card calendar-pane ${
                dragSource === "calendar" ? "dragging" : ""
              } ${
                dragOver === "calendar" && dragSource && dragSource !== "calendar"
                  ? "drag-target"
                  : ""
              } ${
                dragSource === "list" && dragOver === "calendar" ? "shift-down" : ""
              }`}
              onDragOver={(event) => event.preventDefault()}
              onDragEnter={() => onDragEnter("calendar")}
              onDrop={(event) => onDrop(event, "calendar")}
            >
              <div className="mb-3 flex items-center justify-between">
                <h3 className="text-sm font-semibold text-slate-700">Kalender</h3>
                <div className="flex items-center gap-2">
                  <button
                    type="button"
                    className="rounded-full border border-slate-200 px-2 py-1 text-xs text-slate-600"
                    onClick={() => toggleCollapse("calendar")}
                    aria-label={collapsed.calendar ? "Kalender aufklappen" : "Kalender zuklappen"}
                    title={collapsed.calendar ? "Aufklappen" : "Zuklappen"}
                  >
                    {collapsed.calendar ? <IconChevronDown /> : <IconChevronUp />}
                  </button>
                  <div
                    className="drag-handle"
                    draggable
                    onDragStart={(event) => onDragStart(event, "calendar")}
                    onDragEnd={onDragEnd}
                    onPointerDown={(event) => onPointerDragStart(event, "calendar")}
                    title="Zum Tauschen ziehen"
                    aria-label="Kalender verschieben"
                  >
                    <IconGrip />
                  </div>
                </div>
              </div>
              {!collapsed.calendar && (
                <FullCalendar
                  plugins={[dayGridPlugin, timeGridPlugin, listPlugin, interactionPlugin]}
                locale={deLocale}
                initialView={initialView}
                firstDay={1}
                headerToolbar={{
                  left: "prev,next today",
                  center: "title",
                  right: "timeGridWeek,dayGridMonth,dayGridYear,listWeek"
                  }}
                  views={{
                    dayGridMonth: { buttonText: "Monat" },
                    timeGridWeek: {
                      buttonText: "Woche",
                      slotDuration: "02:00:00",
                      slotLabelInterval: "02:00:00",
                      slotLabelFormat: { hour: "2-digit", minute: "2-digit", hour12: false }
                    },
                    dayGridYear: { buttonText: "Jahr" },
                    listWeek: { buttonText: "Übersicht" }
                  }}
                  buttonText={{ today: "Heute" }}
                  events={calendarEvents}
                  dayCellContent={(arg) => {
                    if (arg.view.type !== "dayGridMonth") {
                      return arg.dayNumberText;
                    }
                    return (
                      <div className="flex items-center justify-between">
                        <span>{arg.dayNumberText}</span>
                        {data?.user && (
                          <button
                            type="button"
                            className="rounded-full border border-slate-200 bg-white/80 px-2 py-0.5 text-xs text-slate-600 hover:bg-slate-100"
                            aria-label={`Termin am ${arg.date.toLocaleDateString("de-DE")} anlegen`}
                            onClick={(event) => {
                              event.preventDefault();
                              event.stopPropagation();
                              openFormForDate(arg.date);
                            }}
                          >
                            +
                          </button>
                        )}
                      </div>
                    );
                  }}
                  eventClick={(info) => {
                    const match = events.find((event) => event.id === info.event.id);
                    if (match) {
                      setDetailsEvent(match);
                    }
                  }}
                eventContent={(arg) => {
                  const status = arg.event.extendedProps.status as string;
                  const isSelected = selectedEventIds.has(arg.event.id);
                  return (
                      <div className="event-shell">
                        <div className="min-w-0">
                          <div className="text-sm font-medium truncate">{arg.event.title}</div>
                          <div className="text-xs text-slate-600 truncate">
                            {arg.event.extendedProps.category || "Ohne Kategorie"}
                          </div>
                        </div>
                        {canManageView && (
                          <ViewToggleButton
                            isSelected={isSelected}
                            onClick={(event) => {
                              event.preventDefault();
                              event.stopPropagation();
                              toggleEvent(arg.event.id);
                            }}
                            size="sm"
                          />
                        )}
                      </div>
                    );
                  }}
                  eventDidMount={(info) => {
                    if (!info.el) return;
                    const startLabel = info.event.allDay
                      ? "Ganztägig"
                      : info.event.start
                      ? info.event.start.toLocaleTimeString("de-DE", {
                          hour: "2-digit",
                          minute: "2-digit"
                        })
                      : "";
                    const tooltip = startLabel
                      ? `${info.event.title} - ${startLabel}`
                      : info.event.title;
                    requestAnimationFrame(() =>
                      applyEventTooltip(info.el, tooltip)
                    );
                  }}
                  height="auto"
                  datesSet={(arg) => {
                    if (ALLOWED_CALENDAR_VIEWS.includes(arg.view.type)) {
                      setInitialView(arg.view.type);
                      try {
                        window.localStorage.setItem(
                          CALENDAR_VIEW_STORAGE_KEY,
                          arg.view.type
                        );
                      } catch {
                        // ignore
                      }
                    }
                  }}
                />
              )}
            </div>
          );
        }

        if (section === "map") {
          return (
            <div
              key="map"
              ref={mapRef}
              data-pane="map"
              className={`card drag-card ${
                dragSource === "map" ? "dragging" : ""
              } ${
                dragOver === "map" && dragSource && dragSource !== "map"
                  ? "drag-target"
                  : ""
              } ${dragSource === "calendar" && dragOver === "map" ? "shift-down" : ""} ${
                dragSource === "list" && dragOver === "map" ? "shift-up" : ""
              }`}
              onDragOver={(event) => event.preventDefault()}
              onDragEnter={() => onDragEnter("map")}
              onDrop={(event) => onDrop(event, "map")}
            >
              <div className="mb-3 flex items-center justify-between">
                <h3 className="text-sm font-semibold text-slate-700">Karte</h3>
                <div className="flex items-center gap-2">
                  <button
                    type="button"
                    className="rounded-full border border-slate-200 px-2 py-1 text-xs text-slate-600"
                    onClick={() => toggleCollapse("map")}
                    aria-label={collapsed.map ? "Karte aufklappen" : "Karte zuklappen"}
                    title={collapsed.map ? "Aufklappen" : "Zuklappen"}
                  >
                    {collapsed.map ? <IconChevronDown /> : <IconChevronUp />}
                  </button>
                  <button
                    type="button"
                    className="rounded-full border border-slate-200 px-2 py-1 text-xs text-slate-600"
                    onClick={() => setMapFullscreen(true)}
                  >
                    Vollbild
                  </button>
                  <div
                    className="drag-handle"
                    draggable
                    onDragStart={(event) => onDragStart(event, "map")}
                    onDragEnd={onDragEnd}
                    onPointerDown={(event) => onPointerDragStart(event, "map")}
                    title="Zum Tauschen ziehen"
                    aria-label="Karte verschieben"
                  >
                    <IconGrip />
                  </div>
                </div>
              </div>
              {!collapsed.map && (
                <div className="space-y-3">
                  <div className="flex flex-wrap items-center gap-2 text-xs text-slate-600">
                    <button
                      type="button"
                      onClick={() => {
                        setMapDateFilter(new Set(["past", "today", "tomorrow", "future"]));
                        setMapCategoryFilter(new Set());
                      }}
                      className="inline-flex items-center rounded-full border border-slate-200 bg-white px-2 py-1"
                    >
                      Alles
                    </button>
                    <button
                      type="button"
                      onClick={() => {
                        setMapDateFilter(new Set());
                        setMapCategoryFilter(new Set(mapCategoryOptions));
                      }}
                      className="inline-flex items-center rounded-full border border-slate-200 bg-white px-2 py-1"
                    >
                      Nichts
                    </button>
                    <button
                      type="button"
                      onClick={() => toggleMapDateFilter("today")}
                      className={`inline-flex items-center gap-2 rounded-full border px-2 py-1 ${
                        mapDateFilter.has("today")
                          ? "border-amber-300 bg-amber-50 text-amber-900"
                          : "border-slate-200 bg-white"
                      }`}
                    >
                      <span className="h-2.5 w-2.5 rounded-full border border-amber-500 bg-amber-200"></span>
                      Heute
                    </button>
                    <button
                      type="button"
                      onClick={() => toggleMapDateFilter("tomorrow")}
                      className={`inline-flex items-center gap-2 rounded-full border px-2 py-1 ${
                        mapDateFilter.has("tomorrow")
                          ? "border-emerald-300 bg-emerald-50 text-emerald-900"
                          : "border-slate-200 bg-white"
                      }`}
                    >
                      <span className="h-2.5 w-2.5 rounded-full border border-emerald-500 bg-emerald-200"></span>
                      Morgen
                    </button>
                    <button
                      type="button"
                      onClick={() => toggleMapDateFilter("past")}
                      className={`inline-flex items-center gap-2 rounded-full border px-2 py-1 ${
                        mapDateFilter.has("past")
                          ? "border-slate-300 bg-slate-100 text-slate-700"
                          : "border-slate-200 bg-white"
                      }`}
                    >
                      <span className="h-2.5 w-2.5 rounded-full border border-slate-300 bg-slate-200"></span>
                      Vergangenheit
                    </button>
                    <button
                      type="button"
                      onClick={() => toggleMapDateFilter("future")}
                      className={`inline-flex items-center gap-2 rounded-full border px-2 py-1 ${
                        mapDateFilter.has("future")
                          ? "border-sky-200 bg-sky-50 text-sky-900"
                          : "border-slate-200 bg-white"
                      }`}
                    >
                      <span className="h-2.5 w-2.5 rounded-full border border-slate-300 bg-slate-100"></span>
                      Zukunft
                    </button>
                    {mapCategoryOptions.map((category) => {
                      const active = mapCategoryFilter.has(category);
                      return (
                        <button
                          key={category}
                          type="button"
                          onClick={() => toggleMapCategoryFilter(category)}
                          className={`inline-flex items-center gap-2 rounded-full border px-2 py-1 ${
                            active
                              ? "border-slate-300 bg-slate-100 text-slate-900"
                              : "border-slate-200 bg-white"
                          }`}
                        >
                          <span
                            className="h-2.5 w-2.5 rounded-full"
                            style={{ backgroundColor: getCategoryColor(category) }}
                          ></span>
                          {category}
                        </button>
                      );
                    })}
                  </div>
                  <div
                    className="h-80 overflow-hidden rounded-xl overscroll-contain"
                    onWheel={(event) => event.stopPropagation()}
                  >
                  <MapContainer
                    center={[52.52, 13.405]}
                    zoom={5}
                    scrollWheelZoom
                    className="h-full w-full"
                  >
                    <TileLayer
                      attribution={
                        isDarkTheme
                          ? '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> &copy; <a href="https://carto.com/attributions">CARTO</a>'
                          : '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
                      }
                      url={
                        isDarkTheme
                          ? "https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png"
                          : "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
                      }
                    />
                    <MapAutoBounds points={mapPoints} />
                    {filteredMapEvents.map((event) => {
                      const fillColor = getCategoryColor(event.categoryName);
                      const isPast = event.bucket === "past";
                      const isToday = event.bucket === "today";
                      const isTomorrow = event.bucket === "tomorrow";
                      const strokeColor = isPast
                        ? "#94a3b8"
                        : isToday
                        ? "#f59e0b"
                        : isTomorrow
                        ? "#10b981"
                        : fillColor;
                      return (
                        <CircleMarker
                          key={event.id}
                          center={[event.locationLat as number, event.locationLng as number]}
                          radius={isToday || isTomorrow ? 8 : 6}
                          color={strokeColor}
                          fillColor={fillColor}
                          fillOpacity={isPast ? 0.35 : 0.75}
                          weight={isToday || isTomorrow ? 2 : 1}
                        >
                          <Popup>
                            <div className="space-y-1">
                              <p className="text-sm font-semibold">{event.title}</p>
                              <p className="text-xs text-slate-600">
                                {new Date(event.startAt).toLocaleString("de-DE", {
                                  dateStyle: "medium",
                                  timeStyle: "short"
                                })}
                              </p>
                              <p className="text-xs text-slate-600">
                                {event.categoryName}
                              </p>
                              {event.location && (
                                <p className="text-xs text-slate-600">
                                  {event.location}
                                </p>
                              )}
                            </div>
                          </Popup>
                        </CircleMarker>
                      );
                    })}
                  </MapContainer>
                  </div>
                </div>
              )}
            </div>
          );
        }

        return (
          <div
            key="list"
            ref={listRef}
            data-pane="list"
            className={`card space-y-4 drag-card ${
              dragSource === "list" ? "dragging" : ""
            } ${
              dragOver === "list" && dragSource && dragSource !== "list" ? "drag-target" : ""
            } ${
              dragSource === "calendar" && dragOver === "list" ? "shift-up" : ""
            }`}
            onDragOver={(event) => event.preventDefault()}
            onDragEnter={() => onDragEnter("list")}
            onDrop={(event) => onDrop(event, "list")}
          >
            <div className="flex items-center justify-between">
              <h3 className="text-sm font-semibold text-slate-700">Termine</h3>
              <div className="flex items-center gap-2">
                <button
                  type="button"
                  className="rounded-full border border-slate-200 px-2 py-1 text-xs text-slate-600"
                  onClick={() => toggleCollapse("list")}
                  aria-label={collapsed.list ? "Termine aufklappen" : "Termine zuklappen"}
                  title={collapsed.list ? "Aufklappen" : "Zuklappen"}
                >
                  {collapsed.list ? <IconChevronDown /> : <IconChevronUp />}
                </button>
                <div
                  className="drag-handle"
                  draggable
                  onDragStart={(event) => onDragStart(event, "list")}
                  onDragEnd={onDragEnd}
                  onPointerDown={(event) => onPointerDragStart(event, "list")}
                  title="Zum Tauschen ziehen"
                  aria-label="Termine verschieben"
                >
                  <IconGrip />
                </div>
              </div>
            </div>
            {!collapsed.list && (
              <>
            <div className="flex flex-wrap items-center justify-between gap-3">
              <div className="flex flex-wrap items-center gap-2">
                <input
                  value={query}
                  onChange={(event) => setQuery(event.target.value)}
                  placeholder="Suchen..."
                  className="min-w-[220px] rounded-xl border border-slate-300 px-3 py-2 text-sm"
                />
                <div className="flex flex-wrap items-center gap-2">
                  <button
                    type="button"
                    className={filterButtonClass(listQuickRange === "next7")}
                    onClick={() =>
                      setListQuickRange((current) =>
                        current === "next7" ? null : "next7"
                      )
                    }
                  >
                    7 Tage
                  </button>
                  <button
                    type="button"
                    className={filterButtonClass(listQuickRange === "next30")}
                    onClick={() =>
                      setListQuickRange((current) =>
                        current === "next30" ? null : "next30"
                      )
                    }
                  >
                    30 Tage
                  </button>
                  <button
                    type="button"
                    className={filterButtonClass(listQuickRange === "nextYear")}
                    onClick={() =>
                      setListQuickRange((current) =>
                        current === "nextYear" ? null : "nextYear"
                      )
                    }
                  >
                    1 Jahr
                  </button>
                  <button
                    type="button"
                    className={filterButtonClass(hidePastInList)}
                    onClick={() => setHidePastInList((current) => !current)}
                  >
                    Alte ausblenden
                  </button>
                </div>
              </div>
              {isAdmin && (
                <div className="flex flex-wrap items-center gap-2">
                  <button
                    type="button"
                    className="rounded-full border border-slate-200 px-3 py-1 text-xs text-slate-700"
                    onClick={() => toggleSelectAllVisible(true)}
                    disabled={displayedEvents.length === 0}
                  >
                    Alle markieren
                  </button>
                  <button
                    type="button"
                    className="rounded-full border border-slate-200 px-3 py-1 text-xs text-slate-700"
                    onClick={() => toggleSelectAllVisible(false)}
                    disabled={bulkSelection.size === 0}
                  >
                    Auswahl löschen
                  </button>
                  <span className="text-xs text-slate-500">
                    {bulkSelection.size} ausgewählt
                  </span>
                  <button
                    type="button"
                    className="rounded-full border border-red-200 p-2 text-red-600"
                    onClick={deleteSelectedEvents}
                    disabled={bulkSelection.size === 0}
                    aria-label="Ausgewählte löschen"
                  >
                    <svg
                      viewBox="0 0 24 24"
                      className="h-4 w-4"
                      fill="none"
                      stroke="currentColor"
                      strokeWidth="2"
                    >
                      <path d="M3 6h18" strokeLinecap="round" />
                      <path d="M8 6V4h8v2" strokeLinecap="round" />
                      <path d="M19 6l-1 14H6L5 6" strokeLinecap="round" />
                      <path d="M10 11v6M14 11v6" strokeLinecap="round" />
                    </svg>
                  </button>
                </div>
              )}
            </div>
        <div className="overflow-x-auto">
          <table className="list-table w-full table-fixed text-left text-sm">
            <thead className="text-xs uppercase tracking-[0.2em] text-slate-500">
              <tr>
                {isAdmin && (
                  <th className={`pb-2 pl-2 ${tableWidths.checkbox}`}>
                    <input
                      type="checkbox"
                      aria-label="Alle sichtbaren Termine auswählen"
                      checked={
                        displayedEvents.length > 0 &&
                        displayedEvents.every((event) =>
                          bulkSelection.has(event.id)
                        )
                      }
                      onChange={(event) =>
                        toggleSelectAllVisible(event.target.checked)
                      }
                    />
                  </th>
                )}
                <th className={`pb-2 whitespace-nowrap ${tableWidths.date}`}>
                  <SortButton
                    label="Datum"
                    active={sortKey === "startAt"}
                    direction={sortDir}
                    onClick={() => toggleSort("startAt")}
                  />
                </th>
                <th className={`pb-2 ${tableWidths.title}`}>
                  <SortButton
                    label="Titel"
                    active={sortKey === "title"}
                    direction={sortDir}
                    onClick={() => toggleSort("title")}
                  />
                </th>
                <th className={`pb-2 ${tableWidths.category}`}>
                  <div className="flex flex-wrap items-center gap-2">
                    <SortButton
                      label="Kategorie"
                      active={sortKey === "category"}
                      direction={sortDir}
                      onClick={() => toggleSort("category")}
                    />
                    <select
                      className="max-w-full rounded-full border border-slate-200 bg-white px-2 py-0.5 text-xs text-slate-600"
                      value={categoryFilter}
                      onChange={(event) => setCategoryFilter(event.target.value)}
                      aria-label="Kategorie filtern"
                    >
                      <option value="ALL">Alle</option>
                      {categoryOptions.map((category) => (
                        <option key={category} value={category}>
                          {category}
                        </option>
                      ))}
                    </select>
                  </div>
                </th>
                <th className={`pb-2 ${tableWidths.location}`}>
                  <SortButton
                    label="Ort"
                    active={sortKey === "location"}
                    direction={sortDir}
                    onClick={() => toggleSort("location")}
                  />
                </th>
                <th className={`pb-2 whitespace-nowrap ${tableWidths.actions}`}>
                  Aktionen
                </th>
              </tr>
            </thead>
            <tbody>
              {displayedEvents.length === 0 ? (
                <tr>
                  <td colSpan={isAdmin ? 6 : 5} className="py-4 text-slate-600">
                    Keine Termine für die aktuelle Auswahl.
                  </td>
                </tr>
              ) : (
                displayedEvents.map((event) => {
                  const categoryName = event.category?.name || "Ohne Kategorie";
                  const locationLabel = formatLocation(event.location);
                  const dateLabel = new Date(event.startAt).toLocaleString("de-DE", {
                    dateStyle: "medium",
                    timeStyle: "short"
                  });
                  return (
                    <tr
                      key={event.id}
                      data-bucket={getDateBucket(event.startAt)}
                      className={`border-t border-slate-200 ${
                        getDateBucket(event.startAt) === "past"
                          ? "bg-slate-50 text-slate-500"
                          : getDateBucket(event.startAt) === "today"
                          ? "bg-amber-50/60"
                          : getDateBucket(event.startAt) === "tomorrow"
                          ? "bg-emerald-50/60"
                          : "bg-sky-50/40"
                      }`}
                    >
                      {isAdmin && (
                        <td className="py-2 pr-3 pl-2">
                          <input
                            type="checkbox"
                            aria-label={`${event.title} auswählen`}
                            checked={bulkSelection.has(event.id)}
                            onChange={() => toggleBulkSelection(event.id)}
                          />
                        </td>
                      )}
                      <td className="py-2 pr-3 pl-2">
                        <span className="block w-full truncate whitespace-nowrap" title={dateLabel}>
                          {dateLabel}
                        </span>
                      </td>
                      <td className="py-2 pr-3 font-medium">
                        <span
                          className="block w-full truncate"
                          title={event.title}
                        >
                          {event.title}
                        </span>
                      </td>
                      <td className="py-2 pr-3">
                        <span
                          className="block w-full truncate"
                          title={categoryName}
                        >
                          {categoryName}
                        </span>
                      </td>
                      <td className="py-2 pr-3">
                        <div className="flex items-center justify-between gap-2">
                          <span
                            className="min-w-0 flex-1 truncate"
                            title={locationLabel}
                          >
                            {locationLabel}
                          </span>
                          {event.locationLat && event.locationLng && (
                            <a
                              className="inline-flex items-center justify-center rounded-full border border-slate-200 p-1 text-slate-600 hover:bg-slate-100"
                              href={`https://maps.google.com/?q=${event.locationLat},${event.locationLng}&z=14`}
                              target="_blank"
                              rel="noreferrer"
                              title="Google Maps"
                              aria-label="Google Maps"
                            >
                              <IconMapPin />
                            </a>
                          )}
                        </div>
                      </td>
                      <td className="py-2 pr-3 whitespace-nowrap">
                        <div className="flex flex-nowrap justify-end gap-1">
                          {canManageView && (
                            <ViewToggleButton
                              isSelected={selectedEventIds.has(event.id)}
                              onClick={() => toggleEvent(event.id)}
                              size="sm"
                            />
                          )}
                        {isAdmin && (
                          <button
                            type="button"
                            className="rounded-full border border-slate-200 p-2 text-slate-600"
                            onClick={() => {
                              setEditStatus(null);
                              setEditError(null);
                              setEditEvent(event);
                              setIsEditOpen(true);
                            }}
                            aria-label="Termin bearbeiten"
                          >
                            <svg
                              viewBox="0 0 24 24"
                              className="h-4 w-4"
                              fill="none"
                              stroke="currentColor"
                              strokeWidth="2"
                            >
                              <path
                                d="M4 20h4l10-10-4-4L4 16v4z"
                                strokeLinecap="round"
                                strokeLinejoin="round"
                              />
                              <path
                                d="M13 7l4 4"
                                strokeLinecap="round"
                                strokeLinejoin="round"
                              />
                            </svg>
                          </button>
                        )}
                        <button
                          type="button"
                          className="rounded-full border border-slate-200 p-2 text-slate-600"
                          onClick={() => setDetailsEvent(event)}
                          aria-label="Termin Details"
                        >
                          <svg
                            viewBox="0 0 24 24"
                            className="h-4 w-4"
                            fill="none"
                            stroke="currentColor"
                            strokeWidth="2"
                          >
                            <circle cx="12" cy="12" r="9" />
                            <path d="M12 8h.01M12 12v4" strokeLinecap="round" />
                          </svg>
                        </button>
                      </div>
                    </td>
                    </tr>
                  );
                })
              )}
            </tbody>
          </table>
        </div>
              </>
            )}
          </div>
        );
      })}
      {detailsEvent && portalRoot
        ? createPortal(
            <div
              className="fixed inset-0 z-30 flex items-center justify-center bg-black/40 px-4 py-6"
              onClick={() => setDetailsEvent(null)}
            >
              <div
                className="card w-full max-w-2xl max-h-[90vh] overflow-y-auto"
                ref={detailsModalRef}
                role="dialog"
                aria-modal="true"
                tabIndex={-1}
                onClick={(event) => event.stopPropagation()}
                onKeyDown={(event) => {
                  if (event.key !== "Tab") return;
                  const node = detailsModalRef.current;
                  if (!node) return;
                  const focusable = node.querySelectorAll<HTMLElement>(
                    'button,[href],input,select,textarea,[tabindex]:not([tabindex="-1"])'
                  );
                  if (focusable.length === 0) {
                    event.preventDefault();
                    return;
                  }
                  const first = focusable[0];
                  const last = focusable[focusable.length - 1];
                  if (event.shiftKey && document.activeElement === first) {
                    event.preventDefault();
                    last.focus();
                  } else if (!event.shiftKey && document.activeElement === last) {
                    event.preventDefault();
                    first.focus();
                  }
                }}
              >
                  <div className="flex items-center justify-between">
                  <h3 className="text-lg font-semibold">Termin Details</h3>
                  <div className="flex items-center gap-2">
                    {isAdmin && (
                      <button
                        type="button"
                        className="rounded-full border border-slate-200 px-3 py-1 text-xs text-slate-700"
                        onClick={() => {
                          setEditStatus(null);
                          setEditError(null);
                          setEditEvent(detailsEvent);
                          setIsEditOpen(true);
                          setDetailsEvent(null);
                        }}
                      >
                        Bearbeiten
                      </button>
                    )}
                    <button
                      type="button"
                      className="text-sm text-slate-600"
                      onClick={() => setDetailsEvent(null)}
                    >
                      Schließen
                    </button>
                  </div>
                </div>
                <div className="mt-4 grid gap-4 md:grid-cols-2">
                  <div className="space-y-2">
                    <div>
                      <p className="text-xs uppercase tracking-[0.2em] text-slate-500">
                        Titel
                      </p>
                      <p className="text-sm font-semibold">{detailsEvent.title}</p>
                    </div>
                    <div>
                      <p className="text-xs uppercase tracking-[0.2em] text-slate-500">
                        Zeitpunkt
                      </p>
                      <p className="text-sm">
                        {new Date(detailsEvent.startAt).toLocaleString("de-DE", {
                          dateStyle: "full",
                          timeStyle: "short"
                        })}
                      </p>
                    </div>
                    <div>
                      <p className="text-xs uppercase tracking-[0.2em] text-slate-500">
                        Ort
                      </p>
                      <p className="text-sm">{detailsEvent.location || "-"}</p>
                    </div>
                    <div>
                      <p className="text-xs uppercase tracking-[0.2em] text-slate-500">
                        Kategorie
                      </p>
                      <p className="text-sm">
                        {detailsEvent.category?.name || "Ohne Kategorie"}
                      </p>
                    </div>
                    {detailsEvent.description && (
                      <div>
                        <p className="text-xs uppercase tracking-[0.2em] text-slate-500">
                          Beschreibung
                        </p>
                        <p className="text-sm text-slate-700">
                          {renderLinkedText(detailsEvent.description)}
                        </p>
                      </div>
                    )}
                    {detailsEvent.locationLat && detailsEvent.locationLng && (
                      <div className="flex flex-wrap gap-2">
                        <a
                          className="btn-ghost"
                          href={`https://maps.google.com/?q=${detailsEvent.locationLat},${detailsEvent.locationLng}&z=14`}
                          target="_blank"
                          rel="noreferrer"
                        >
                          Google Maps
                        </a>
                      </div>
                    )}
                  </div>
                  <div className="min-h-[240px] overflow-hidden rounded-xl border border-slate-200 bg-slate-100">
                    {placesProvider === "google" &&
                    placesKey &&
                    (detailsEvent.locationPlaceId ||
                      (detailsEvent.locationLat && detailsEvent.locationLng)) ? (
                      <iframe
                        title="Karte"
                        className="h-[320px] w-full"
                        loading="lazy"
                        referrerPolicy="no-referrer-when-downgrade"
                        src={
                          detailsEvent.locationPlaceId
                            ? `https://www.google.com/maps/embed/v1/place?key=${placesKey}&q=place_id:${detailsEvent.locationPlaceId}&zoom=14`
                            : `https://www.google.com/maps/embed/v1/place?key=${placesKey}&q=${detailsEvent.locationLat},${detailsEvent.locationLng}&zoom=14`
                        }
                      />
                    ) : placesProvider === "osm" &&
                      detailsEvent.locationLat &&
                      detailsEvent.locationLng ? (
                      <iframe
                        title="Karte"
                        className="h-[320px] w-full"
                        loading="lazy"
                        src={`https://www.openstreetmap.org/export/embed.html?bbox=${
                          detailsEvent.locationLng - 0.0045
                        },${detailsEvent.locationLat - 0.0045},${
                          detailsEvent.locationLng + 0.0045
                        },${detailsEvent.locationLat + 0.0045}&layer=mapnik&marker=${detailsEvent.locationLat},${detailsEvent.locationLng}`}
                      />
                    ) : (
                      <div className="flex h-full items-center justify-center text-sm text-slate-500">
                        Keine Karte verfügbar.
                      </div>
                    )}
                  </div>
                </div>
              </div>
            </div>,
            portalRoot
          )
        : null}
      {isEditOpen && editEvent && portalRoot
        ? createPortal(
            <div
              className="fixed inset-0 z-30 flex items-center justify-center bg-black/40 px-4 py-6"
              onClick={() => {
                setIsEditOpen(false);
                setEditEvent(null);
              }}
            >
              <div
                className="card w-full max-w-2xl max-h-[90vh] overflow-y-auto"
                onClick={(event) => event.stopPropagation()}
              >
                <div className="flex items-center justify-between">
                  <h3 className="text-lg font-semibold">Termin bearbeiten</h3>
                  <button
                    type="button"
                    className="text-sm text-slate-600"
                    onClick={() => {
                      setIsEditOpen(false);
                      setEditEvent(null);
                    }}
                  >
                    Schließen
                  </button>
                </div>
                <form onSubmit={updateEvent} className="mt-4 space-y-3">
                  <div className="grid gap-3 md:grid-cols-2">
                    <input
                      name="title"
                      required
                      defaultValue={editEvent.title}
                      className="rounded-xl border border-slate-300 px-3 py-2"
                      placeholder="Titel"
                    />
                    <input
                      name="location"
                      defaultValue={editEvent.location || ""}
                      className="rounded-xl border border-slate-300 px-3 py-2"
                      placeholder="Ort"
                    />
                    <input
                      type="hidden"
                      name="locationPlaceId"
                      value={editEvent.locationPlaceId || ""}
                    />
                    <input
                      type="hidden"
                      name="locationLat"
                      value={editEvent.locationLat ?? ""}
                    />
                    <input
                      type="hidden"
                      name="locationLng"
                      value={editEvent.locationLng ?? ""}
                    />
                    <input
                      name="startAt"
                      type="datetime-local"
                      required
                      defaultValue={formatLocalDateTime(editEvent.startAt)}
                      className="rounded-xl border border-slate-300 px-3 py-2"
                    />
                    <input
                      name="endAt"
                      type="datetime-local"
                      defaultValue={formatLocalDateTime(editEvent.endAt)}
                      className="rounded-xl border border-slate-300 px-3 py-2"
                    />
                    <select
                      name="categoryId"
                      required
                      defaultValue={editEvent.category?.id || ""}
                      className="rounded-xl border border-slate-300 px-3 py-2 md:col-span-2"
                    >
                      <option value="" disabled>
                        Kategorie wählen
                      </option>
                      {categories.map((category) => (
                        <option key={category.id} value={category.id}>
                          {category.name}
                        </option>
                      ))}
                    </select>
                    {showPublicControls && (
                      <div className="rounded-xl border border-slate-200 bg-slate-50/60 p-3 text-sm text-slate-700 md:col-span-2">
                        <p className="text-xs uppercase tracking-[0.2em] text-slate-500">
                          Öffentlich
                        </p>
                        <div className="mt-2 flex flex-wrap gap-3">
                          <label className="flex items-center gap-2">
                            <input
                              type="radio"
                              name="publicOverride"
                              value="inherit"
                              defaultChecked={
                                editEvent.publicOverride !== true &&
                                editEvent.publicOverride !== false
                              }
                            />
                            Kategorie übernehmen
                          </label>
                          <label className="flex items-center gap-2">
                            <input
                              type="radio"
                              name="publicOverride"
                              value="public"
                              defaultChecked={editEvent.publicOverride === true}
                            />
                            Öffentlich
                          </label>
                          <label className="flex items-center gap-2">
                            <input
                              type="radio"
                              name="publicOverride"
                              value="private"
                              defaultChecked={editEvent.publicOverride === false}
                            />
                            Nicht öffentlich
                          </label>
                        </div>
                      </div>
                    )}
                  </div>
                  <textarea
                    name="description"
                    defaultValue={editEvent.description || ""}
                    placeholder="Beschreibung"
                    className="min-h-[96px] rounded-xl border border-slate-300 px-3 py-2"
                  />
                  <button type="submit" className="btn-accent">
                    Speichern
                  </button>
                  <button
                    type="button"
                    className="rounded-full border border-red-200 px-3 py-1 text-xs text-red-600"
                    onClick={() => deleteEvent(editEvent.id)}
                  >
                    Löschen
                  </button>
                  {editStatus && (
                    <p className="text-sm text-emerald-600">{editStatus}</p>
                  )}
                  {editError && (
                    <p className="text-sm text-red-600">{editError}</p>
                  )}
                </form>
              </div>
            </div>,
            portalRoot
          )
        : null}
      {mapFullscreen && portalRoot
        ? createPortal(
            <div
              className="fixed inset-0 z-40 flex flex-col bg-slate-950/40 p-4"
              onClick={() => setMapFullscreen(false)}
            >
              <div
                className="card flex h-full w-full flex-col"
                onClick={(event) => event.stopPropagation()}
              >
                <div className="flex items-center justify-between">
                  <h3 className="text-lg font-semibold">Karte</h3>
                  <button
                    type="button"
                    className="text-sm text-slate-600"
                    onClick={() => setMapFullscreen(false)}
                  >
                    Schließen
                  </button>
                </div>
                <div className="mt-3 flex flex-wrap items-center gap-2 text-xs text-slate-600">
                  <button
                    type="button"
                    onClick={() => {
                      setMapDateFilter(new Set(["past", "today", "tomorrow", "future"]));
                      setMapCategoryFilter(new Set());
                    }}
                    className="inline-flex items-center rounded-full border border-slate-200 bg-white px-2 py-1"
                  >
                    Alles
                  </button>
                  <button
                    type="button"
                    onClick={() => {
                      setMapDateFilter(new Set());
                      setMapCategoryFilter(new Set(mapCategoryOptions));
                    }}
                    className="inline-flex items-center rounded-full border border-slate-200 bg-white px-2 py-1"
                  >
                    Nichts
                  </button>
                  <button
                    type="button"
                    onClick={() => toggleMapDateFilter("today")}
                    className={`inline-flex items-center gap-2 rounded-full border px-2 py-1 ${
                      mapDateFilter.has("today")
                        ? "border-amber-300 bg-amber-50 text-amber-900"
                        : "border-slate-200 bg-white"
                    }`}
                  >
                    <span className="h-2.5 w-2.5 rounded-full border border-amber-500 bg-amber-200"></span>
                    Heute
                  </button>
                  <button
                    type="button"
                    onClick={() => toggleMapDateFilter("tomorrow")}
                    className={`inline-flex items-center gap-2 rounded-full border px-2 py-1 ${
                      mapDateFilter.has("tomorrow")
                        ? "border-emerald-300 bg-emerald-50 text-emerald-900"
                        : "border-slate-200 bg-white"
                    }`}
                  >
                    <span className="h-2.5 w-2.5 rounded-full border border-emerald-500 bg-emerald-200"></span>
                    Morgen
                  </button>
                  <button
                    type="button"
                    onClick={() => toggleMapDateFilter("past")}
                    className={`inline-flex items-center gap-2 rounded-full border px-2 py-1 ${
                      mapDateFilter.has("past")
                        ? "border-slate-300 bg-slate-100 text-slate-700"
                        : "border-slate-200 bg-white"
                    }`}
                  >
                    <span className="h-2.5 w-2.5 rounded-full border border-slate-300 bg-slate-200"></span>
                    Vergangenheit
                  </button>
                  <button
                    type="button"
                    onClick={() => toggleMapDateFilter("future")}
                    className={`inline-flex items-center gap-2 rounded-full border px-2 py-1 ${
                      mapDateFilter.has("future")
                        ? "border-sky-200 bg-sky-50 text-sky-900"
                        : "border-slate-200 bg-white"
                    }`}
                  >
                    <span className="h-2.5 w-2.5 rounded-full border border-slate-300 bg-slate-100"></span>
                    Zukunft
                  </button>
                  {mapCategoryOptions.map((category) => {
                    const active = mapCategoryFilter.has(category);
                    return (
                      <button
                        key={category}
                        type="button"
                        onClick={() => toggleMapCategoryFilter(category)}
                        className={`inline-flex items-center gap-2 rounded-full border px-2 py-1 ${
                          active
                            ? "border-slate-300 bg-slate-100 text-slate-900"
                            : "border-slate-200 bg-white"
                        }`}
                      >
                        <span
                          className="h-2.5 w-2.5 rounded-full"
                          style={{ backgroundColor: getCategoryColor(category) }}
                        ></span>
                        {category}
                      </button>
                    );
                  })}
                </div>
                <div
                  className="mt-4 flex-1 overflow-hidden rounded-xl overscroll-contain"
                  onWheel={(event) => event.stopPropagation()}
                >
                  <MapContainer
                    center={[52.52, 13.405]}
                    zoom={5}
                    scrollWheelZoom
                    className="h-full w-full"
                  >
                    <TileLayer
                      attribution={
                        isDarkTheme
                          ? '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> &copy; <a href="https://carto.com/attributions">CARTO</a>'
                          : '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
                      }
                      url={
                        isDarkTheme
                          ? "https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png"
                          : "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
                      }
                    />
                    <MapAutoBounds points={mapPoints} />
                    {filteredMapEvents.map((event) => {
                      const fillColor = getCategoryColor(event.categoryName);
                      const isPast = event.bucket === "past";
                      const isToday = event.bucket === "today";
                      const isTomorrow = event.bucket === "tomorrow";
                      const strokeColor = isPast
                        ? "#94a3b8"
                        : isToday
                        ? "#f59e0b"
                        : isTomorrow
                        ? "#10b981"
                        : fillColor;
                      return (
                        <CircleMarker
                          key={event.id}
                          center={[event.locationLat as number, event.locationLng as number]}
                          radius={isToday || isTomorrow ? 8 : 6}
                          color={strokeColor}
                          fillColor={fillColor}
                          fillOpacity={isPast ? 0.35 : 0.75}
                          weight={isToday || isTomorrow ? 2 : 1}
                        >
                          <Popup>
                            <div className="space-y-1">
                              <p className="text-sm font-semibold">{event.title}</p>
                              <p className="text-xs text-slate-600">
                                {new Date(event.startAt).toLocaleString("de-DE", {
                                  dateStyle: "medium",
                                  timeStyle: "short"
                                })}
                              </p>
                              <p className="text-xs text-slate-600">
                                {event.categoryName}
                              </p>
                              {event.location && (
                                <p className="text-xs text-slate-600">
                                  {event.location}
                                </p>
                              )}
                            </div>
                          </Popup>
                        </CircleMarker>
                      );
                    })}
                  </MapContainer>
                </div>
              </div>
            </div>,
            portalRoot
          )
        : null}
    </section>
  );
}

function formatLocation(value?: string | null) {
  if (!value) return "-";
  const parts = value.split(",").map((part) => part.trim()).filter(Boolean);
  if (parts.length === 0) return value;
  const postal = parts.find((part) => /\b\d{5}\b/.test(part));
  const street = parts[0] || "";
  const city = parts[1] || "";
  if (postal && city) {
    return street ? `${street}, ${postal} ${city}` : `${postal} ${city}`;
  }
  if (postal) {
    return street ? `${street}, ${postal}` : postal;
  }
  const main = parts.slice(0, 2).join(", ");
  return main || value;
}

function renderLinkedText(value: string) {
  const regex = /\b(https?:\/\/[^\s<]+|www\.[^\s<]+)/gi;
  const parts: Array<string | JSX.Element> = [];
  let lastIndex = 0;
  let match: RegExpExecArray | null;

  while ((match = regex.exec(value)) !== null) {
    if (match.index > lastIndex) {
      parts.push(value.slice(lastIndex, match.index));
    }
    let rawUrl = match[0];
    let trailing = "";
    while (/[),.!?]$/.test(rawUrl)) {
      trailing = rawUrl.slice(-1) + trailing;
      rawUrl = rawUrl.slice(0, -1);
    }
    const href = rawUrl.startsWith("http") ? rawUrl : `https://${rawUrl}`;
    parts.push(
      <a
        key={`link-${match.index}`}
        href={href}
        target="_blank"
        rel="noreferrer"
        className="text-emerald-700 underline decoration-emerald-300 underline-offset-2"
      >
        {rawUrl}
      </a>
    );
    if (trailing) {
      parts.push(trailing);
    }
    lastIndex = match.index + match[0].length;
  }

  if (lastIndex < value.length) {
    parts.push(value.slice(lastIndex));
  }

  return parts.length > 0 ? parts : value;
}

function MapAutoBounds({ points }: { points: Array<{ lat: number; lng: number }> }) {
  const map = useMap();
  useEffect(() => {
    if (points.length === 0) return;
    const bounds = points.map((point) => [point.lat, point.lng]) as [
      number,
      number
    ][];
    map.fitBounds(bounds, { padding: [24, 24] });
  }, [map, points]);
  return null;
}

function StatusIcon({ status }: { status: string }) {
  if (status === "APPROVED") {
    return (
      <span title="Freigegeben" aria-label="Freigegeben" className="text-emerald-600">
        <svg viewBox="0 0 24 24" className="h-4 w-4" fill="none" stroke="currentColor" strokeWidth="2">
          <path d="M5 12l4 4L19 6" strokeLinecap="round" strokeLinejoin="round" />
        </svg>
      </span>
    );
  }
  if (status === "REJECTED") {
    return (
      <span title="Abgelehnt" aria-label="Abgelehnt" className="text-red-600">
        <svg viewBox="0 0 24 24" className="h-4 w-4" fill="none" stroke="currentColor" strokeWidth="2">
          <path d="M6 6l12 12M18 6l-12 12" strokeLinecap="round" />
        </svg>
      </span>
    );
  }
  return (
    <span title="Offen" aria-label="Offen" className="text-amber-600">
      <svg viewBox="0 0 24 24" className="h-4 w-4" fill="none" stroke="currentColor" strokeWidth="2">
        <path d="M12 6v6l4 2" strokeLinecap="round" strokeLinejoin="round" />
        <circle cx="12" cy="12" r="9" />
      </svg>
    </span>
  );
}

function ViewToggleButton({
  isSelected,
  onClick,
  size = "md"
}: {
  isSelected: boolean;
  onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
  size?: "sm" | "md";
}) {
  const base =
    size === "sm"
      ? "event-toggle rounded-full bg-white/80 px-1.5 py-0.5 text-[9px] text-slate-700"
      : "rounded-full border border-slate-200 px-3 py-1 text-xs text-slate-700";
  const iconClass = size === "sm" ? "h-[11px] w-[11px]" : "h-4 w-4";
  const label = isSelected
    ? "Vom Kalenderfeed entfernen"
    : "Zum Kalenderfeed hinzufügen";
  return (
    <button
      type="button"
      className={base}
      onClick={onClick}
      aria-label={label}
      title={label}
    >
      {isSelected ? <IconBell className={iconClass} /> : <IconSleep className={iconClass} />}
    </button>
  );
}

function IconBell({ className = "h-4 w-4" }: { className?: string }) {
  return (
    <svg viewBox="0 0 24 24" className={className} fill="none" stroke="currentColor" strokeWidth="2">
      <path d="M6 8a6 6 0 1112 0c0 7 2 7 2 7H4s2 0 2-7" strokeLinecap="round" strokeLinejoin="round" />
      <path d="M10 19a2 2 0 004 0" strokeLinecap="round" />
    </svg>
  );
}

function IconSleep({ className = "h-4 w-4" }: { className?: string }) {
  return (
    <svg viewBox="0 0 24 24" className={className} fill="none" stroke="currentColor" strokeWidth="2">
      <path d="M3 5h6l-6 6h6" strokeLinecap="round" strokeLinejoin="round" />
      <path d="M9 9h6l-6 6h6" strokeLinecap="round" strokeLinejoin="round" />
      <path d="M15 13h6l-6 6h6" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

function IconGrip() {
  return (
    <svg viewBox="0 0 24 24" className="h-4 w-4" fill="none" stroke="currentColor" strokeWidth="2">
      <circle cx="8" cy="6" r="1" />
      <circle cx="16" cy="6" r="1" />
      <circle cx="8" cy="12" r="1" />
      <circle cx="16" cy="12" r="1" />
      <circle cx="8" cy="18" r="1" />
      <circle cx="16" cy="18" r="1" />
    </svg>
  );
}

function IconChevronDown() {
  return (
    <svg viewBox="0 0 24 24" className="h-4 w-4" fill="none" stroke="currentColor" strokeWidth="2">
      <path d="M6 9l6 6 6-6" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

function IconChevronUp() {
  return (
    <svg viewBox="0 0 24 24" className="h-4 w-4" fill="none" stroke="currentColor" strokeWidth="2">
      <path d="M6 15l6-6 6 6" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

function SortButton({
  label,
  active,
  direction,
  onClick
}: {
  label: string;
  active: boolean;
  direction: "asc" | "desc";
  onClick: () => void;
}) {
  return (
    <button
      type="button"
      onClick={onClick}
      className={`inline-flex items-center gap-1 text-xs uppercase tracking-[0.2em] ${
        active ? "text-slate-900" : "text-slate-500"
      }`}
      title="Sortieren"
    >
      {label}
      <SortIcon direction={direction} active={active} />
    </button>
  );
}

function SortIcon({
  direction,
  active
}: {
  direction: "asc" | "desc";
  active: boolean;
}) {
  return (
    <svg
      viewBox="0 0 24 24"
      className={`h-3 w-3 ${active ? "opacity-100" : "opacity-40"}`}
      fill="none"
      stroke="currentColor"
      strokeWidth="2"
    >
      {direction === "asc" ? (
        <path d="M6 15l6-6 6 6" strokeLinecap="round" strokeLinejoin="round" />
      ) : (
        <path d="M6 9l6 6 6-6" strokeLinecap="round" strokeLinejoin="round" />
      )}
    </svg>
  );
}

function IconMapPin() {
  return (
    <svg viewBox="0 0 24 24" className="h-4 w-4" fill="none" stroke="currentColor" strokeWidth="2">
      <path d="M12 21s6-6.5 6-11a6 6 0 10-12 0c0 4.5 6 11 6 11z" strokeLinecap="round" strokeLinejoin="round" />
      <circle cx="12" cy="10" r="2.5" />
    </svg>
  );
}
