import { AgentToggleButton } from "@agent-native/core/client/agent-chat";
import { agentNativePath } from "@agent-native/core/client/api-path";
import { useT } from "@agent-native/core/client/i18n";
import type {
  CalendarEvent,
  CalendarEventDraft,
  UpdateEventScope,
} from "@shared/api";
import {
  IconCheck,
  IconChevronLeft,
  IconChevronRight,
  IconChevronDown,
  IconMenu2,
  IconSearch,
} from "@tabler/icons-react";
import { useQueryClient } from "@tanstack/react-query";
import {
  format,
  startOfMonth,
  endOfMonth,
  startOfWeek,
  endOfWeek,
  addMonths,
  subMonths,
  addWeeks,
  subWeeks,
  addDays,
  subDays,
  parseISO,
  startOfDay,
} from "date-fns";
import { useState, useMemo, useEffect, useCallback, useRef } from "react";
import { Link } from "react-router";
import { toast } from "sonner";

import type { QuickCreateEvent } from "@/components/calendar/CommandPalette";
import { CommandPalette } from "@/components/calendar/CommandPalette";
import { CreateEventPopover } from "@/components/calendar/CreateEventDialog";
import { DayView } from "@/components/calendar/DayView";
import { DeleteEventDialog } from "@/components/calendar/DeleteEventDialog";
import { EventDetailPanel } from "@/components/calendar/EventDetailPanel";
import { GoogleConnectBanner } from "@/components/calendar/GoogleConnectBanner";
import {
  shouldPromptGuests,
  useGuestNotificationPrompt,
} from "@/components/calendar/GuestNotificationDialog";
import { MonthView } from "@/components/calendar/MonthView";
import { PeopleSearchDialog } from "@/components/calendar/PeopleSearchDialog";
import { WeekView } from "@/components/calendar/WeekView";
import { useCalendarContext } from "@/components/layout/AppLayout";
import type { ViewMode } from "@/components/layout/AppLayout";
import { Button } from "@/components/ui/button";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { Spinner } from "@/components/ui/spinner";
import {
  Tooltip,
  TooltipContent,
  TooltipProvider,
  TooltipTrigger,
} from "@/components/ui/tooltip";
import {
  useEvents,
  useCreateEvent,
  useUpdateEvent,
  useDeleteEvent,
  useRsvpEvent,
  findEventByCurrentOrReplacedId,
  prefetchEvents,
  shouldShowEventsSkeleton,
} from "@/hooks/use-events";
import { useGoogleAuthStatus } from "@/hooks/use-google-auth";
import { useMeetingStartNotifications } from "@/hooks/use-meeting-start-notifications";
import { useIsMobile } from "@/hooks/use-mobile";
import { useOverlayPeople } from "@/hooks/use-overlay-people";
import { useSettings } from "@/hooks/use-settings";
import { setUndoAction, runUndo } from "@/hooks/use-undo";
import { useViewPreferences } from "@/hooks/use-view-preferences";
import { resolveEventAccountEmail } from "@/lib/event-account-selection";
import { getGoogleEventColorHex } from "@/lib/event-colors";
import {
  dateTimeInTimezoneToIso,
  getLocalTimezone,
} from "@/lib/event-form-utils";
import { buildDeleteEventMutationInput } from "@/lib/event-mutation-inputs";
import { isMcpEmbedSurface } from "@/lib/mcp-embed";
import { cn } from "@/lib/utils";

const CALENDAR_DRAFT_EVENT_PREFIX = "calendar-draft-event:";

type DraftEventPatch = Partial<CalendarEvent> & {
  addGoogleMeet?: boolean;
  addZoom?: boolean;
  workingLocationType?: "homeOffice" | "officeLocation" | "customLocation";
  workingLocationLabel?: string;
};

function safeCalendarDraftId(id: string | undefined): string | null {
  return id && /^[a-zA-Z0-9_-]{1,64}$/.test(id) ? id : null;
}

function calendarDraftEventId(id: string) {
  return `${CALENDAR_DRAFT_EVENT_PREFIX}${id}`;
}

function calendarDraftIdFromEventId(eventId: string) {
  return eventId.startsWith(CALENDAR_DRAFT_EVENT_PREFIX)
    ? eventId.slice(CALENDAR_DRAFT_EVENT_PREFIX.length)
    : null;
}

function isSlotDraftId(id: string) {
  return id.startsWith("slot-");
}

function parseValidDate(value: string | undefined): Date | null {
  if (!value) return null;
  const date = new Date(value);
  return Number.isNaN(date.getTime()) ? null : date;
}

function fallbackDraftRange(fallbackDate: Date) {
  const start = new Date(fallbackDate);
  start.setHours(9, 0, 0, 0);
  const end = new Date(start);
  end.setHours(10, 0, 0, 0);
  return { start, end };
}

function isRecurringCalendarEvent(event: CalendarEvent): boolean {
  return Boolean(event.recurringEventId || event.recurrence?.length);
}

function updateScopePayload(scope: UpdateEventScope | undefined): {
  scope?: UpdateEventScope;
} {
  return scope ? { scope } : {};
}

function addMinutesToDateTimeParts(
  date: string,
  time: string,
  minutes: number,
) {
  const [hour, minute] = time.split(":").map(Number);
  const safeHour = Number.isFinite(hour) ? hour : 9;
  const safeMinute = Number.isFinite(minute) ? minute : 0;
  const totalMinutes = safeHour * 60 + safeMinute + minutes;
  const dayOffset = Math.floor(totalMinutes / (24 * 60));
  const minuteOfDay = ((totalMinutes % (24 * 60)) + 24 * 60) % (24 * 60);
  const endDate = new Date(`${date}T00:00:00`);
  endDate.setDate(endDate.getDate() + dayOffset);
  const endHour = Math.floor(minuteOfDay / 60);
  const endMinute = minuteOfDay % 60;
  const pad = (n: number) => String(n).padStart(2, "0");
  return {
    date: format(endDate, "yyyy-MM-dd"),
    time: `${pad(endHour)}:${pad(endMinute)}`,
  };
}

function draftRange(draft: CalendarEventDraft, fallbackDate: Date) {
  const fallback = fallbackDraftRange(fallbackDate);
  const start = parseValidDate(draft.start) ?? fallback.start;
  const parsedEnd = parseValidDate(draft.end);
  const end =
    parsedEnd && parsedEnd.getTime() > start.getTime()
      ? parsedEnd
      : new Date(start.getTime() + 60 * 60 * 1000);
  return { start, end };
}

function draftToCalendarEvent(
  draft: CalendarEventDraft,
  fallbackDate: Date,
): CalendarEvent {
  const { start, end } = draftRange(draft, fallbackDate);
  return {
    id: calendarDraftEventId(draft.id),
    title: draft.title?.trim() || "(No title)",
    description: draft.description ?? "",
    start: start.toISOString(),
    end: end.toISOString(),
    startTimeZone: draft.startTimeZone,
    endTimeZone: draft.endTimeZone ?? draft.startTimeZone,
    location: draft.location ?? draft.workingLocationLabel ?? "",
    allDay: draft.allDay ?? false,
    source: "local",
    accountEmail: draft.accountEmail,
    colorId: draft.colorId,
    color: draft.colorId ? getGoogleEventColorHex(draft.colorId) : undefined,
    transparency: draft.transparency,
    visibility: draft.visibility,
    eventType: draft.eventType ?? "default",
    recurrence: draft.recurrence,
    attendees: draft.attendees,
    reminders: draft.reminders,
    remindersUseDefault: draft.remindersUseDefault,
    attachments: draft.attachments,
    pendingConferenceProvider: draft.addZoom
      ? "zoom"
      : draft.addGoogleMeet
        ? "meet"
        : undefined,
    createdAt: draft.createdAt ?? new Date().toISOString(),
    updatedAt: draft.updatedAt ?? draft.createdAt ?? new Date().toISOString(),
  };
}

