"use client";

import FullCalendar from "@fullcalendar/react";
import dayGridPlugin from "@fullcalendar/daygrid";
import interactionPlugin from "@fullcalendar/interaction";
import timeGridPlugin from "@fullcalendar/timegrid";
import listPlugin from "@fullcalendar/list";
import type { DateSelectArg, EventClickArg, EventDropArg } from "@fullcalendar/core";
import type { EventResizeDoneArg } from "@fullcalendar/interaction";
import { CalendarPlus, Check, Eye, EyeOff, Plus, Trash2, X } from "lucide-react";
import dynamic from "next/dynamic";
import { useMemo, useState, useEffect, useTransition } from "react";
import { richTextToPlainText } from "@/components/rich-text";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { ShareButton } from "@/components/share-button";
import { ReminderPicker } from "@/components/reminder-picker";
import type { CalView } from "@/modules/_core/themes";
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select";
import type { CalendarDto, CalendarEventDto } from "../server/queries";
import { getEventReminderOffsets } from "../server/queries";
import {
  createCalendar,
  createEvent,
  deleteCalendar,
  deleteEvent,
  renameCalendar,
  setCalendarColor,
  setCalendarVisibility,
  updateEvent,
} from "../server/actions";

const RichTextEditor = dynamic(
  () => import("@/components/rich-text/rich-text-editor").then((mod) => mod.RichTextEditor),
  {
    ssr: false,
    loading: () => (
      <div className="min-h-24 rounded-lg border border-input px-3 py-2 text-sm text-muted-foreground animate-pulse">
        Loading editor…
      </div>
    ),
  },
);

type EventDraft = {
  id?: string;
  calendarId: string;
  title: string;
  startAt: string;
  endAt: string;
  allDay: boolean;
  location: string;
  notes: string;
  reminderOffsets: number[];
};

type MobileView = "day" | "week" | "agenda";

const DEFAULT_COLOR = "#B85C3C";
const VISIBILITY_ITEMS = [
  { label: "Household", value: "household" },
  { label: "Private", value: "private" },
];

const VIEW_MAP: Record<CalView, string> = {
  month: "dayGridMonth",
  week: "timeGridWeek",
  day: "timeGridDay",
};

const MOBILE_VIEW_MAP: Record<MobileView, string> = {
  day: "timeGridDay",
  week: "timeGridWeek",
  agenda: "listWeek",
};

function withAlpha(hex: string, alpha: number): string {
  if (!hex.startsWith("#")) return hex;
  let h = hex.slice(1);
  if (h.length === 3)
    h = h
      .split("")
      .map((c) => c + c)
      .join("");
  if (h.length !== 6) return hex;
  const a = Math.round(Math.min(1, Math.max(0, alpha)) * 255)
    .toString(16)
    .padStart(2, "0");
  return `#${h}${a}`;
}

