"use client";

import { useMemo, useState, useTransition } from "react";
import { useRouter } from "next/navigation";
import {
  Check,
  ChevronLeft,
  ChevronRight,
  Clock3,
  Info,
  Loader2,
  LockKeyhole,
  X,
} from "lucide-react";
import {
  eachDayOfInterval,
  endOfMonth,
  endOfWeek,
  format,
  isSameMonth,
  startOfDay,
  startOfMonth,
  startOfWeek,
} from "date-fns";
import { de } from "date-fns/locale";
import { toast } from "sonner";

import { cn } from "@/lib/utils";
import { formatDateKey, getWorkingDaysOfMonth } from "@/lib/date-utils";
import { saveNotdienstAvailabilities } from "./actions";

export const notdienstEventCategories = {
  closure: {
    label: "Schließtag",
    dot: "oklch(0.62 0.10 35)",
    bg: "oklch(0.96 0.04 35)",
    border: "oklch(0.88 0.04 35)",
    text: "oklch(0.38 0.08 35)",
  },
  team: {
    label: "Teamtag",
    dot: "oklch(0.62 0.10 70)",
    bg: "oklch(0.96 0.04 70)",
    border: "oklch(0.88 0.04 70)",
    text: "oklch(0.38 0.08 70)",
  },
  parents: {
    label: "Elternabend",
    dot: "oklch(0.62 0.10 280)",
    bg: "oklch(0.96 0.04 280)",
    border: "oklch(0.88 0.04 280)",
    text: "oklch(0.38 0.08 280)",
  },
  social: {
    label: "Fest",
    dot: "oklch(0.62 0.10 155)",
    bg: "oklch(0.96 0.04 155)",
    border: "oklch(0.88 0.04 155)",
    text: "oklch(0.38 0.08 155)",
  },
  birthday: {
    label: "Geburtstag",
    dot: "oklch(0.62 0.10 320)",
    bg: "oklch(0.96 0.04 320)",
    border: "oklch(0.88 0.04 320)",
    text: "oklch(0.38 0.08 320)",
  },
} as const;

type NotdienstEventCategory = keyof typeof notdienstEventCategories;

export type NotdienstCalendarItem = {
  id: string;
  title: string;
  category: NotdienstEventCategory;
  startDate: Date;
  endDate: Date;
  allDay: boolean;
  note: string | null;
};

interface NotdienstFormProps {
  targetYear: number;
  targetMonth: number;
  isLocked: boolean;
  requiredDaysTotal: number;
  initialSelectedDates: string[];
  childrenIds: string[];
  calendarItems: NotdienstCalendarItem[];
}

export function NotdienstForm({
  targetYear,
  targetMonth,
  isLocked,
  requiredDaysTotal,
  initialSelectedDates,
  childrenIds,
  calendarItems,
}: NotdienstFormProps) {
  const router = useRouter();
  // Lokaler State für die Auswahl. Ein Datum liegt als stabiler yyyy-MM-dd-Schlüssel vor.
  const [selectedDates, setSelectedDates] = useState<Set<string>>(
    new Set(initialSelectedDates),
  );
  const [isPending, startTransition] = useTransition();

  const closureDateKeys = useMemo(
    () => getClosureDateKeys(calendarItems),
    [calendarItems],
  );
  const selectableDateKeys = useMemo(() => {
    const workingDays = getWorkingDaysOfMonth(targetYear, targetMonth);
    return new Set(
      workingDays
        .map(formatDateKey)
        .filter((dateKey) => !closureDateKeys.has(dateKey)),
    );
  }, [closureDateKeys, targetMonth, targetYear]);
  const monthStart = startOfMonth(new Date(targetYear, targetMonth - 1));
  const selectedCount = selectedDates.size;
  const hasMinimumRequirement = requiredDaysTotal > 0;
  const isFulfilled =
    !hasMinimumRequirement || selectedCount >= requiredDaysTotal;
  const selectedLabel =
    selectedCount === 1 ? "1 Tag markiert" : `${selectedCount} Tage markiert`;

  function handleChange(nextDates: Set<string>) {
    if (isLocked || isPending) return;
    setSelectedDates(nextDates);
  }

  function handleSave() {
    if (isLocked) return;

    if (hasMinimumRequirement && !isFulfilled) {
      toast.error(
        `Bitte wähle noch ${requiredDaysTotal - selectedCount} weitere Termine aus.`,
      );
      return;
    }

    startTransition(async () => {
      const payload = {
        dates: Array.from(selectedDates),
        childrenIds,
      };

      const result = await saveNotdienstAvailabilities(payload);

      if (result?.error) {
        toast.error(result.error);
      } else {
        setSelectedDates(new Set(result.dates));
        toast.success("Verfügbarkeiten erfolgreich gespeichert.");
        router.refresh();
      }
    });
  }

  return (
    <div>
      {isLocked ? (
        <div className="mb-4 flex items-start gap-3 rounded-[10px] border border-[oklch(0.88_0.04_35)] bg-[oklch(0.96_0.018_35)] p-4 text-sm text-[oklch(0.38_0.08_35)]">
          <Info className="mt-0.5 h-4 w-4 shrink-0" />
          <p>
            Die Frist für diesen Monat ist abgelaufen. Änderungen sind nicht
            mehr möglich.
          </p>
        </div>
      ) : null}

      <div className="overflow-x-auto">
        <NotdienstCalendar
          month={monthStart}
          events={calendarItems}
          selected={selectedDates}
          requiredDays={requiredDaysTotal}
          disabled={isLocked || isPending}
          selectableDateKeys={selectableDateKeys}
          onChange={handleChange}
          onSave={handleSave}
          isSaving={isPending}
        />
      </div>
      <p className="sr-only" aria-live="polite">
        {hasMinimumRequirement
          ? `Du hast ${selectedCount} von ${requiredDaysTotal} benötigten Tagen markiert.`
          : `${selectedLabel}. Für diese Kita ist aktuell keine Mindestanzahl hinterlegt.`}
      </p>
    </div>
  );
}