function applyDraftPatch(
  draft: CalendarEventDraft,
  patch: DraftEventPatch,
): CalendarEventDraft {
  const next: CalendarEventDraft = {
    ...draft,
    updatedAt: new Date().toISOString(),
  };
  const copy = <K extends keyof CalendarEventDraft>(key: K) => {
    if (patch[key] !== undefined) {
      next[key] = patch[key] as CalendarEventDraft[K];
    }
  };

  copy("title");
  copy("description");
  copy("start");
  copy("end");
  copy("startTimeZone");
  copy("endTimeZone");
  copy("location");
  copy("allDay");
  copy("eventType");
  copy("transparency");
  copy("visibility");
  copy("colorId");
  copy("recurrence");
  copy("reminders");
  copy("remindersUseDefault");
  copy("attachments");
  copy("attendees");
  copy("accountEmail");
  copy("workingLocationType");
  copy("workingLocationLabel");

  if (patch.addGoogleMeet !== undefined) {
    next.addGoogleMeet = patch.addGoogleMeet;
    if (patch.addGoogleMeet) next.addZoom = false;
  }
  if (patch.addZoom !== undefined) {
    next.addZoom = patch.addZoom;
    if (patch.addZoom) next.addGoogleMeet = false;
  }

  return next;
}

function persistCalendarDraft(draft: CalendarEventDraft) {
  const safeId = safeCalendarDraftId(draft.id);
  if (!safeId) return;
  fetch(
    agentNativePath(
      `/_agent-native/application-state/calendar-draft-${safeId}`,
    ),
    {
      method: "PUT",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(draft),
    },
  ).catch(() => {});
}

function deletePersistedCalendarDraft(id: string) {
  const safeId = safeCalendarDraftId(id);
  if (!safeId) return;
  fetch(
    agentNativePath(
      `/_agent-native/application-state/calendar-draft-${safeId}`,
    ),
    {
      method: "DELETE",
      headers: { "X-Agent-Native-CSRF": "1" },
    },
  ).catch(() => {});
}

