import { useState, useRef, useEffect, useMemo } from 'react';
import { useLocation } from 'react-router-dom';
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import { toast } from 'sonner';
import FullCalendar from '@fullcalendar/react';
import dayGridPlugin from '@fullcalendar/daygrid';
import timeGridPlugin from '@fullcalendar/timegrid';
import interactionPlugin from '@fullcalendar/interaction';
import type { EventClickArg, DateSelectArg, EventDropArg, DatesSetArg } from '@fullcalendar/core';
import { ChevronLeft, ChevronRight, Plus, Search } from 'lucide-react';
import api, { getErrorMessage } from '@/lib/api';
import type { CalendarEvent, EventTemplate, Location as LocationType } from '@/types';
import { useCalendars } from '@/hooks/useCalendars';
import { useSettings } from '@/hooks/useSettings';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import CalendarSidebar from './CalendarSidebar';
import EventDetailPanel from './EventDetailPanel';
import type { CreateDefaults } from './EventDetailPanel';

type CalendarView = 'dayGridMonth' | 'timeGridWeek' | 'timeGridDay';

const viewLabels: Record<CalendarView, string> = {
  dayGridMonth: 'Month',
  timeGridWeek: 'Week',
  timeGridDay: 'Day',
};

export default function CalendarPage() {
  const queryClient = useQueryClient();
  const location = useLocation();
  const calendarRef = useRef<FullCalendar>(null);
  const [currentView, setCurrentView] = useState<CalendarView>('dayGridMonth');
  const [calendarTitle, setCalendarTitle] = useState('');

  const [eventSearch, setEventSearch] = useState('');
  const [searchFocused, setSearchFocused] = useState(false);

  // Panel state
  const [selectedEventId, setSelectedEventId] = useState<number | string | null>(null);
  const [panelMode, setPanelMode] = useState<'closed' | 'view' | 'create'>('closed');
  const [createDefaults, setCreateDefaults] = useState<CreateDefaults | null>(null);

  const { settings } = useSettings();
  const { data: calendars = [] } = useCalendars();
  const calendarContainerRef = useRef<HTMLDivElement>(null);

  // Location data for event panel
  const { data: locations = [] } = useQuery({
    queryKey: ['locations'],
    queryFn: async () => {
      const { data } = await api.get<LocationType[]>('/locations');
      return data;
    },
    staleTime: 5 * 60 * 1000,
  });

  const locationMap = useMemo(() => {
    const map = new Map<number, string>();
    locations.forEach((l) => map.set(l.id, l.name));
    return map;
  }, [locations]);

  // Handle navigation state from dashboard
  useEffect(() => {
    const state = location.state as { date?: string; view?: string; eventId?: number } | null;
    if (!state) return;
    const calApi = calendarRef.current?.getApi();
    if (state.date && calApi) {
      calApi.gotoDate(state.date);
      if (state.view) calApi.changeView(state.view);
    }
    if (state.eventId) {
      setSelectedEventId(state.eventId);
      setPanelMode('view');
    }
    window.history.replaceState({}, '');
  }, [location.state]);

  // Resize FullCalendar when container size changes (e.g. sidebar collapse)
  useEffect(() => {
    const el = calendarContainerRef.current;
    if (!el) return;
    const observer = new ResizeObserver(() => {
      calendarRef.current?.getApi().updateSize();
    });
    observer.observe(el);
    return () => observer.disconnect();
  }, []);

  const panelOpen = panelMode !== 'closed';

  // Continuously resize calendar during panel open/close CSS transition
  useEffect(() => {
    let rafId: number;
    const start = performance.now();
    const duration = 350; // slightly longer than the 300ms CSS transition
    const tick = () => {
      calendarRef.current?.getApi().updateSize();
      if (performance.now() - start < duration) {
        rafId = requestAnimationFrame(tick);
      }
    };
    rafId = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(rafId);
  }, [panelOpen]);

  // Scroll wheel navigation in month view
  useEffect(() => {
    const el = calendarContainerRef.current;
    if (!el) return;
    let debounceTimer: ReturnType<typeof setTimeout> | null = null;
    const handleWheel = (e: WheelEvent) => {
      const api = calendarRef.current?.getApi();
      if (!api || api.view.type !== 'dayGridMonth') return;
      e.preventDefault();
      if (debounceTimer) return;
      debounceTimer = setTimeout(() => {
        debounceTimer = null;
      }, 300);
      if (e.deltaY > 0) api.next();
      else if (e.deltaY < 0) api.prev();
    };
    el.addEventListener('wheel', handleWheel, { passive: false });
    return () => el.removeEventListener('wheel', handleWheel);
  }, []);

  const { data: events = [] } = useQuery({
    queryKey: ['calendar-events'],
    queryFn: async () => {
      const { data } = await api.get<CalendarEvent[]>('/events');
      return data;
    },
  });

  const selectedEvent = useMemo(
    () => events.find((e) => e.id === selectedEventId) ?? null,
    [selectedEventId, events],
  );

  // Escape key closes detail panel
  useEffect(() => {
    if (!panelOpen) return;
    const handler = (e: KeyboardEvent) => {
      if (e.key === 'Escape') handlePanelClose();
    };
    document.addEventListener('keydown', handler);
    return () => document.removeEventListener('keydown', handler);
  }, [panelOpen]);

  const visibleCalendarIds = useMemo(
    () => new Set(calendars.filter((c) => c.is_visible).map((c) => c.id)),
    [calendars],
  );

  const toLocalDatetime = (d: Date): string => {
    const pad = (n: number) => n.toString().padStart(2, '0');
    return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
  };

  const updateEventTimes = (
    id: number,
    start: string,
    end: string,
    allDay: boolean,
    revert: () => void,
  ) => {
    queryClient.setQueryData<CalendarEvent[]>(['calendar-events'], (old) =>
      old?.map((e) =>
        e.id === id
          ? { ...e, start_datetime: start, end_datetime: end, all_day: allDay }
          : e,
      ),
    );
    eventMutation.mutate({ id, start, end, allDay, revert });
  };

  const eventMutation = useMutation({
    mutationFn: async ({
      id,
      start,
      end,
      allDay,
    }: {
      id: number;
      start: string;
      end: string;
      allDay: boolean;
      revert: () => void;
    }) => {
      const response = await api.put(`/events/${id}`, {
        start_datetime: start,
        end_datetime: end,
        all_day: allDay,
      });
      return response.data;
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['calendar-events'] });
      toast.success('Event updated');
    },
    onError: (error, variables) => {
      variables.revert();
      queryClient.invalidateQueries({ queryKey: ['calendar-events'] });
      toast.error(getErrorMessage(error, 'Failed to update event'));
    },
  });

  const filteredEvents = useMemo(() => {
    if (calendars.length === 0) return events;
    return events.filter((e) => visibleCalendarIds.has(e.calendar_id));
  }, [events, visibleCalendarIds, calendars.length]);

  const searchResults = useMemo(() => {
    if (!eventSearch.trim()) return [];
    const q = eventSearch.toLowerCase();
    return filteredEvents
      .filter((e) => e.title.toLowerCase().includes(q))
      .slice(0, 8);
  }, [filteredEvents, eventSearch]);

  const handleSearchSelect = (event: CalendarEvent) => {
    const calApi = calendarRef.current?.getApi();
    if (!calApi) return;
    const startDate = new Date(event.start_datetime);
    calApi.gotoDate(startDate);
    if (event.all_day) {
      calApi.changeView('dayGridMonth');
    } else {
      calApi.changeView('timeGridDay');
    }
    setEventSearch('');
    setSearchFocused(false);
    // Also open the event in the panel
    if (!event.is_virtual) {
      setSelectedEventId(event.id);
      setPanelMode('view');
    }
  };

  const calendarEvents = filteredEvents.map((event) => ({
    id: String(event.id),
    title: event.title,
    start: event.start_datetime,
    end: event.end_datetime || undefined,
    allDay: event.all_day,
    backgroundColor: event.calendar_color || 'hsl(var(--accent-color))',
    borderColor: event.calendar_color || 'hsl(var(--accent-color))',
    extendedProps: {
      is_virtual: event.is_virtual,
      is_recurring: event.is_recurring,
      parent_event_id: event.parent_event_id,
    },
  }));

  const handleEventClick = (info: EventClickArg) => {
    const event = events.find((e) => String(e.id) === info.event.id);
    if (!event) return;
    if (event.is_virtual) {
      toast.info(`${event.title} — from People contacts`);
      return;
    }
    setSelectedEventId(event.id);
    setPanelMode('view');
  };

  const handleEventDrop = (info: EventDropArg) => {
    if (info.event.extendedProps.is_virtual) {
      info.revert();
      return;
    }
    if (info.event.extendedProps.is_recurring || info.event.extendedProps.parent_event_id) {
      info.revert();
      toast.info('Click the event to edit recurring events');
      return;
    }
    const id = parseInt(info.event.id);
    const start = info.event.allDay
      ? info.event.startStr
      : info.event.start
        ? toLocalDatetime(info.event.start)
        : info.event.startStr;
    const end = info.event.allDay
      ? info.event.endStr || info.event.startStr
      : info.event.end
        ? toLocalDatetime(info.event.end)
        : start;
    updateEventTimes(id, start, end, info.event.allDay, info.revert);
  };

  const handleEventResize = (info: { event: EventDropArg['event']; revert: () => void }) => {
    if (info.event.extendedProps.is_virtual) {
      info.revert();
      return;
    }
    if (info.event.extendedProps.is_recurring || info.event.extendedProps.parent_event_id) {
      info.revert();
      toast.info('Click the event to edit recurring events');
      return;
    }
    const id = parseInt(info.event.id);
    const start = info.event.allDay
      ? info.event.startStr
      : info.event.start
        ? toLocalDatetime(info.event.start)
        : info.event.startStr;
    const end = info.event.allDay
      ? info.event.endStr || info.event.startStr
      : info.event.end
        ? toLocalDatetime(info.event.end)
        : start;
    updateEventTimes(id, start, end, info.event.allDay, info.revert);
  };

  const handleDateSelect = (selectInfo: DateSelectArg) => {
    setSelectedEventId(null);
    setPanelMode('create');
    setCreateDefaults({
      start: selectInfo.startStr,
      end: selectInfo.endStr,
      allDay: selectInfo.allDay,
    });
  };

  const handleCreateNew = () => {
    setSelectedEventId(null);
    setPanelMode('create');
    setCreateDefaults(null);
  };

  const handlePanelClose = () => {
    calendarRef.current?.getApi().unselect();
    setPanelMode('closed');
    setSelectedEventId(null);
    setCreateDefaults(null);
  };

  const handleUseTemplate = (template: EventTemplate) => {
    setSelectedEventId(null);
    setPanelMode('create');
    setCreateDefaults({
      templateData: {
        title: template.title,
        description: template.description || '',
        all_day: template.all_day,
        calendar_id: template.calendar_id ?? undefined,
        location_id: template.location_id || undefined,
        is_starred: template.is_starred,
        recurrence_rule: template.recurrence_rule || undefined,
      } as Partial<CalendarEvent>,
      templateName: template.name,
    });
  };

  const handleDatesSet = (arg: DatesSetArg) => {
    setCalendarTitle(arg.view.title);
    setCurrentView(arg.view.type as CalendarView);
  };

  const navigatePrev = () => calendarRef.current?.getApi().prev();
  const navigateNext = () => calendarRef.current?.getApi().next();
  const navigateToday = () => calendarRef.current?.getApi().today();
  const changeView = (view: CalendarView) => calendarRef.current?.getApi().changeView(view);

  return (
    <div className="flex h-full overflow-hidden animate-fade-in">
      <CalendarSidebar onUseTemplate={handleUseTemplate} />

      <div ref={calendarContainerRef} className="flex-1 flex flex-col overflow-hidden">
        {/* Custom toolbar */}
        <div className="border-b bg-card px-6 h-16 flex items-center gap-4 shrink-0">
          <div className="flex items-center gap-1">
            <Button variant="ghost" size="icon" className="h-8 w-8" onClick={navigatePrev}>
              <ChevronLeft className="h-4 w-4" />
            </Button>
            <Button variant="ghost" size="icon" className="h-8 w-8" onClick={navigateNext}>
              <ChevronRight className="h-4 w-4" />
            </Button>
          </div>
          <Button variant="outline" size="sm" className="h-8" onClick={navigateToday}>
            Today
          </Button>

          <div className="flex items-center rounded-md border border-border overflow-hidden">
            {(Object.entries(viewLabels) as [CalendarView, string][]).map(([view, label]) => (
              <button
                key={view}
                onClick={() => changeView(view)}
                className={`px-3 py-1.5 text-sm font-medium transition-colors duration-150 ${
                  currentView === view
                    ? 'bg-accent/15 text-accent'
                    : 'text-muted-foreground hover:text-foreground hover:bg-card-elevated'
                }`}
                style={{
                  backgroundColor: currentView === view ? 'hsl(var(--accent-color) / 0.15)' : undefined,
                  color: currentView === view ? 'hsl(var(--accent-color))' : undefined,
                }}
              >
                {label}
              </button>
            ))}
          </div>

          <h2 className="text-lg font-semibold font-heading">{calendarTitle}</h2>

          <div className="flex-1" />

          {/* Event search */}
          <div className="relative">
            <Search className="absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground" />
            <Input
              placeholder="Search events..."
              value={eventSearch}
              onChange={(e) => setEventSearch(e.target.value)}
              onFocus={() => setSearchFocused(true)}
              onBlur={() => setTimeout(() => setSearchFocused(false), 200)}
              className="w-52 h-8 pl-8 text-sm ring-inset"
            />
            {searchFocused && searchResults.length > 0 && (
              <div className="absolute z-50 mt-1 w-72 right-0 rounded-md border bg-popover shadow-lg overflow-hidden">
                {searchResults.map((event) => (
                  <button
                    key={event.id}
                    type="button"
                    onMouseDown={(e) => e.preventDefault()}
                    onClick={() => handleSearchSelect(event)}
                    className="flex items-center gap-2.5 w-full px-3 py-2 text-sm text-left hover:bg-accent/10 transition-colors"
                  >
                    <div
                      className="w-2 h-2 rounded-full shrink-0"
                      style={{ backgroundColor: event.calendar_color || 'hsl(var(--accent-color))' }}
                    />
                    <div className="min-w-0 flex-1">
                      <span className="font-medium truncate block">{event.title}</span>
                      <span className="text-xs text-muted-foreground">
                        {new Date(event.start_datetime).toLocaleDateString(undefined, { month: 'short', day: 'numeric' })}
                      </span>
                    </div>
                  </button>
                ))}
              </div>
            )}
          </div>

          <Button size="sm" onClick={handleCreateNew}>
            <Plus className="mr-2 h-4 w-4" />
            Create Event
          </Button>
        </div>

        {/* Calendar grid + event detail panel */}
        <div className="flex-1 overflow-hidden flex">
          <div
            className={`overflow-y-auto transition-all duration-300 ease-[cubic-bezier(0.16,1,0.3,1)] ${
              panelOpen ? 'w-full lg:w-[55%]' : 'w-full'
            }`}
          >
            <div className="h-full">
              <FullCalendar
                key={`fc-${settings?.first_day_of_week ?? 0}`}
                ref={calendarRef}
                plugins={[dayGridPlugin, timeGridPlugin, interactionPlugin]}
                initialView="dayGridMonth"
                headerToolbar={false}
                firstDay={settings?.first_day_of_week ?? 0}
                events={calendarEvents}
                editable={true}
                selectable={true}
                selectMirror={true}
                unselectAuto={false}
                dayMaxEvents={true}
                weekends={true}
                nowIndicator={true}
                eventClick={handleEventClick}
                eventDrop={handleEventDrop}
                eventResize={handleEventResize}
                select={handleDateSelect}
                datesSet={handleDatesSet}
                height="100%"
              />
            </div>
          </div>

          {/* Detail panel (desktop) */}
          <div
            className={`overflow-hidden transition-all duration-300 ease-[cubic-bezier(0.16,1,0.3,1)] ${
              panelOpen ? 'hidden lg:block lg:w-[45%]' : 'w-0 opacity-0'
            }`}
          >
            <EventDetailPanel
              event={panelMode === 'view' ? selectedEvent : null}
              isCreating={panelMode === 'create'}
              createDefaults={createDefaults}
              onClose={handlePanelClose}
              onSaved={handlePanelClose}
              locationName={selectedEvent?.location_id ? locationMap.get(selectedEvent.location_id) : undefined}
            />
          </div>
        </div>
      </div>

      {/* Mobile detail panel overlay */}
      {panelOpen && (
        <div
          className="lg:hidden fixed inset-0 z-50 bg-background/80 backdrop-blur-sm"
          onClick={handlePanelClose}
        >
          <div
            className="fixed inset-y-0 right-0 w-full sm:w-[400px] bg-card border-l border-border shadow-lg"
            onClick={(e) => e.stopPropagation()}
          >
            <EventDetailPanel
              event={panelMode === 'view' ? selectedEvent : null}
              isCreating={panelMode === 'create'}
              createDefaults={createDefaults}
              onClose={handlePanelClose}
              onSaved={handlePanelClose}
              locationName={selectedEvent?.location_id ? locationMap.get(selectedEvent.location_id) : undefined}
            />
          </div>
        </div>
      )}
    </div>
  );
}