type NotdienstCalendarProps = {
  month: Date;
  events: NotdienstCalendarItem[];
  selected: Set<string>;
  requiredDays: number;
  disabled?: boolean;
  selectableDateKeys: Set<string>;
  isSaving?: boolean;
  onChange: (selected: Set<string>) => void;
  onSave: () => void;
};

export function NotdienstCalendar({
  month,
  events,
  selected,
  requiredDays,
  disabled = false,
  selectableDateKeys,
  isSaving = false,
  onChange,
  onSave,
}: NotdienstCalendarProps) {
  const [visibleMonth, setVisibleMonth] = useState(month);
  const [activeEvent, setActiveEvent] = useState<NotdienstCalendarItem | null>(
    null,
  );
  const visibleMonthStart = startOfMonth(visibleMonth);
  const visibleMonthEnd = endOfMonth(visibleMonthStart);
  const calendarStart = startOfWeek(visibleMonthStart, { weekStartsOn: 1 });
  const calendarEnd = endOfWeek(visibleMonthEnd, { weekStartsOn: 1 });
  const calendarDays = eachDayOfInterval({
    start: calendarStart,
    end: calendarEnd,
  }).map((day) => startOfDay(day));
  const selectedCount = selected.size;
  const extraCount = Math.max(0, selectedCount - requiredDays);
  const statusText =
    requiredDays > 0 && selectedCount >= requiredDays
      ? `Fertig — alle ${requiredDays} Tage markiert`
      : `${selectedCount} von ${requiredDays} Tagen`;

  function toggleDate(dateKey: string) {
    if (disabled || !selectableDateKeys.has(dateKey)) return;

    const nextSelected = new Set(selected);
    if (nextSelected.has(dateKey)) {
      nextSelected.delete(dateKey);
    } else {
      nextSelected.add(dateKey);
    }
    onChange(nextSelected);
  }

  return (
    <section
      className="min-w-[1040px] bg-[var(--notdienst-bg)] px-11 py-10 text-[var(--notdienst-ink)] shadow-sm [--notdienst-accent-deep:oklch(0.42_0.10_38)] [--notdienst-accent-mid:oklch(0.82_0.07_38)] [--notdienst-accent-soft:oklch(0.93_0.04_38)] [--notdienst-accent:oklch(0.58_0.11_38)] [--notdienst-bg:oklch(0.965_0.012_75)] [--notdienst-hairline-soft:oklch(0.93_0.01_75)] [--notdienst-hairline:oklch(0.90_0.012_75)] [--notdienst-ink-faint:oklch(0.76_0.01_45)] [--notdienst-ink-muted:oklch(0.60_0.012_45)] [--notdienst-ink-soft:oklch(0.45_0.015_45)] [--notdienst-ink:oklch(0.26_0.015_45)] [--notdienst-out-month:oklch(0.82_0.012_75)] [--notdienst-surface-week:oklch(0.945_0.014_75)] [--notdienst-surface:oklch(0.985_0.008_75)]"
      style={{
        backgroundImage:
          "radial-gradient(oklch(0.85 0.02 75 / 0.10) 1px, transparent 1px)",
        backgroundSize: "16px 16px",
      }}
    >
      <header className="flex items-start justify-between gap-8">
        <div>
          <p className="text-[11px] font-semibold uppercase leading-none text-[var(--notdienst-accent)] [letter-spacing:0.18em]">
            NOTDIENST · {format(visibleMonthStart, "MMMM yyyy", { locale: de })}
          </p>
          <h1 className="mt-2 text-[36px] font-normal leading-tight text-[var(--notdienst-ink)] [letter-spacing:-0.025em]">
            Wann magst du{" "}
            <em className="font-normal italic text-[var(--notdienst-accent-deep)]">
              helfen
            </em>
            ?
          </h1>
        </div>

        <div className="mt-3 flex items-center gap-4 rounded-full border border-[var(--notdienst-hairline)] bg-[var(--notdienst-surface)] px-5 py-3 shadow-[0_12px_30px_oklch(0.36_0.02_45_/_0.06)]">
          <div className="flex items-center gap-1.5">
            {Array.from({ length: Math.max(requiredDays, 1) }).map(
              (_, index) => {
                const isMarked = index < selectedCount;
                return (
                  <span
                    key={index}
                    className={cn(
                      "h-2.5 w-2.5 rounded-full border border-[var(--notdienst-accent)]",
                      isMarked && "bg-[var(--notdienst-accent)]",
                    )}
                  />
                );
              },
            )}
            {extraCount > 0 ? (
              <span className="ml-1 text-[11px] font-semibold text-[var(--notdienst-accent-deep)]">
                +{extraCount}
              </span>
            ) : null}
          </div>
          <span className="text-[13px] font-semibold text-[var(--notdienst-ink-soft)]">
            {statusText}
          </span>
          <button
            type="button"
            onClick={onSave}
            disabled={disabled || isSaving || selectedCount < requiredDays}
            className="inline-flex h-8 items-center gap-2 rounded-full bg-[var(--notdienst-accent-deep)] px-5 text-[13px] font-bold text-white transition hover:brightness-110 disabled:cursor-not-allowed disabled:opacity-55"
          >
            {isSaving ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : null}
            Speichern
          </button>
        </div>
      </header>

      <div className="mt-9 flex items-center justify-between gap-8">
        <div className="flex items-center gap-3">
          <h2 className="text-[22px] font-medium text-[var(--notdienst-ink)] [font-variant-numeric:tabular-nums]">
            {format(visibleMonthStart, "MMMM yyyy", { locale: de })}
          </h2>
          <div className="flex items-center gap-1.5">
            <button
              type="button"
              aria-label="Vorheriger Monat"
              onClick={() =>
                setVisibleMonth(
                  new Date(
                    visibleMonthStart.getFullYear(),
                    visibleMonthStart.getMonth() - 1,
                    1,
                  ),
                )
              }
              className="inline-flex h-7 w-7 items-center justify-center rounded-full border border-[var(--notdienst-hairline)] bg-[var(--notdienst-surface)] text-[var(--notdienst-ink-soft)] transition hover:border-[var(--notdienst-accent-mid)] hover:text-[var(--notdienst-accent-deep)]"
            >
              <ChevronLeft className="h-4 w-4" />
            </button>
            <button
              type="button"
              aria-label="Nächster Monat"
              onClick={() =>
                setVisibleMonth(
                  new Date(
                    visibleMonthStart.getFullYear(),
                    visibleMonthStart.getMonth() + 1,
                    1,
                  ),
                )
              }
              className="inline-flex h-7 w-7 items-center justify-center rounded-full border border-[var(--notdienst-hairline)] bg-[var(--notdienst-surface)] text-[var(--notdienst-ink-soft)] transition hover:border-[var(--notdienst-accent-mid)] hover:text-[var(--notdienst-accent-deep)]"
            >
              <ChevronRight className="h-4 w-4" />
            </button>
          </div>
        </div>

        <CalendarLegend />
      </div>

      <div className="mt-8 grid grid-cols-7 gap-1.5">
        {[
          "Montag",
          "Dienstag",
          "Mittwoch",
          "Donnerstag",
          "Freitag",
          "Samstag",
          "Sonntag",
        ].map((weekday) => (
          <div
            key={weekday}
            className="px-2 text-[11px] font-semibold uppercase text-[var(--notdienst-ink-muted)] [letter-spacing:0.08em]"
          >
            {weekday}
          </div>
        ))}
      </div>

      <div className="mt-2 grid grid-cols-7 gap-1.5">
        {calendarDays.map((day) => {
          const dateKey = formatDateKey(day);
          const isInTargetMonth = isSameMonth(day, visibleMonthStart);
          const isSelected = selected.has(dateKey);
          const dayEvents = getCalendarItemsForDay(events, day);
          const hasClosure = dayEvents.some(
            (event) => event.category === "closure",
          );
          const isSelectable =
            isInTargetMonth && selectableDateKeys.has(dateKey) && !hasClosure;
          const isWeekend = day.getDay() === 0 || day.getDay() === 6;

          return (
            <CalendarTile
              key={dateKey}
              day={day}
              events={dayEvents}
              isInTargetMonth={isInTargetMonth}
              isSelected={isSelected}
              isSelectable={isSelectable}
              isWeekend={isWeekend}
              hasClosure={hasClosure}
              disabled={disabled}
              onToggle={() => toggleDate(dateKey)}
              onOpenEvent={setActiveEvent}
            />
          );
        })}
      </div>

      {activeEvent ? (
        <EventDialog
          event={activeEvent}
          onClose={() => setActiveEvent(null)}
        />
      ) : null}
    </section>
  );
}