export default function CalendarView() {
  const t = useT();
  const isMobile = useIsMobile();
  const {
    selectedDate,
    setSelectedDate,
    viewMode,
    setViewMode,
    peopleSearchOpen,
    setPeopleSearchOpen,
    addCalendarOpen,
    setAddCalendarOpen,
    setAddCalendarDefaultTab,
    eventDetailSidebar,
    setEventDetailSidebar,
    sidebarEvent,
    setSidebarEvent,
    focusedEvent,
    setFocusedEvent,
    hiddenCalendars,
    eventDraft,
    setEventDraft,
    openSidebar,
  } = useCalendarContext();
  const { prefs: viewPrefs, update: setViewPrefs } = useViewPreferences();
  const [createDialogOpen, setCreateDialogOpen] = useState(false);
  const [createDefaultStart, setCreateDefaultStart] = useState<string>();
  const [createDefaultEnd, setCreateDefaultEnd] = useState<string>();
  const [quickEditEventId, setQuickEditEventId] = useState<string | null>(null);
  const [quickEditTempIds, setQuickEditTempIds] = useState<
    Record<string, string>
  >({});
  const openedDraftIdRef = useRef<string | null>(null);
  const committingDraftIdsRef = useRef<Set<string>>(new Set());
  const discardedCommittingDraftsRef = useRef<Map<string, CalendarEventDraft>>(
    new Map(),
  );
  const [commandPaletteOpen, setCommandPaletteOpen] = useState(false);
  const [deleteDialogEvent, setDeleteDialogEvent] =
    useState<CalendarEvent | null>(null);

  const queryClient = useQueryClient();
  const googleStatus = useGoogleAuthStatus();
  const defaultAccountEmail = googleStatus.data?.accounts?.[0]?.email;
  const settingsQuery = useSettings();
  const { data: settings } = settingsQuery;
  const { data: rawOverlayPeople } = useOverlayPeople();
  const overlayPeople = Array.isArray(rawOverlayPeople) ? rawOverlayPeople : [];
  const overlayEmails = useMemo(
    () => overlayPeople.map((p) => p.email),
    [overlayPeople],
  );
  const createEvent = useCreateEvent();
  const updateEvent = useUpdateEvent();
  const deleteEvent = useDeleteEvent();
  const rsvpEvent = useRsvpEvent();
  const { promptGuestNotification, guestNotificationDialog } =
    useGuestNotificationPrompt();
  const viewModeLabels: Record<ViewMode, string> = {
    month: t("calendarView.month"),
    week: t("calendarView.week"),
    day: t("calendarView.day"),
  };

  // Compute date range for query based on view
  const { from, to } = useMemo(() => {
    switch (viewMode) {
      case "month": {
        const ms = startOfMonth(selectedDate);
        const me = endOfMonth(selectedDate);
        return {
          from: startOfWeek(ms).toISOString(),
          to: endOfWeek(me).toISOString(),
        };
      }
      case "week": {
        return {
          from: startOfWeek(selectedDate).toISOString(),
          to: endOfWeek(selectedDate).toISOString(),
        };
      }
      case "day": {
        const dayStart = new Date(selectedDate);
        dayStart.setHours(0, 0, 0, 0);
        const dayEnd = new Date(selectedDate);
        dayEnd.setHours(23, 59, 59, 999);
        return { from: dayStart.toISOString(), to: dayEnd.toISOString() };
      }
    }
  }, [viewMode, selectedDate]);

  const {
    data: rawEventsData,
    error: eventsError,
    isLoading,
    isFetching,
    isPlaceholderData,
  } = useEvents(from, to, overlayEmails);
  const rawEvents = Array.isArray(rawEventsData) ? rawEventsData : [];
  const draftEvent = useMemo(
    () => (eventDraft ? draftToCalendarEvent(eventDraft, selectedDate) : null),
    [eventDraft, selectedDate],
  );
  const draftEventIds = useMemo(
    () => (draftEvent ? [draftEvent.id] : []),
    [draftEvent],
  );

  useEffect(() => {
    if (!eventDraft || !defaultAccountEmail) return;
    const resolvedAccountEmail = resolveEventAccountEmail(
      googleStatus.data?.accounts ?? [],
      eventDraft.accountEmail,
    );
    if (
      !resolvedAccountEmail ||
      eventDraft.accountEmail === resolvedAccountEmail
    ) {
      return;
    }
    const nextDraft = { ...eventDraft, accountEmail: resolvedAccountEmail };
    setEventDraft(nextDraft);
    persistCalendarDraft(nextDraft);
  }, [
    defaultAccountEmail,
    eventDraft,
    googleStatus.data?.accounts,
    setEventDraft,
  ]);

  // Warm the adjacent ranges so j/k (and the chevron buttons) feel instant.
  // Borrowed from the mail template's background-warm pattern — fire-and-forget
  // prefetch that lets React Query dedupe and reuse the response when the user
  // actually navigates. Only runs once the current range has loaded so we
  // don't fight the primary fetch for bandwidth.
  useEffect(() => {
    if (isLoading) return;
    const ranges = (() => {
      switch (viewMode) {
        case "month": {
          const next = addMonths(selectedDate, 1);
          const prev = subMonths(selectedDate, 1);
          return [next, prev].map((d) => ({
            from: startOfWeek(startOfMonth(d)).toISOString(),
            to: endOfWeek(endOfMonth(d)).toISOString(),
          }));
        }
        case "week": {
          // Two weeks forward so rapid `j j` stays instant, plus one back.
          const next = addWeeks(selectedDate, 1);
          const next2 = addWeeks(selectedDate, 2);
          const prev = subWeeks(selectedDate, 1);
          return [next, next2, prev].map((d) => ({
            from: startOfWeek(d).toISOString(),
            to: endOfWeek(d).toISOString(),
          }));
        }
        case "day": {
          const next = addDays(selectedDate, 1);
          const prev = subDays(selectedDate, 1);
          return [next, prev].map((d) => {
            const start = new Date(d);
            start.setHours(0, 0, 0, 0);
            const end = new Date(d);
            end.setHours(23, 59, 59, 999);
            return { from: start.toISOString(), to: end.toISOString() };
          });
        }
      }
    })();
    for (const range of ranges) {
      void prefetchEvents(queryClient, range.from, range.to, overlayEmails);
    }
  }, [isLoading, viewMode, selectedDate, overlayEmails, queryClient]);

  // Show the skeleton only when there is genuinely nothing to show for the
  // current date range — the first load, or navigating to a range we have not
  // fetched yet. Crucially, do NOT show it when only the *set* of calendars
  // changes (adding/removing a feed or person overlay). Those swaps change the
  // query key, so `keepPreviousData` keeps the user's existing events on screen
  // as placeholder data; flashing a skeleton over them is the bug. Instead we
  // keep the events visible and let the refreshed set merge in. We track the
  // last date range we settled real (non-placeholder) data for, so a skeleton
  // only appears when the range itself differs.
  const rangeKey = `${from}|${to}`;
  const settledRangeRef = useRef<string | null>(null);
  useEffect(() => {
    if (!isLoading && !isPlaceholderData) {
      settledRangeRef.current = rangeKey;
    }
  }, [isLoading, isPlaceholderData, rangeKey]);
  const eventsLoading = shouldShowEventsSkeleton({
    isLoading,
    isPlaceholderData,
    settledRangeKey: settledRangeRef.current,
    rangeKey,
  });
  // A quiet background refresh is in flight (e.g. a newly added calendar's
  // events are still loading) while the existing events stay visible. Drives a
  // small non-blocking spinner instead of hiding everything behind a skeleton.
  const eventsRefreshing = isFetching && !eventsLoading;

  // Apply overlay ownership markers and filter hidden calendars
  const events = useMemo(() => {
    const ownerMap = new Map(overlayPeople.map((p) => [p.email, p]));
    const sourceEvents = draftEvent
      ? [...rawEvents.filter((e) => e.id !== draftEvent.id), draftEvent]
      : rawEvents;
    return sourceEvents
      .map((e) => {
        if (e.overlayEmail && ownerMap.has(e.overlayEmail)) {
          const owner = ownerMap.get(e.overlayEmail);
          return {
            ...e,
            ownerColor: owner?.color,
            ownerName: owner?.name,
          };
        }
        const tempId = quickEditTempIds[e.id];
        return tempId && !e._tempId ? { ...e, _tempId: tempId } : e;
      })
      .filter((e) => {
        // Hide events from hidden people overlays
        if (e.overlayEmail && hiddenCalendars.people.includes(e.overlayEmail))
          return false;
        // Hide events from hidden Google accounts
        if (
          e.accountEmail &&
          !e.overlayEmail &&
          hiddenCalendars.accounts.includes(e.accountEmail)
        )
          return false;
        // Hide events from hidden external calendars
        if (e.source === "ical") {
          const hiddenMatch = hiddenCalendars.external.some((calId) =>
            e.id.startsWith(`ical-${calId}-`),
          );
          if (hiddenMatch) return false;
        }
        return true;
      });
  }, [rawEvents, draftEvent, overlayPeople, hiddenCalendars, quickEditTempIds]);

  // Filter events for day view — use overlap check so multi-day continuation
  // events (started on a prior day) still appear on the selected day.
  const dayEvents = useMemo(
    () =>
      viewMode === "day"
        ? events.filter((e) => {
            const evStart = parseISO(e.start);
            const evEnd = parseISO(e.end);
            const dayStart = startOfDay(selectedDate);
            const dayEnd = addDays(dayStart, 1);
            return evStart < dayEnd && evEnd > dayStart;
          })
        : events,
    [events, viewMode, selectedDate],
  );
  const openNotificationEvent = useCallback(
    (event: CalendarEvent) => {
      setSelectedDate(parseISO(event.start));
      setViewMode("day");
      setSidebarEvent(event);
      setFocusedEvent(event);
    },
    [setFocusedEvent, setSelectedDate, setSidebarEvent, setViewMode],
  );
  useMeetingStartNotifications(events, openNotificationEvent);

  useEffect(() => {
    if (!eventDraft) {
      openedDraftIdRef.current = null;
      return;
    }
    if (openedDraftIdRef.current === eventDraft.id) return;
    openedDraftIdRef.current = eventDraft.id;

    const { start } = draftRange(eventDraft, selectedDate);
    setSelectedDate(start);
    if (isMobile || viewMode === "month") {
      setViewMode("day");
    }
    setCreateDefaultStart(undefined);
    setCreateDefaultEnd(undefined);
    setCreateDialogOpen(false);
    setEventDetailSidebar(false);
    setSidebarEvent(null);
    setQuickEditEventId(calendarDraftEventId(eventDraft.id));
  }, [
    eventDraft,
    isMobile,
    selectedDate,
    setEventDetailSidebar,
    setSelectedDate,
    setSidebarEvent,
    setViewMode,
    viewMode,
  ]);

  const createDraftEvent = useCallback(
    (eventId: string, pendingPatch?: DraftEventPatch) => {
      const draftId = calendarDraftIdFromEventId(eventId);
      if (!draftId || !eventDraft || eventDraft.id !== draftId) return;
      if (committingDraftIdsRef.current.has(draftId)) return;
      committingDraftIdsRef.current.add(draftId);
      const pendingDraft = pendingPatch
        ? applyDraftPatch(eventDraft, pendingPatch)
        : eventDraft;
      const accountEmail = resolveEventAccountEmail(
        googleStatus.data?.accounts ?? [],
        pendingDraft.accountEmail,
      );
      if (!accountEmail) {
        committingDraftIdsRef.current.delete(draftId);
        toast.error(t("calendarView.calendarSettingsLoading"));
        return;
      }
      const draft =
        pendingDraft.accountEmail === accountEmail
          ? pendingDraft
          : { ...pendingDraft, accountEmail };
      discardedCommittingDraftsRef.current.delete(draftId);
      if (pendingPatch || draft !== pendingDraft) {
        setEventDraft(draft);
        persistCalendarDraft(draft);
      }
      const trimmedTitle = draft.title?.trim();
      const title =
        trimmedTitle && trimmedTitle !== "(No title)" ? trimmedTitle : "";
      if (!title && !isSlotDraftId(draftId)) {
        committingDraftIdsRef.current.delete(draftId);
        toast.error(t("calendarView.addTitleBeforeCreate"));
        return;
      }

      const { start, end } = draftRange(draft, selectedDate);
      if (end.getTime() <= start.getTime()) {
        committingDraftIdsRef.current.delete(draftId);
        toast.error(t("calendarView.endTimeAfterStart"));
        return;
      }

      const eventType = draft.eventType ?? "default";
      const location = draft.location ?? draft.workingLocationLabel ?? "";
      const timezone = draft.startTimeZone ?? getLocalTimezone();
      const statusPatch =
        eventType === "default"
          ? {}
          : {
              eventType,
              workingLocationType:
                draft.workingLocationType ?? "customLocation",
              workingLocationLabel:
                (draft.workingLocationType ?? "customLocation") ===
                "customLocation"
                  ? location
                  : draft.workingLocationLabel,
            };

      const payload: Parameters<typeof createEvent.mutate>[0] = {
        _tempId: eventId,
        title,
        description: draft.description ?? "",
        start: start.toISOString(),
        end: end.toISOString(),
        startTimeZone: draft.allDay ? undefined : timezone,
        endTimeZone: draft.allDay
          ? undefined
          : (draft.endTimeZone ?? draft.startTimeZone ?? timezone),
        location,
        accountEmail,
        allDay: draft.allDay ?? false,
        transparency:
          eventType === "workingLocation"
            ? "transparent"
            : eventType === "default"
              ? draft.transparency
              : "opaque",
        visibility:
          eventType === "workingLocation" ? "public" : draft.visibility,
        reminders: draft.reminders,
        remindersUseDefault: draft.remindersUseDefault,
        ...statusPatch,
        addGoogleMeet: draft.addGoogleMeet,
        addZoom: draft.addZoom,
        color: draft.colorId
          ? getGoogleEventColorHex(draft.colorId)
          : undefined,
        colorId: draft.colorId,
        recurrence: draft.recurrence,
        attachments: draft.attachments,
        attendees: draft.attendees,
      };

      deletePersistedCalendarDraft(draftId);
      setEventDraft(null);
      setQuickEditEventId(null);
      createEvent.mutate(payload, {
        onSuccess: (result) => {
          discardedCommittingDraftsRef.current.delete(draftId);
          const createdEventId = result?.id;
          if (createdEventId) {
            const undo = () => {
              deleteEvent.mutate(
                buildDeleteEventMutationInput(
                  {
                    id: createdEventId,
                    accountEmail:
                      result.accountEmail ??
                      draft.accountEmail ??
                      defaultAccountEmail,
                  },
                  { scope: "single", sendUpdates: "none" },
                ),
              );
            };
            setUndoAction(undo);
          }
        },
        onError: (error) => {
          const restoreDraft =
            discardedCommittingDraftsRef.current.get(draftId) ?? draft;
          if (restoreDraft) {
            discardedCommittingDraftsRef.current.delete(draftId);
            persistCalendarDraft(restoreDraft);
            setEventDraft(restoreDraft);
            setQuickEditEventId(calendarDraftEventId(draftId));
          }
          toast.error(
            error instanceof Error
              ? error.message
              : t("eventForm.createFailed"),
          );
        },
        onSettled: () => {
          committingDraftIdsRef.current.delete(draftId);
        },
      });
    },
    [
      createEvent,
      defaultAccountEmail,
      deleteEvent,
      eventDraft,
      googleStatus.data?.accounts,
      selectedDate,
      setEventDraft,
      t,
    ],
  );

  const updateDraftEvent = useCallback(
    (eventId: string, patch: DraftEventPatch) => {
      const draftId = calendarDraftIdFromEventId(eventId);
      if (!draftId || !eventDraft || eventDraft.id !== draftId) return null;

      const nextDraft = applyDraftPatch(eventDraft, patch);
      setEventDraft(nextDraft);
      persistCalendarDraft(nextDraft);
      return nextDraft;
    },
    [eventDraft, setEventDraft],
  );

  const discardDraftEvent = useCallback(
    (eventId: string) => {
      const draftId = calendarDraftIdFromEventId(eventId);
      if (!draftId || !eventDraft || eventDraft.id !== draftId) return;
      if (committingDraftIdsRef.current.has(draftId)) {
        discardedCommittingDraftsRef.current.set(draftId, eventDraft);
        committingDraftIdsRef.current.delete(draftId);
      } else {
        discardedCommittingDraftsRef.current.delete(draftId);
      }
      deletePersistedCalendarDraft(draftId);
      setEventDraft(null);
      setQuickEditEventId(null);
      if (sidebarEvent?.id === eventId) setSidebarEvent(null);
      if (focusedEvent?.id === eventId) setFocusedEvent(null);
    },
    [
      eventDraft,
      focusedEvent,
      setEventDraft,
      setFocusedEvent,
      setSidebarEvent,
      sidebarEvent,
    ],
  );

  useEffect(() => {
    if (sidebarEvent) {
      const rebound = findEventByCurrentOrReplacedId(events, sidebarEvent.id);
      if (rebound && rebound.id !== sidebarEvent.id) setSidebarEvent(rebound);
    }
    if (focusedEvent) {
      const rebound = findEventByCurrentOrReplacedId(events, focusedEvent.id);
      if (rebound && rebound.id !== focusedEvent.id) setFocusedEvent(rebound);
    }
  }, [events, focusedEvent, setFocusedEvent, setSidebarEvent, sidebarEvent]);

  const selectedEvent = useMemo(() => {
    const candidate = sidebarEvent ?? focusedEvent;
    if (!candidate) return null;
    return findEventByCurrentOrReplacedId(events, candidate.id) ?? candidate;
  }, [events, sidebarEvent, focusedEvent]);

  const refreshedSidebarEvent = useMemo(() => {
    if (!sidebarEvent) return null;
    return (
      findEventByCurrentOrReplacedId(events, sidebarEvent.id) ?? sidebarEvent
    );
  }, [events, sidebarEvent]);

  function handleNavigate(direction: "prev" | "next") {
    const fns =
      direction === "next"
        ? { month: addMonths, week: addWeeks, day: addDays }
        : { month: subMonths, week: subWeeks, day: subDays };
    setSelectedDate(fns[viewMode](selectedDate, 1));
  }

  function handleToday() {
    setSelectedDate(new Date());
  }

  const handleDateSelect = useCallback(
    (date: Date) => {
      setSelectedDate(date);
      if (viewMode === "month") {
        setViewMode("day");
      }
    },
    [viewMode, setSelectedDate, setViewMode],
  );

  function handleGoToDate(date: Date) {
    setSelectedDate(date);
    setViewMode("day");
  }

  function handleOpenSelectedEventInGoogleCalendar(event: CalendarEvent) {
    if (!event.htmlLink) {
      toast.error(t("calendarView.googleCalendarLinkUnavailable"));
      return;
    }

    try {
      const url = new URL(event.htmlLink);
      const isGoogleCalendarUrl =
        url.protocol === "https:" &&
        (url.hostname === "calendar.google.com" ||
          (url.hostname === "www.google.com" &&
            url.pathname.startsWith("/calendar/")));

      if (!isGoogleCalendarUrl) {
        toast.error(t("calendarView.googleCalendarLinkUnavailable"));
        return;
      }

      const opened = window.open(
        url.toString(),
        "_blank",
        "noopener,noreferrer",
      );
      if (!opened) {
        window.location.assign(url.toString());
      }
    } catch {
      toast.error(t("calendarView.googleCalendarLinkUnavailable"));
    }
  }

  const handleDirectDelete = useCallback(
    async (
      ev: CalendarEvent,
      notificationOptions?: {
        sendUpdates: "all" | "none";
        notificationMessage?: string;
      },
    ) => {
      const isOrganizer =
        ev.organizer?.self ||
        ev.attendees?.find((a) => a.self)?.organizer ||
        !ev.attendees?.length;
      const hasOtherAttendees =
        ev.attendees && ev.attendees.filter((a) => !a.self).length > 0;
      const removeOnly = !isOrganizer && !!hasOtherAttendees;
      const shouldAskGuests = !removeOnly && shouldPromptGuests(ev);
      const guestNotification =
        notificationOptions ??
        (shouldAskGuests
          ? await promptGuestNotification({
              event: ev,
              action: "cancellation",
            })
          : { sendUpdates: "none" as const });
      if (!guestNotification) return;

      // Snapshot for undo — preserve all event fields so undo recreates faithfully
      const { id: _id, source: _source, ...snapshot } = ev;
      // removeOnly means the current user was only an attendee, not the
      // organizer — the event still exists for everyone else. Undo must
      // re-accept the existing event rather than fabricate a new one the
      // user doesn't own.
      const undo = removeOnly
        ? () => {
            rsvpEvent.mutate(
              {
                id: ev.id,
                status: "accepted",
                accountEmail: ev.accountEmail,
                sendUpdates: "none",
              },
              {
                onError: () =>
                  toast.error(t("calendarView.failedRestoreAttendance")),
              },
            );
          }
        : () => {
            createEvent.mutate(snapshot);
          };

      deleteEvent.mutate(
        buildDeleteEventMutationInput(ev, {
          scope: "single",
          ...guestNotification,
          removeOnly,
        }),
        {
          onSuccess: () => {
            if (sidebarEvent?.id === ev.id) setSidebarEvent(null);
            setUndoAction(undo);
            toast(
              removeOnly
                ? t("calendarView.eventRemoved")
                : t("calendarView.eventDeleted"),
              {
                action: { label: t("calendarView.undo"), onClick: undo },
              },
            );
          },
          onError: () => toast.error(t("calendarView.failedDeleteEvent")),
        },
      );
    },
    [
      createEvent,
      deleteEvent,
      promptGuestNotification,
      rsvpEvent,
      setSidebarEvent,
      sidebarEvent,
      t,
    ],
  );

  const handleDeleteEvent = useCallback(
    (eventId: string) => {
      if (calendarDraftIdFromEventId(eventId)) {
        discardDraftEvent(eventId);
        return;
      }
      const ev = events.find((e) => e.id === eventId);
      if (!ev) return;
      const isRecurring = !!(ev.recurringEventId || ev.recurrence?.length);
      const isOrganizer =
        ev.organizer?.self ||
        ev.attendees?.find((a) => a.self)?.organizer ||
        !ev.attendees?.length;
      const hasOtherAttendees =
        ev.attendees && ev.attendees.filter((a) => !a.self).length > 0;
      const removeOnly = !isOrganizer && !!hasOtherAttendees;
      if (isRecurring || (!removeOnly && shouldPromptGuests(ev))) {
        setDeleteDialogEvent(ev);
      } else {
        void handleDirectDelete(ev);
      }
    },
    [discardDraftEvent, events, handleDirectDelete],
  );

  // Move event to a new date (drag-and-drop from MonthView)
  async function handleEventDrop(eventId: string, newDate: Date) {
    const event = events.find((e) => e.id === eventId);
    if (!event) return;

    if (calendarDraftIdFromEventId(eventId)) {
      const originalStart = parseISO(event.start);
      const originalEnd = parseISO(event.end);
      const newStart = new Date(originalStart);
      const newEnd = new Date(originalEnd);
      newStart.setFullYear(
        newDate.getFullYear(),
        newDate.getMonth(),
        newDate.getDate(),
      );
      newEnd.setFullYear(
        newDate.getFullYear(),
        newDate.getMonth(),
        newDate.getDate(),
      );
      updateDraftEvent(eventId, {
        start: newStart.toISOString(),
        end: newEnd.toISOString(),
      });
      return;
    }

    const oldStartISO = event.start;
    const oldEndISO = event.end;
    const originalStart = parseISO(event.start);
    const originalEnd = parseISO(event.end);
    const newStart = new Date(originalStart);
    const newEnd = new Date(originalEnd);

    newStart.setFullYear(
      newDate.getFullYear(),
      newDate.getMonth(),
      newDate.getDate(),
    );
    newEnd.setFullYear(
      newDate.getFullYear(),
      newDate.getMonth(),
      newDate.getDate(),
    );

    // Guard against a zero/negative duration reaching the server (e.g. a
    // DST transition collapsing a short event's start/end onto each other).
    if (newEnd.getTime() <= newStart.getTime()) return;

    const updates = {
      start: newStart.toISOString(),
      end: newEnd.toISOString(),
    };
    const isRecurring = isRecurringCalendarEvent(event);
    const guestNotification = await promptGuestNotification({
      event,
      action: "update",
      updates,
      recurrenceScope: isRecurring,
    });
    if (!guestNotification) return;

    const undoScope = guestNotification.scope;
    const undo = () => {
      updateEvent.mutate({
        id: eventId,
        accountEmail: event.accountEmail,
        start: oldStartISO,
        end: oldEndISO,
        sendUpdates: "none",
        ...updateScopePayload(undoScope),
      });
    };
    const toastId = toast.loading(
      isRecurring
        ? t("calendarView.updatingRecurringEvent")
        : t("calendarView.movingEvent"),
    );

    updateEvent.mutate(
      {
        id: eventId,
        accountEmail: event.accountEmail,
        ...updates,
        ...guestNotification,
      },
      {
        onSuccess: () => {
          setUndoAction(undo);
          toast.success(t("calendarView.eventMoved"), {
            id: toastId,
            action: { label: t("calendarView.undo"), onClick: undo },
          });
        },
        onError: () =>
          toast.error(t("calendarView.failedMoveEvent"), { id: toastId }),
      },
    );
  }

  // Move/resize event to new start/end times (drag from Week/Day views)
  const handleEventTimeChange = useCallback(
    async (eventId: string, newStart: Date, newEnd: Date) => {
      // Skip no-op drags (dropped back in same spot)
      const event = events.find((e) => e.id === eventId);
      if (!event) return;

      // Guard against a zero/negative duration reaching the server —
      // gesture math should already prevent this, but never commit it.
      if (newEnd.getTime() <= newStart.getTime()) return;

      if (calendarDraftIdFromEventId(eventId)) {
        const timezone = settings?.timezone || getLocalTimezone();
        updateDraftEvent(eventId, {
          start: newStart.toISOString(),
          end: newEnd.toISOString(),
          allDay: false,
          startTimeZone: timezone,
          endTimeZone: timezone,
        });
        return;
      }

      const oldStart = parseISO(event.start).getTime();
      const oldEnd = parseISO(event.end).getTime();
      if (oldStart === newStart.getTime() && oldEnd === newEnd.getTime()) {
        return;
      }

      const oldStartISO = event.start;
      const oldEndISO = event.end;
      const updates = {
        start: newStart.toISOString(),
        end: newEnd.toISOString(),
      };
      const isRecurring = isRecurringCalendarEvent(event);
      const guestNotification = await promptGuestNotification({
        event,
        action: "update",
        updates,
        recurrenceScope: isRecurring,
      });
      if (!guestNotification) return;

      const undoScope = guestNotification.scope;
      const undo = () => {
        updateEvent.mutate({
          id: eventId,
          accountEmail: event.accountEmail,
          start: oldStartISO,
          end: oldEndISO,
          sendUpdates: "none",
          ...updateScopePayload(undoScope),
        });
      };
      const toastId = toast.loading(
        isRecurring
          ? t("calendarView.updatingRecurringEvent")
          : t("calendarView.updatingEvent"),
      );

      updateEvent.mutate(
        {
          id: eventId,
          accountEmail: event.accountEmail,
          ...updates,
          ...guestNotification,
        },
        {
          onSuccess: () => {
            setUndoAction(undo);
            toast.success(t("calendarView.eventUpdated"), {
              id: toastId,
              action: { label: t("calendarView.undo"), onClick: undo },
            });
          },
          onError: () =>
            toast.error(t("calendarView.failedUpdateEvent"), { id: toastId }),
        },
      );
    },
    [
      events,
      settings,
      updateDraftEvent,
      promptGuestNotification,
      updateEvent,
      t,
    ],
  );

  const handleClickTimeSlot = useCallback(
    async (
      clickedDate: Date,
      startTime: string,
      endTime: string,
      options?: { explicitDuration?: boolean },
    ) => {
      let activeSettings = settings;
      if (!activeSettings) {
        const result = await settingsQuery.refetch();
        activeSettings = result.data;
      }
      if (!activeSettings?.timezone) {
        toast.error(t("calendarView.calendarSettingsLoading"));
        return;
      }

      setSelectedDate(clickedDate);
      const defaultDuration = Math.max(
        5,
        activeSettings.defaultEventDuration ?? 30,
      );
      const timezone = activeSettings.timezone;
      setCreateDefaultStart(startTime);
      setCreateDialogOpen(false);

      const dateStr = format(clickedDate, "yyyy-MM-dd");
      // A drag-to-create gesture already computed the exact dragged range;
      // a plain click falls back to the user's configured default duration.
      const end = options?.explicitDuration
        ? { date: dateStr, time: endTime }
        : addMinutesToDateTimeParts(dateStr, startTime, defaultDuration);
      setCreateDefaultEnd(end.time);
      const startISO = dateTimeInTimezoneToIso(dateStr, startTime, timezone);
      const endISO = dateTimeInTimezoneToIso(end.date, end.time, timezone);
      const now = new Date().toISOString();
      const draftId = `slot-${Date.now()}`;
      const draft: CalendarEventDraft = {
        id: draftId,
        title: "",
        description: "",
        location: "",
        start: startISO,
        end: endISO,
        startTimeZone: timezone,
        endTimeZone: timezone,
        allDay: false,
        eventType: "default",
        accountEmail: defaultAccountEmail,
        createdAt: now,
        updatedAt: now,
      };

      persistCalendarDraft(draft);
      setEventDraft(draft);
      setQuickEditEventId(calendarDraftEventId(draftId));
    },
    [
      defaultAccountEmail,
      settings,
      settingsQuery,
      t,
      setSelectedDate,
      setEventDraft,
    ],
  );

  // Command palette natural-language quick create (e.g. "lunch with Sam
  // tomorrow 12:30") — builds a prefilled draft and jumps to it, reusing the
  // same draft/quick-edit flow as clicking a time slot.
  const handleCreateEventFromText = useCallback(
    async (quickCreate: QuickCreateEvent) => {
      let activeSettings = settings;
      if (!activeSettings) {
        const result = await settingsQuery.refetch();
        activeSettings = result.data;
      }
      if (!activeSettings?.timezone) {
        toast.error(t("calendarView.calendarSettingsLoading"));
        return;
      }

      const timezone = activeSettings.timezone;
      const defaultDuration = Math.max(
        5,
        activeSettings.defaultEventDuration ?? 30,
      );
      const startTime = quickCreate.hasExplicitTime
        ? format(quickCreate.start, "HH:mm")
        : "09:00";
      const dateStr = format(quickCreate.start, "yyyy-MM-dd");
      const end = addMinutesToDateTimeParts(
        dateStr,
        startTime,
        defaultDuration,
      );
      const startISO = dateTimeInTimezoneToIso(dateStr, startTime, timezone);
      const endISO = dateTimeInTimezoneToIso(end.date, end.time, timezone);

      setSelectedDate(quickCreate.start);
      setViewMode("day");
      setCreateDefaultStart(startTime);
      setCreateDefaultEnd(end.time);
      setCreateDialogOpen(false);

      const now = new Date().toISOString();
      const draftId = `slot-${Date.now()}`;
      const draft: CalendarEventDraft = {
        id: draftId,
        title: quickCreate.title,
        description: "",
        location: "",
        start: startISO,
        end: endISO,
        startTimeZone: timezone,
        endTimeZone: timezone,
        allDay: false,
        eventType: "default",
        accountEmail: defaultAccountEmail,
        createdAt: now,
        updatedAt: now,
      };

      persistCalendarDraft(draft);
      setEventDraft(draft);
      setQuickEditEventId(calendarDraftEventId(draftId));
    },
    [
      defaultAccountEmail,
      settings,
      settingsQuery,
      t,
      setSelectedDate,
      setViewMode,
      setEventDraft,
    ],
  );

  const handleQuickEditSave = useCallback(
    async (eventId: string, title: string, accountEmail?: string) => {
      setQuickEditEventId(null);
      if (calendarDraftIdFromEventId(eventId)) {
        updateDraftEvent(eventId, { title: title.trim() });
        return;
      }
      setQuickEditTempIds((current) => {
        if (!current[eventId]) return current;
        const { [eventId]: _removed, ...next } = current;
        return next;
      });
      if (title.trim() && title.trim() !== "(No title)") {
        const event = events.find((e) => e.id === eventId);
        const updates = { title: title.trim() };
        const guestNotification = event
          ? await promptGuestNotification({
              event,
              action: "update",
              updates,
            })
          : { sendUpdates: "none" as const };
        if (!guestNotification) return;
        updateEvent.mutate({
          id: eventId,
          accountEmail: event?.accountEmail ?? accountEmail,
          ...updates,
          ...guestNotification,
        });
      }
    },
    [events, updateDraftEvent, promptGuestNotification, updateEvent],
  );

  const handleTitleSave = useCallback(
    async (eventId: string, title: string, accountEmail?: string) => {
      if (calendarDraftIdFromEventId(eventId)) {
        updateDraftEvent(eventId, { title });
        return;
      }
      const event = events.find((e) => e.id === eventId);
      const updates = { title };
      const guestNotification = event
        ? await promptGuestNotification({
            event,
            action: "update",
            updates,
          })
        : { sendUpdates: "none" as const };
      if (!guestNotification) return;
      updateEvent.mutate({
        id: eventId,
        accountEmail: event?.accountEmail ?? accountEmail,
        ...updates,
        ...guestNotification,
      });
    },
    [events, updateDraftEvent, promptGuestNotification, updateEvent],
  );

  const handleQuickEditCancel = useCallback(
    (eventId: string, accountEmail?: string) => {
      setQuickEditEventId(null);
      if (calendarDraftIdFromEventId(eventId)) {
        discardDraftEvent(eventId);
        return;
      }
      setQuickEditTempIds((current) => {
        if (!current[eventId]) return current;
        const { [eventId]: _removed, ...next } = current;
        return next;
      });
      // Delete the event if title was never set
      const ev = events.find((e) => e.id === eventId);
      if (!ev || ev.title === "(No title)") {
        deleteEvent.mutate(
          buildDeleteEventMutationInput(
            {
              id: eventId,
              accountEmail:
                ev?.accountEmail ?? accountEmail ?? defaultAccountEmail,
            },
            {
              scope: "single",
              sendUpdates: "none",
            },
          ),
        );
      }
    },
    [defaultAccountEmail, discardDraftEvent, events, deleteEvent],
  );

  // IconKeyboard shortcuts — don't fire when user is typing in an input
  const isTypingInInput = useCallback((e: KeyboardEvent) => {
    const target = e.target as HTMLElement;
    return (
      target.tagName === "INPUT" ||
      target.tagName === "TEXTAREA" ||
      target.isContentEditable
    );
  }, []);

  useEffect(() => {
    function handleKeyDown(e: KeyboardEvent) {
      // Cmd+K / Ctrl+K — always open command palette
      if ((e.metaKey || e.ctrlKey) && e.key === "k") {
        e.preventDefault();
        setCommandPaletteOpen(true);
        return;
      }

      // Skip all other shortcuts when typing or when a dialog is open
      if (isTypingInInput(e)) return;
      if (createDialogOpen || deleteDialogEvent) return;

      // Delete/Backspace — delete the selected event
      if (e.key === "Delete" || e.key === "Backspace") {
        const targetEvent = sidebarEvent || focusedEvent;
        if (!targetEvent) return;
        e.preventDefault();
        handleDeleteEvent(targetEvent.id);
        return;
      }

      // Don't intercept keyboard shortcuts with modifier keys (Cmd+C, Ctrl+V, etc.)
      if (e.metaKey || e.ctrlKey || e.altKey) return;

      // `?` / shift+/ opens the keyboard shortcuts help — that listener now
      // lives in AppLayout so it works on every tab. Don't double-handle here.

      // Arrow keys navigate the calendar grid — never steal them from list
      // navigation inside the command palette or other open dialogs.
      const isArrowKey =
        e.key === "ArrowLeft" ||
        e.key === "ArrowRight" ||
        e.key === "ArrowUp" ||
        e.key === "ArrowDown";
      if (
        isArrowKey &&
        (commandPaletteOpen || peopleSearchOpen || addCalendarOpen)
      ) {
        return;
      }

      switch (e.key) {
        case "z":
          e.preventDefault();
          runUndo();
          break;
        case "j":
        case "ArrowRight":
          e.preventDefault();
          handleNavigate("next");
          break;
        case "k":
        case "ArrowLeft":
          e.preventDefault();
          handleNavigate("prev");
          break;
        case "ArrowDown":
          e.preventDefault();
          setSelectedDate(
            viewMode === "month"
              ? addWeeks(selectedDate, 1)
              : addDays(selectedDate, 1),
          );
          break;
        case "ArrowUp":
          e.preventDefault();
          setSelectedDate(
            viewMode === "month"
              ? subWeeks(selectedDate, 1)
              : subDays(selectedDate, 1),
          );
          break;
        case "p":
          e.preventDefault();
          setPeopleSearchOpen(true);
          break;
        case "t":
          handleToday();
          break;
        case "m":
          setViewMode("month");
          break;
        case "w":
          setViewMode("week");
          break;
        case "d":
          setViewMode("day");
          break;
        case "c":
          e.preventDefault();
          setEventDraft(null);
          setCreateDefaultStart(undefined);
          setCreateDefaultEnd(undefined);
          setCreateDialogOpen(true);
          break;
        case "/":
          e.preventDefault();
          setCommandPaletteOpen(true);
          break;
      }
    }

    window.addEventListener("keydown", handleKeyDown);
    return () => window.removeEventListener("keydown", handleKeyDown);
  }, [
    createDialogOpen,
    deleteDialogEvent,
    isTypingInInput,
    viewMode,
    selectedDate,
    sidebarEvent,
    focusedEvent,
    events,
    commandPaletteOpen,
    peopleSearchOpen,
    addCalendarOpen,
  ]);

  const headerLabel = (() => {
    switch (viewMode) {
      case "month":
        return isMobile
          ? format(selectedDate, "MMM yyyy")
          : format(selectedDate, "MMMM yyyy");
      case "week": {
        const ws = startOfWeek(selectedDate);
        const we = endOfWeek(selectedDate);
        return isMobile
          ? `${format(ws, "MMM d")} – ${format(we, "d")}`
          : `${format(ws, "MMM d")} – ${format(we, "d, yyyy")}`;
      }
      case "day":
        return isMobile
          ? format(selectedDate, "EEE, MMM d")
          : format(selectedDate, "EEEE, MMMM d, yyyy");
    }
  })();

  return (
    <TooltipProvider delayDuration={500}>
      <div className="flex h-full min-w-0">
        {/* Left: calendar area (header + grid) */}
        <div className="flex min-w-0 flex-1 flex-col overflow-hidden">
          {/* Google Calendar connect banner — show when there's a credentials error */}
          {eventsError ? <GoogleConnectBanner /> : null}

          {/* Error detail */}
          {eventsError && (
            <div className="shrink-0 border-b border-destructive/20 bg-destructive/[0.06] px-4 py-1.5 text-xs text-destructive/70">
              {eventsError.message}
            </div>
          )}

          {/* Top bar */}
          <div className="flex h-12 shrink-0 items-center gap-1 border-b border-border px-2 sm:gap-3 sm:px-3">
            {/* Left: view mode dropdown */}
            <div className="flex shrink-0 items-center gap-1">
              <Tooltip>
                <TooltipTrigger asChild>
                  <Button
                    variant="ghost"
                    size="icon"
                    className="h-8 w-8 lg:hidden"
                    onClick={openSidebar}
                    aria-label={t("calendarView.openNavigation")}
                  >
                    <IconMenu2 className="h-4 w-4" />
                  </Button>
                </TooltipTrigger>
                <TooltipContent side="bottom">
                  <p>{t("calendarView.openNavigation")}</p>
                </TooltipContent>
              </Tooltip>
              <DropdownMenu>
                <DropdownMenuTrigger asChild>
                  <Button
                    variant="ghost"
                    size="sm"
                    className="h-8 gap-1 px-2 text-sm font-semibold sm:px-2.5"
                  >
                    {viewModeLabels[viewMode]}
                    <IconChevronDown className="h-3.5 w-3.5 text-muted-foreground" />
                  </Button>
                </DropdownMenuTrigger>
                <DropdownMenuContent align="start">
                  <DropdownMenuItem onClick={() => setViewMode("day")}>
                    {t("calendarView.day")}
                    <kbd className="ml-auto text-[10px] text-muted-foreground">
                      D
                    </kbd>
                  </DropdownMenuItem>
                  <DropdownMenuItem onClick={() => setViewMode("week")}>
                    {t("calendarView.week")}
                    <kbd className="ml-auto text-[10px] text-muted-foreground">
                      W
                    </kbd>
                  </DropdownMenuItem>
                  <DropdownMenuItem onClick={() => setViewMode("month")}>
                    {t("calendarView.month")}
                    <kbd className="ml-auto text-[10px] text-muted-foreground">
                      M
                    </kbd>
                  </DropdownMenuItem>
                  <DropdownMenuSeparator />
                  <DropdownMenuLabel className="text-[10px] font-normal uppercase tracking-wider text-muted-foreground">
                    {t("calendarView.display")}
                  </DropdownMenuLabel>
                  <DropdownMenuItem
                    onSelect={(e) => {
                      e.preventDefault();
                      setViewPrefs({ hideWeekends: !viewPrefs.hideWeekends });
                    }}
                  >
                    {t("calendarView.hideWeekends")}
                    {viewPrefs.hideWeekends && (
                      <IconCheck className="ml-auto h-3.5 w-3.5 text-muted-foreground" />
                    )}
                  </DropdownMenuItem>
                </DropdownMenuContent>
              </DropdownMenu>
            </div>

            {/* Center: today, nav arrows, date label */}
            <div className="flex min-w-0 flex-1 items-center justify-center gap-0.5 sm:gap-1">
              <Tooltip>
                <TooltipTrigger asChild>
                  <Button
                    variant="outline"
                    size="sm"
                    onClick={handleToday}
                    className="h-7 px-2 text-xs font-medium sm:px-2.5"
                  >
                    {t("calendarView.today")}
                  </Button>
                </TooltipTrigger>
                <TooltipContent side="bottom">
                  <p>
                    {t("eventForm.goToToday")}{" "}
                    <kbd className="ml-1 rounded border border-border bg-muted px-1 font-mono text-[10px]">
                      T
                    </kbd>
                  </p>
                </TooltipContent>
              </Tooltip>

              <Button
                variant="ghost"
                size="icon"
                onClick={() => handleNavigate("prev")}
                className="h-8 w-8 sm:h-7 sm:w-7"
              >
                <IconChevronLeft className="h-4 w-4" />
              </Button>

              <Button
                variant="ghost"
                size="icon"
                onClick={() => handleNavigate("next")}
                className="h-8 w-8 sm:h-7 sm:w-7"
              >
                <IconChevronRight className="h-4 w-4" />
              </Button>

              <span className="ml-0.5 min-w-0 flex-1 truncate whitespace-nowrap text-center text-xs font-semibold sm:ml-1 sm:text-sm">
                {headerLabel}
              </span>

              {eventsRefreshing && (
                <Spinner
                  className="ml-1 size-3.5 shrink-0 text-muted-foreground"
                  aria-label={t("calendarView.loadingCalendars")}
                />
              )}
            </div>

            {/* Right: search, new event */}
            <div className="flex shrink-0 items-center gap-0.5 sm:gap-1">
              <Tooltip>
                <TooltipTrigger asChild>
                  <Button
                    variant="ghost"
                    size="icon"
                    className="h-8 w-8 sm:h-7 sm:w-7"
                    onClick={() => setCommandPaletteOpen(true)}
                  >
                    <IconSearch className="h-4 w-4" />
                  </Button>
                </TooltipTrigger>
                <TooltipContent side="bottom">
                  <p>
                    {t("root.commandSearch")}{" "}
                    <kbd className="ml-1 rounded border border-border bg-muted px-1 font-mono text-[10px]">
                      /
                    </kbd>
                  </p>
                </TooltipContent>
              </Tooltip>

              <CreateEventPopover
                open={createDialogOpen}
                onOpenChange={(open) => {
                  setCreateDialogOpen(open);
                  if (open) {
                    setEventDraft(null);
                  }
                  if (!open) {
                    setCreateDefaultStart(undefined);
                    setCreateDefaultEnd(undefined);
                  }
                }}
                defaultDate={selectedDate}
                defaultStartTime={createDefaultStart}
                defaultEndTime={createDefaultEnd}
              />
              <AccountAvatars />
              <AgentToggleButton />
            </div>
          </div>

          {/* Calendar grid */}
          <div className="flex-1 overflow-hidden">
            {viewMode === "month" && (
              <MonthView
                events={events}
                selectedDate={selectedDate}
                onDateSelect={handleDateSelect}
                onDeleteEvent={handleDeleteEvent}
                onEventDrop={handleEventDrop}
                draftEventIds={draftEventIds}
                onDraftUpdate={updateDraftEvent}
                onDraftCreate={createDraftEvent}
                onDraftDiscard={discardDraftEvent}
                isLoading={eventsLoading}
              />
            )}
            {viewMode === "week" && (
              <WeekView
                events={events}
                selectedDate={selectedDate}
                onDateSelect={handleDateSelect}
                onDeleteEvent={handleDeleteEvent}
                onEventTimeChange={handleEventTimeChange}
                onClickTimeSlot={handleClickTimeSlot}
                quickEditEventId={quickEditEventId}
                onQuickEditSave={handleQuickEditSave}
                onQuickEditCancel={handleQuickEditCancel}
                draftEventIds={draftEventIds}
                onDraftUpdate={updateDraftEvent}
                onDraftCreate={createDraftEvent}
                onDraftDiscard={discardDraftEvent}
                isLoading={eventsLoading}
              />
            )}
            {viewMode === "day" && (
              <DayView
                events={dayEvents}
                date={selectedDate}
                onDeleteEvent={handleDeleteEvent}
                onEventTimeChange={handleEventTimeChange}
                onClickTimeSlot={handleClickTimeSlot}
                quickEditEventId={quickEditEventId}
                onQuickEditSave={handleQuickEditSave}
                onQuickEditCancel={handleQuickEditCancel}
                draftEventIds={draftEventIds}
                onDraftUpdate={updateDraftEvent}
                onDraftCreate={createDraftEvent}
                onDraftDiscard={discardDraftEvent}
                isLoading={eventsLoading}
              />
            )}
          </div>
        </div>

        {/* Event detail sidebar — full height, outside the calendar column */}
        {eventDetailSidebar && (
          <EventDetailPanel
            event={refreshedSidebarEvent}
            onClose={() => setSidebarEvent(null)}
            onDelete={handleDeleteEvent}
            onTitleSave={handleTitleSave}
          />
        )}

        {/* Dialogs */}
        <CommandPalette
          open={commandPaletteOpen}
          onClose={() => setCommandPaletteOpen(false)}
          events={events}
          onGoToDate={handleGoToDate}
          onEventClick={(event) => {
            setCommandPaletteOpen(false);
            handleGoToDate(parseISO(event.start));
          }}
          onCreateEvent={() => {
            setCommandPaletteOpen(false);
            setEventDraft(null);
            setCreateDialogOpen(true);
          }}
          onCreateEventFromText={(quickCreate) => {
            setCommandPaletteOpen(false);
            void handleCreateEventFromText(quickCreate);
          }}
          onViewChange={setViewMode}
          onToday={handleToday}
          selectedEvent={selectedEvent}
          onOpenSelectedEventInGoogleCalendar={
            handleOpenSelectedEventInGoogleCalendar
          }
          onAddPeopleCalendar={() => {
            setCommandPaletteOpen(false);
            setAddCalendarDefaultTab("people");
            setAddCalendarOpen(true);
          }}
          onAddUrlCalendar={() => {
            setCommandPaletteOpen(false);
            setAddCalendarDefaultTab("url");
            setAddCalendarOpen(true);
          }}
        />
        <PeopleSearchDialog
          open={peopleSearchOpen}
          onOpenChange={setPeopleSearchOpen}
        />
        <DeleteEventDialog
          event={deleteDialogEvent}
          open={deleteDialogEvent !== null}
          onClose={() => setDeleteDialogEvent(null)}
          onConfirm={(options) => {
            if (!deleteDialogEvent) return;
            const snapshot = { ...deleteDialogEvent };
            const eventId = deleteDialogEvent.id;
            const undo = () => {
              createEvent.mutate({
                title: snapshot.title,
                description: snapshot.description ?? "",
                location: snapshot.location ?? "",
                start: snapshot.start,
                end: snapshot.end,
                startTimeZone: snapshot.startTimeZone,
                endTimeZone: snapshot.endTimeZone,
                allDay: snapshot.allDay ?? false,
                color: snapshot.color,
                colorId: snapshot.colorId,
                attachments: snapshot.attachments,
                eventType: snapshot.eventType,
                transparency: snapshot.transparency,
                visibility: snapshot.visibility,
                reminders: snapshot.reminders,
                remindersUseDefault: snapshot.remindersUseDefault,
                accountEmail: snapshot.accountEmail,
                outOfOfficeProperties: snapshot.outOfOfficeProperties,
                focusTimeProperties: snapshot.focusTimeProperties,
                workingLocationProperties: snapshot.workingLocationProperties,
              });
            };
            // Optimistic: close dialog immediately
            setDeleteDialogEvent(null);
            if (sidebarEvent?.id === eventId) {
              setSidebarEvent(null);
            }
            deleteEvent.mutate(
              buildDeleteEventMutationInput(snapshot, options),
              {
                onSuccess: () => {
                  setUndoAction(undo);
                  toast(
                    options.removeOnly
                      ? t("calendarView.eventRemoved")
                      : t("calendarView.eventDeleted"),
                    {
                      action: { label: t("calendarView.undo"), onClick: undo },
                    },
                  );
                },
                onError: () => toast.error(t("calendarView.failedDeleteEvent")),
              },
            );
          }}
        />
        {guestNotificationDialog}
      </div>
    </TooltipProvider>
  );
}