export function CalendarShell({
  calendars,
  events,
  defaultView = "month",
  defaultReminderOffsets = [30],
}: {
  calendars: CalendarDto[];
  events: CalendarEventDto[];
  defaultView?: CalView;
  defaultReminderOffsets?: number[];
}) {
  const [calendarRows, setCalendarRows] = useState(calendars);
  const [eventRows, setEventRows] = useState(events);
  const [visibleIds, setVisibleIds] = useState(() => new Set(calendars.map((c) => c.id)));
  const [selectedEvent, setSelectedEvent] = useState<EventDraft | null>(null);
  const [calendarName, setCalendarName] = useState("");
  const [calendarColor, setCalendarColorValue] = useState(DEFAULT_COLOR);
  const [calendarVisibility, setCalendarVisibilityValue] = useState<"private" | "household">(
    "household",
  );
  const [lastCalendarId, setLastCalendarId] = useState(calendars[0]?.id ?? "");
  const [isPending, startTransition] = useTransition();
  const [isMobile, setIsMobile] = useState(false);
  const [showCalendarsPanel, setShowCalendarsPanel] = useState(false);
  const [mobileView, setMobileView] = useState<MobileView>("day");

  useEffect(() => {
    const mq = window.matchMedia("(max-width: 759px)");
    const apply = () => setIsMobile(mq.matches);
    apply();
    mq.addEventListener("change", apply);
    return () => mq.removeEventListener("change", apply);
  }, []);

  const defaultCalendarId = calendarRows.some((calendar) => calendar.id === lastCalendarId)
    ? lastCalendarId
    : (calendarRows[0]?.id ?? "");
  const visibleEvents = useMemo(
    () => eventRows.filter((event) => visibleIds.has(event.calendarId)),
    [eventRows, visibleIds],
  );
  const calendarSelectItems = useMemo(
    () => calendarRows.map((calendar) => ({ label: calendar.name, value: calendar.id })),
    [calendarRows],
  );

  function toggleCalendar(id: string) {
    setVisibleIds((current) => {
      const next = new Set(current);
      if (next.has(id)) next.delete(id);
      else next.add(id);
      return next;
    });
  }

  function openNewEvent(startAt?: Date, endAt?: Date, allDay = false) {
    if (!defaultCalendarId) return;
    const start = startAt ?? new Date();
    const end = endAt ?? new Date(start.getTime() + 60 * 60 * 1000);
    setSelectedEvent({
      calendarId: defaultCalendarId,
      title: "",
      startAt: toInputDateTime(start),
      endAt: toInputDateTime(end),
      allDay,
      location: "",
      notes: "",
      reminderOffsets: [...defaultReminderOffsets],
    });
  }

  function openExistingEvent({ event }: EventClickArg) {
    const row = eventRows.find((item) => item.id === event.id);
    if (!row) return;
    const eventId = row.id;
    setSelectedEvent({
      id: row.id,
      calendarId: row.calendarId,
      title: row.title,
      startAt: toInputDateTime(new Date(row.startAt)),
      endAt: toInputDateTime(new Date(row.endAt)),
      allDay: row.allDay,
      location: row.location ?? "",
      notes: row.notes ?? "",
      reminderOffsets: [],
    });
    void getEventReminderOffsets(eventId).then((offsets) => {
      setSelectedEvent((current) =>
        current?.id === eventId ? { ...current, reminderOffsets: offsets } : current,
      );
    });
  }

  function handleDateSelect(selection: DateSelectArg) {
    openNewEvent(selection.start, selection.end, selection.allDay);
  }

  function saveSelectedEvent() {
    if (!selectedEvent) return;
    const eventId = selectedEvent.id;
    const payload = {
      calendarId: selectedEvent.calendarId,
      title: selectedEvent.title,
      startAt: new Date(selectedEvent.startAt),
      endAt: new Date(selectedEvent.endAt),
      allDay: selectedEvent.allDay,
      location: selectedEvent.location || null,
      notes: richTextToPlainText(selectedEvent.notes) ? selectedEvent.notes : null,
    };

    startTransition(async () => {
      if (eventId) {
        await updateEvent({
          id: eventId,
          ...payload,
          reminderOffsets: selectedEvent.reminderOffsets,
        });
        setLastCalendarId(payload.calendarId);
        setEventRows((current) =>
          current.map((event) =>
            event.id === eventId
              ? {
                  ...event,
                  calendarId: payload.calendarId,
                  title: payload.title,
                  allDay: payload.allDay,
                  location: payload.location,
                  notes: payload.notes,
                  startAt: payload.startAt.toISOString(),
                  endAt: payload.endAt.toISOString(),
                }
              : event,
          ),
        );
      } else {
        const created = await createEvent({
          ...payload,
          reminderOffsets: selectedEvent.reminderOffsets,
        });
        setLastCalendarId(payload.calendarId);
        setEventRows((current) => [...current, created]);
      }
      setSelectedEvent(null);
    });
  }

  function removeSelectedEvent() {
    if (!selectedEvent?.id) return;
    const id = selectedEvent.id;
    startTransition(async () => {
      await deleteEvent({ id });
      setEventRows((current) => current.filter((event) => event.id !== id));
      setSelectedEvent(null);
    });
  }

  function moveEvent(change: EventDropArg | EventResizeDoneArg) {
    const start = change.event.start;
    const end = change.event.end ?? start;
    if (!start || !end) return;
    const id = change.event.id;
    setEventRows((current) =>
      current.map((event) =>
        event.id === id
          ? {
              ...event,
              startAt: start.toISOString(),
              endAt: end.toISOString(),
              allDay: change.event.allDay,
            }
          : event,
      ),
    );
    startTransition(async () => {
      await updateEvent({
        id,
        startAt: start,
        endAt: end,
        allDay: change.event.allDay,
      });
    });
  }

  function addCalendar() {
    startTransition(async () => {
      const created = await createCalendar({
        name: calendarName,
        color: calendarColor,
        visibility: calendarVisibility,
      });
      const calendar = {
        id: created.id,
        name: created.name,
        color: created.color,
        visibility: created.visibility as "private" | "household",
        ownerId: created.ownerId,
      };
      setCalendarRows((current) => [...current, calendar]);
      setVisibleIds((current) => new Set([...current, calendar.id]));
      setLastCalendarId(calendar.id);
      setCalendarName("");
      setCalendarColorValue(DEFAULT_COLOR);
      setCalendarVisibilityValue("household");
    });
  }

  function updateCalendar(calendar: CalendarDto, values: Partial<CalendarDto>) {
    const next = { ...calendar, ...values };
    setCalendarRows((current) => current.map((item) => (item.id === calendar.id ? next : item)));
    startTransition(async () => {
      if (values.name !== undefined) {
        await renameCalendar({ id: calendar.id, name: values.name });
      }
      if (values.color !== undefined) {
        await setCalendarColor({ id: calendar.id, color: values.color });
      }
      if (values.visibility !== undefined) {
        await setCalendarVisibility({ id: calendar.id, visibility: values.visibility });
      }
    });
  }

  function updateCalendarName(calendar: CalendarDto, name: string) {
    setCalendarRows((current) =>
      current.map((item) => (item.id === calendar.id ? { ...item, name } : item)),
    );
  }

  function commitCalendarName(calendar: CalendarDto) {
    if (!calendar.name.trim()) return;
    startTransition(async () => {
      await renameCalendar({ id: calendar.id, name: calendar.name });
    });
  }

  function removeCalendar(calendarId: string) {
    startTransition(async () => {
      await deleteCalendar({ id: calendarId });
      setCalendarRows((current) => current.filter((calendar) => calendar.id !== calendarId));
      setEventRows((current) => current.filter((event) => event.calendarId !== calendarId));
      setVisibleIds((current) => {
        const next = new Set(current);
        next.delete(calendarId);
        return next;
      });
    });
  }

  // Shared calendar list + new-calendar form rendered in both the desktop aside
  // and the mobile bottom-sheet panel.
  const calendarsPanelContent = (
    <>
      <div className="space-y-2">
        {calendarRows.map((calendar) => (
          <div key={calendar.id} className="rounded-lg border bg-background p-2">
            <div className="flex items-center gap-2">
              <button
                type="button"
                className="grid size-7 place-items-center rounded-md border"
                onClick={() => toggleCalendar(calendar.id)}
                aria-label={`${visibleIds.has(calendar.id) ? "Hide" : "Show"} ${calendar.name}`}
              >
                {visibleIds.has(calendar.id) ? <Eye /> : <EyeOff />}
              </button>
              <span
                className="size-3 rounded-full"
                style={{ backgroundColor: calendar.color ?? DEFAULT_COLOR }}
              />
              <Input
                aria-label={`${calendar.name} name`}
                value={calendar.name}
                onChange={(event) => updateCalendarName(calendar, event.target.value)}
                onBlur={() => commitCalendarName(calendar)}
              />
            </div>
            <div className="mt-2 grid grid-cols-[1fr_1fr_auto] gap-2">
              <Input
                aria-label={`${calendar.name} color`}
                type="color"
                value={calendar.color ?? DEFAULT_COLOR}
                onChange={(event) => updateCalendar(calendar, { color: event.target.value })}
              />
              <Select
                items={VISIBILITY_ITEMS}
                value={calendar.visibility}
                onValueChange={(value) =>
                  updateCalendar(calendar, { visibility: value as "private" | "household" })
                }
              >
                <SelectTrigger aria-label={`${calendar.name} visibility`}>
                  <SelectValue>
                    {calendar.visibility === "household" ? "Household" : "Private"}
                  </SelectValue>
                </SelectTrigger>
                <SelectContent>
                  <SelectGroup>
                    {VISIBILITY_ITEMS.map((item) => (
                      <SelectItem key={item.value} value={item.value}>
                        {item.label}
                      </SelectItem>
                    ))}
                  </SelectGroup>
                </SelectContent>
              </Select>
              <Button
                size="icon"
                variant="ghost"
                onClick={() => removeCalendar(calendar.id)}
                aria-label={`Delete ${calendar.name}`}
              >
                <Trash2 />
              </Button>
            </div>
          </div>
        ))}
      </div>

      <div className="mt-4 rounded-lg border bg-background p-3">
        <div className="mb-3 flex items-center gap-2 text-sm font-medium">
          <CalendarPlus className="size-4" />
          New calendar
        </div>
        <div className="space-y-2">
          <Label htmlFor="calendar-name">Name</Label>
          <Input
            id="calendar-name"
            value={calendarName}
            onChange={(event) => setCalendarName(event.target.value)}
          />
          <div className="grid grid-cols-[1fr_1fr] gap-2">
            <Input
              aria-label="Calendar color"
              type="color"
              value={calendarColor}
              onChange={(event) => setCalendarColorValue(event.target.value)}
            />
            <Select
              items={VISIBILITY_ITEMS}
              value={calendarVisibility}
              onValueChange={(value) =>
                setCalendarVisibilityValue(value as "private" | "household")
              }
            >
              <SelectTrigger aria-label="Calendar visibility">
                <SelectValue>
                  {calendarVisibility === "household" ? "Household" : "Private"}
                </SelectValue>
              </SelectTrigger>
              <SelectContent>
                <SelectGroup>
                  {VISIBILITY_ITEMS.map((item) => (
                    <SelectItem key={item.value} value={item.value}>
                      {item.label}
                    </SelectItem>
                  ))}
                </SelectGroup>
              </SelectContent>
            </Select>
          </div>
          <Button className="w-full" onClick={addCalendar} disabled={!calendarName}>
            <Check />
            Create calendar
          </Button>
        </div>
      </div>
    </>
  );

  const fcEvents = visibleEvents.map((event) => {
    const color =
      calendarRows.find((calendar) => calendar.id === event.calendarId)?.color ?? DEFAULT_COLOR;
    return {
      id: event.id,
      title: event.title,
      start: event.startAt,
      end: event.endAt,
      allDay: event.allDay,
      backgroundColor: withAlpha(color, 0.14),
      borderColor: color,
      textColor: "var(--ink-2)",
      extendedProps: { calendarId: event.calendarId, color },
    };
  });

  // Shared event form content — rendered inside both the desktop centered modal
  // and the mobile bottom-sheet.
  const eventFormContent = selectedEvent && (
    <div className="grid gap-3">
      <Label htmlFor="event-title">Title</Label>
      <Input
        id="event-title"
        value={selectedEvent.title}
        onChange={(event) => setSelectedEvent({ ...selectedEvent, title: event.target.value })}
      />
      <Label htmlFor="event-calendar">Calendar</Label>
      <Select
        items={calendarSelectItems}
        value={selectedEvent.calendarId}
        onValueChange={(value) => setSelectedEvent({ ...selectedEvent, calendarId: value ?? "" })}
      >
        <SelectTrigger id="event-calendar">
          <SelectValue>
            {calendarRows.find((c) => c.id === selectedEvent.calendarId)?.name ??
              "Select a calendar"}
          </SelectValue>
        </SelectTrigger>
        <SelectContent>
          <SelectGroup>
            {calendarSelectItems.map((calendar) => (
              <SelectItem key={calendar.value} value={calendar.value}>
                {calendar.label}
              </SelectItem>
            ))}
          </SelectGroup>
        </SelectContent>
      </Select>
      <div className="grid gap-3 sm:grid-cols-2">
        <div className="space-y-1.5">
          <Label htmlFor="event-start">Start</Label>
          <Input
            id="event-start"
            type="datetime-local"
            value={selectedEvent.startAt}
            onChange={(event) =>
              setSelectedEvent({ ...selectedEvent, startAt: event.target.value })
            }
          />
        </div>
        <div className="space-y-1.5">
          <Label htmlFor="event-end">End</Label>
          <Input
            id="event-end"
            type="datetime-local"
            value={selectedEvent.endAt}
            onChange={(event) => setSelectedEvent({ ...selectedEvent, endAt: event.target.value })}
          />
        </div>
      </div>
      <Label htmlFor="event-location">Location</Label>
      <Input
        id="event-location"
        value={selectedEvent.location}
        onChange={(event) => setSelectedEvent({ ...selectedEvent, location: event.target.value })}
      />
      <div className="space-y-1.5 min-w-0">
        <Label htmlFor="event-notes">Notes</Label>
        <RichTextEditor
          id="event-notes"
          aria-label="Notes"
          value={selectedEvent.notes}
          onChange={(notes) => setSelectedEvent({ ...selectedEvent, notes })}
          disabled={isPending}
          placeholder="Add details…"
        />
      </div>
      <ReminderPicker
        offsets={selectedEvent.reminderOffsets}
        onChange={(reminderOffsets) => setSelectedEvent({ ...selectedEvent, reminderOffsets })}
        disabled={isPending}
      />
      <div className="flex items-center justify-between gap-2 pt-2">
        <div className="flex gap-2">
          {selectedEvent.id && (
            <Button variant="destructive" onClick={removeSelectedEvent}>
              Delete event
            </Button>
          )}
        </div>
        <div className="flex gap-2">
          {selectedEvent.id && (
            <ShareButton entityType="calendar.event" entityId={selectedEvent.id} />
          )}
          <Button onClick={saveSelectedEvent} disabled={!selectedEvent.title || isPending}>
            Save event
          </Button>
        </div>
      </div>
    </div>
  );

  return (
    <div
      className="grid grid-cols-1 lg:grid-cols-[280px_1fr]"
      style={{ minHeight: "calc(100dvh - var(--cal-topbar-h) - var(--cal-nav-h))" }}
    >
      {/* Desktop aside — hidden on mobile */}
      <aside
        className={`${isMobile ? "hidden" : ""} border-b bg-sidebar p-4 lg:border-r lg:border-b-0`}
      >
        <div className="mb-4 flex items-center justify-between">
          <h1 className="text-xl font-semibold">Calendar</h1>
          <Button size="icon-sm" variant="outline" onClick={() => openNewEvent()}>
            <Plus />
            <span className="sr-only">New event</span>
          </Button>
        </div>
        {calendarsPanelContent}
      </aside>

      {/* Calendar section */}
      <section className="fc-skin flex min-w-0 flex-col">
        {/* Mobile toolbar: view switcher + calendars button + new event */}
        {isMobile && (
          <div
            className="flex items-center gap-2 border-b px-3 py-2"
            style={{ borderColor: "var(--hair)" }}
          >
            <div className="seg flex-1">
              {(["day", "week", "agenda"] as MobileView[]).map((v) => (
                <button
                  key={v}
                  type="button"
                  aria-selected={mobileView === v}
                  onClick={() => setMobileView(v)}
                >
                  {v.charAt(0).toUpperCase() + v.slice(1)}
                </button>
              ))}
            </div>
            <button
              type="button"
              className="btn btn-ghost btn-sm"
              onClick={() => setShowCalendarsPanel(true)}
            >
              <Eye size={14} />
              Cals
            </button>
            <button type="button" className="btn btn-primary btn-sm" onClick={() => openNewEvent()}>
              <Plus size={14} />
              New
            </button>
          </div>
        )}

        <div className="flex-1 p-4">
          <FullCalendar
            key={isMobile ? `mobile-${mobileView}` : `desktop-${defaultView}`}
            plugins={[dayGridPlugin, timeGridPlugin, interactionPlugin, listPlugin]}
            initialView={isMobile ? MOBILE_VIEW_MAP[mobileView] : VIEW_MAP[defaultView]}
            headerToolbar={
              isMobile
                ? { left: "prev,next", center: "title", right: "today" }
                : {
                    left: "prev,next today",
                    center: "title",
                    right: "dayGridMonth,timeGridWeek,timeGridDay",
                  }
            }
            buttonText={{ today: "Today", month: "Month", week: "Week", day: "Day", list: "List" }}
            dayHeaderFormat={{ weekday: "short" }}
            selectable={!isMobile}
            editable={!isMobile}
            eventResizableFromStart={!isMobile}
            select={handleDateSelect}
            eventClick={openExistingEvent}
            eventDrop={moveEvent}
            eventResize={moveEvent}
            events={fcEvents}
            eventClassNames={(arg) => {
              const id = String(arg.event.extendedProps["calendarId"] ?? "");
              return id ? [`fc-cal-${id.slice(0, 8)}`] : [];
            }}
            height="auto"
          />
        </div>
      </section>

      {/* Mobile: calendars bottom sheet */}
      {isMobile && showCalendarsPanel && (
        <div
          className="fixed inset-0 z-[45] bg-black/40"
          onClick={() => setShowCalendarsPanel(false)}
        >
          <div
            className="absolute inset-x-0 overflow-auto rounded-t-[var(--r-xl)]"
            style={{
              bottom: "var(--cal-nav-h)",
              maxHeight: "72dvh",
              background: "var(--card)",
              paddingBottom: "env(safe-area-inset-bottom)",
            }}
            onClick={(e) => e.stopPropagation()}
          >
            <div
              className="flex items-center justify-between border-b p-4"
              style={{ borderColor: "var(--hair)" }}
            >
              <h3 className="serif text-[16px] font-medium">Calendars</h3>
              <button
                type="button"
                className="btn btn-ghost btn-icon"
                aria-label="Close calendars panel"
                onClick={() => setShowCalendarsPanel(false)}
              >
                <X size={16} />
              </button>
            </div>
            <div className="p-4">{calendarsPanelContent}</div>
          </div>
        </div>
      )}

      {/* Event form — centered modal on desktop, bottom sheet on mobile */}
      {selectedEvent && (
        <div
          className="fixed inset-0 z-50 flex bg-black/20"
          style={
            isMobile
              ? { alignItems: "flex-end" }
              : { alignItems: "center", justifyContent: "center" }
          }
          onClick={(e) => {
            if (e.target === e.currentTarget) setSelectedEvent(null);
          }}
        >
          <div
            className="overflow-auto bg-popover text-popover-foreground shadow-lg"
            style={{
              borderRadius: isMobile ? "var(--r-xl) var(--r-xl) 0 0" : "var(--r-lg)",
              width: isMobile ? "100%" : undefined,
              maxWidth: isMobile ? undefined : "672px",
              maxHeight: "90dvh",
              padding: "1rem",
              paddingBottom: isMobile ? "calc(1rem + env(safe-area-inset-bottom))" : "1rem",
            }}
          >
            <div className="mb-4 flex items-center justify-between">
              <h2 className="text-lg font-semibold">
                {selectedEvent.id ? "Edit event" : "New event"}
              </h2>
              <Button size="icon-sm" variant="ghost" onClick={() => setSelectedEvent(null)}>
                <X size={16} />
                <span className="sr-only">Close</span>
              </Button>
            </div>
            {eventFormContent}
          </div>
        </div>
      )}
    </div>
  );
}

function toInputDateTime(date: Date) {
  const offset = date.getTimezoneOffset();
  const local = new Date(date.getTime() - offset * 60 * 1000);
  return local.toISOString().slice(0, 16);
}