function CalendarTile({
  day,
  events,
  isInTargetMonth,
  isSelected,
  isSelectable,
  isWeekend,
  hasClosure,
  disabled,
  onToggle,
  onOpenEvent,
}: {
  day: Date;
  events: NotdienstCalendarItem[];
  isInTargetMonth: boolean;
  isSelected: boolean;
  isSelectable: boolean;
  isWeekend: boolean;
  hasClosure: boolean;
  disabled: boolean;
  onToggle: () => void;
  onOpenEvent: (event: NotdienstCalendarItem) => void;
}) {
  return (
    <div
      className={cn(
        "relative min-h-24 rounded-[10px] border p-[10px_11px] text-left transition",
        "border-[var(--notdienst-hairline)] bg-[var(--notdienst-surface)]",
        isSelectable &&
          !disabled &&
          "cursor-pointer hover:border-[var(--notdienst-accent-mid)] hover:shadow-[0_10px_28px_oklch(0.36_0.02_45_/_0.08)]",
        isSelected &&
          "border-[var(--notdienst-accent-mid)] bg-[var(--notdienst-accent-soft)]",
        hasClosure &&
          "cursor-default border-[oklch(0.88_0.04_35)] bg-[oklch(0.96_0.018_35)]",
        (isWeekend || !isInTargetMonth) &&
          "cursor-default border-[var(--notdienst-hairline-soft)] bg-[var(--notdienst-surface-week)]",
      )}
      onClick={isSelectable && !disabled ? onToggle : undefined}
    >
      <div className="flex items-start justify-between gap-2">
        <span
          className={cn(
            "font-medium leading-none [font-variant-numeric:tabular-nums] [letter-spacing:-0.02em]",
            isWeekend || !isInTargetMonth
              ? "text-[13px] text-[var(--notdienst-out-month)]"
              : "text-[17px] text-[var(--notdienst-ink)]",
            isSelected && "text-[var(--notdienst-accent-deep)]",
            hasClosure && "text-[oklch(0.38_0.08_35)]",
          )}
        >
          {format(day, "d")}
        </span>
        {isSelected ? (
          <Check className="h-2.5 w-2.5 text-[var(--notdienst-accent-deep)] [stroke-width:2.5]" />
        ) : null}
        {hasClosure ? (
          <LockKeyhole className="h-3 w-3 text-[oklch(0.38_0.08_35)]" />
        ) : null}
      </div>

      <div className="absolute inset-x-[11px] bottom-[10px] space-y-1.5">
        {events.slice(0, 2).map((event) => (
          <button
            key={`${formatDateKey(day)}-${event.id}`}
            type="button"
            className="group/event relative flex w-full items-center gap-1.5 overflow-visible text-left"
            onClick={(clickEvent) => {
              clickEvent.stopPropagation();
              onOpenEvent(event);
            }}
          >
            <span
              className="h-4 w-[3px] shrink-0 rounded-sm"
              style={{
                backgroundColor: notdienstEventCategories[event.category].dot,
              }}
            />
            <span className="truncate text-[11px] font-medium text-[var(--notdienst-ink-soft)]">
              {event.title}
            </span>
            <span className="pointer-events-none absolute bottom-full left-0 z-20 mb-2 hidden min-w-44 rounded-lg bg-[oklch(0.22_0.015_45)] px-3 py-2 text-left text-white shadow-xl group-hover/event:block">
              <span className="block text-[10px] font-semibold uppercase text-white/65 [letter-spacing:0.08em]">
                {notdienstEventCategories[event.category].label}
              </span>
              <span className="mt-0.5 block text-[12px] font-semibold">
                {event.title}
              </span>
              <span className="mt-1 block text-[11px] text-white/75">
                {formatEventTime(event)}
              </span>
            </span>
          </button>
        ))}
        {events.length > 2 ? (
          <p className="text-[11px] font-medium text-[var(--notdienst-ink-muted)]">
            +{events.length - 2} weitere
          </p>
        ) : null}
      </div>
    </div>
  );
}