function AccountAvatars() {
  const t = useT();
  const googleStatus = useGoogleAuthStatus();
  const accounts = googleStatus.data?.accounts ?? [];
  if (accounts.length === 0) return null;

  return (
    <Tooltip>
      <TooltipTrigger asChild>
        <Link
          to="/settings"
          className="flex items-center hover:opacity-90 ml-1"
          aria-label={t("calendarView.manageAccounts")}
        >
          <div className="flex items-center">
            {accounts.map((account, i) => (
              <div
                key={account.email}
                className={cn("relative rounded-full ring-2 ring-card")}
                style={{
                  marginLeft: i === 0 ? 0 : -8,
                  zIndex: accounts.length - i,
                }}
              >
                {account.photoUrl && !isMcpEmbedSurface() ? (
                  <img
                    src={account.photoUrl}
                    alt=""
                    className="h-7 w-7 rounded-full object-cover"
                    referrerPolicy="no-referrer"
                  />
                ) : (
                  // MCP host iframes (ChatGPT / Claude) ship strict COEP/CORP
                  // headers that block cross-origin googleusercontent.com
                  // avatars and produce noisy console errors. Fall back to a
                  // same-origin initial chip when embedded. See
                  // `templates/calendar/app/lib/mcp-embed.ts`.
                  <div className="flex h-7 w-7 items-center justify-center rounded-full bg-primary/20 text-[11px] font-semibold text-primary">
                    {account.email[0]?.toUpperCase()}
                  </div>
                )}
              </div>
            ))}
          </div>
        </Link>
      </TooltipTrigger>
      <TooltipContent side="bottom">
        <div className="space-y-0.5">
          {accounts.map((a) => (
            <div key={a.email} className="text-xs">
              {a.email}
            </div>
          ))}
        </div>
      </TooltipContent>
    </Tooltip>
  );
}
