import { useState, useRef, useEffect, useMemo } from 'react';
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 } from 'lucide-react';
import api, { getErrorMessage } from '@/lib/api';
import type { CalendarEvent, EventTemplate } from '@/types';
import { useCalendars } from '@/hooks/useCalendars';
import { useSettings } from '@/hooks/useSettings';
import { Button } from '@/components/ui/button';
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
} from '@/components/ui/dialog';
import CalendarSidebar from './CalendarSidebar';
import EventForm from './EventForm';

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

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

type ScopeAction = 'edit' | 'delete';

export default function CalendarPage() {
  const queryClient = useQueryClient();
  const calendarRef = useRef<FullCalendar>(null);
  const [showForm, setShowForm] = useState(false);
  const [editingEvent, setEditingEvent] = useState<CalendarEvent | null>(null);
  const [selectedStart, setSelectedStart] = useState<string | null>(null);
  const [selectedEnd, setSelectedEnd] = useState<string | null>(null);
  const [selectedAllDay, setSelectedAllDay] = useState(false);
  const [currentView, setCurrentView] = useState<CalendarView>('dayGridMonth');
  const [calendarTitle, setCalendarTitle] = useState('');

  const [templateEvent, setTemplateEvent] = useState<Partial<CalendarEvent> | null>(null);
  const [templateName, setTemplateName] = useState<string | null>(null);

  // Scope dialog state
  const [scopeDialogOpen, setScopeDialogOpen] = useState(false);
  const [scopeAction, setScopeAction] = useState<ScopeAction>('edit');
  const [scopeEvent, setScopeEvent] = useState<CalendarEvent | null>(null);
  const [activeEditScope, setActiveEditScope] = useState<'this' | 'this_and_future' | null>(null);

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

  // 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();
  }, []);

  // 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 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 scopeDeleteMutation = useMutation({
    mutationFn: async ({ id, scope }: { id: number; scope: string }) => {
      await api.delete(`/events/${id}?scope=${scope}`);
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['calendar-events'] });
      queryClient.invalidateQueries({ queryKey: ['dashboard'] });
      queryClient.invalidateQueries({ queryKey: ['upcoming'] });
      toast.success('Event(s) deleted');
      setScopeDialogOpen(false);
      setScopeEvent(null);
    },
    onError: (error) => {
      toast.error(getErrorMessage(error, 'Failed to delete event'));
    },
  });

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

  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 isRecurring = (event: CalendarEvent): boolean => {
    return !!(event.is_recurring || 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;
    }

    if (isRecurring(event)) {
      setScopeEvent(event);
      setScopeAction('edit');
      setScopeDialogOpen(true);
    } else {
      setEditingEvent(event);
      setShowForm(true);
    }
  };

  const handleScopeChoice = (scope: 'this' | 'this_and_future') => {
    if (!scopeEvent) return;
    if (scopeAction === 'edit') {
      setEditingEvent(scopeEvent);
      setActiveEditScope(scope);
      setShowForm(true);
      setScopeDialogOpen(false);
    } else if (scopeAction === 'delete') {
      scopeDeleteMutation.mutate({ id: scopeEvent.id as number, scope });
    }
  };

  const handleEventDrop = (info: EventDropArg) => {
    if (info.event.extendedProps.is_virtual) {
      info.revert();
      return;
    }
    // Prevent drag-drop on recurring events — user must use scope dialog via click
    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;
    }
    // Prevent resize on recurring events — user must use scope dialog via click
    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) => {
    setSelectedStart(selectInfo.startStr);
    setSelectedEnd(selectInfo.endStr);
    setSelectedAllDay(selectInfo.allDay);
    setShowForm(true);
  };

  const handleCloseForm = () => {
    calendarRef.current?.getApi().unselect();
    setShowForm(false);
    setEditingEvent(null);
    setTemplateEvent(null);
    setTemplateName(null);
    setActiveEditScope(null);
    setSelectedStart(null);
    setSelectedEnd(null);
    setSelectedAllDay(false);
  };

  const handleUseTemplate = (template: EventTemplate) => {
    setTemplateEvent({
      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>);
    setTemplateName(template.name);
    setEditingEvent(null);
    setShowForm(true);
  };

  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">
      <CalendarSidebar onUseTemplate={handleUseTemplate} />

      <div ref={calendarContainerRef} className="flex-1 flex flex-col overflow-hidden">
        {/* Custom toolbar — h-16 matches sidebar header */}
        <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>
          <h2 className="text-lg font-semibold font-heading flex-1">{calendarTitle}</h2>
          <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>
        </div>

        {/* Calendar grid */}
        <div className="flex-1 overflow-y-auto">
          <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>
      </div>

      {showForm && (
        <EventForm
          event={editingEvent}
          templateData={templateEvent}
          templateName={templateName}
          initialStart={selectedStart}
          initialEnd={selectedEnd}
          initialAllDay={selectedAllDay}
          editScope={activeEditScope}
          onClose={handleCloseForm}
        />
      )}

      {/* Recurring event scope dialog */}
      <Dialog open={scopeDialogOpen} onOpenChange={setScopeDialogOpen}>
        <DialogContent className="max-w-sm">
          <DialogHeader>
            <DialogTitle>
              {scopeAction === 'edit' ? 'Edit Recurring Event' : 'Delete Recurring Event'}
            </DialogTitle>
          </DialogHeader>
          <p className="text-sm text-muted-foreground">
            This is a recurring event. How would you like to proceed?
          </p>
          <div className="flex flex-col gap-2 mt-2">
            <Button
              variant="outline"
              className="w-full justify-center"
              onClick={() => handleScopeChoice('this')}
            >
              This event only
            </Button>
            <Button
              variant="outline"
              className="w-full justify-center"
              onClick={() => handleScopeChoice('this_and_future')}
            >
              This and all future events
            </Button>
            <Button
              variant="ghost"
              className="w-full justify-center"
              onClick={() => {
                setScopeDialogOpen(false);
                setScopeEvent(null);
              }}
            >
              Cancel
            </Button>
          </div>
        </DialogContent>
      </Dialog>
    </div>
  );
}