function CalendarLegend() {
  return (
    <div className="flex flex-wrap items-center justify-end gap-4 text-[11px] font-medium text-[var(--notdienst-ink-soft)]">
      <span className="inline-flex items-center gap-1.5">
        <span className="h-[9px] w-[9px] rounded-[2px] bg-[var(--notdienst-accent)]" />
        Dein Notdienst
      </span>
      {Object.entries(notdienstEventCategories).map(([key, category]) => (
        <span key={key} className="inline-flex items-center gap-1.5">
          <span
            className="h-[9px] w-[9px] rounded-[2px]"
            style={{ backgroundColor: category.dot }}
          />
          {category.label}
        </span>
      ))}
    </div>
  );
}

function EventDialog({
  event,
  onClose,
}: {
  event: NotdienstCalendarItem;
  onClose: () => void;
}) {
  const category = notdienstEventCategories[event.category];

  return (
    <div
      className="fixed inset-0 z-50 flex items-center justify-center bg-[oklch(0.25_0.015_45_/_0.20)] p-6 backdrop-blur-[2px]"
      onClick={onClose}
    >
      <div
        className="w-full max-w-md rounded-xl border border-[var(--notdienst-hairline)] bg-[var(--notdienst-surface)] p-6 text-[var(--notdienst-ink)] shadow-2xl"
        onClick={(eventClick) => eventClick.stopPropagation()}
      >
        <div className="flex items-start justify-between gap-4">
          <span
            className="inline-flex rounded-full border px-3 py-1 text-[11px] font-semibold"
            style={{
              backgroundColor: category.bg,
              borderColor: category.border,
              color: category.text,
            }}
          >
            {category.label}
          </span>
          <button
            type="button"
            aria-label="Detail schließen"
            onClick={onClose}
            className="inline-flex h-8 w-8 items-center justify-center rounded-full text-[var(--notdienst-ink-muted)] transition hover:bg-[var(--notdienst-surface-week)] hover:text-[var(--notdienst-ink)]"
          >
            <X className="h-4 w-4" />
          </button>
        </div>
        <h3 className="mt-4 text-2xl font-medium leading-tight [letter-spacing:-0.02em]">
          {event.title}
        </h3>
        <p className="mt-3 flex items-center gap-2 text-sm font-medium text-[var(--notdienst-ink-soft)]">
          <Clock3 className="h-4 w-4" />
          {formatEventDate(event)}
        </p>
        {event.note ? (
          <p className="mt-5 text-sm leading-6 text-[var(--notdienst-ink-soft)]">
            {event.note}
          </p>
        ) : null}
      </div>
    </div>
  );
}

function getCalendarItemsForDay(
  items: NotdienstCalendarItem[],
  day: Date,
) {
  const dayStart = startOfDay(day).getTime();

  return items.filter((item) => {
    const itemStart = startOfDay(item.startDate).getTime();
    const itemEnd = startOfDay(item.endDate).getTime();

    return itemStart <= dayStart && itemEnd >= dayStart;
  });
}

function getClosureDateKeys(items: NotdienstCalendarItem[]) {
  const closureKeys = new Set<string>();

  for (const item of items) {
    if (item.category !== "closure") continue;

    const days = eachDayOfInterval({
      start: startOfDay(item.startDate),
      end: startOfDay(item.endDate),
    });

    for (const day of days) {
      closureKeys.add(formatDateKey(day));
    }
  }

  return closureKeys;
}

function formatEventDate(event: NotdienstCalendarItem) {
  const date = format(event.startDate, "EEEE, d. MMMM yyyy", { locale: de });
  return event.allDay
    ? `${date} · Ganztägig`
    : `${date} · ${formatEventTime(event)}`;
}

function formatEventTime(event: NotdienstCalendarItem) {
  if (event.allDay) return "Ganztägig";

  return `${format(event.startDate, "HH:mm")} - ${format(
    event.endDate,
    "HH:mm",
  )}`;
}
