/**
 * Owner Scheduler - Horizontal timeline view for owner/manager/staff users
 */

import React, { useState, useRef, useEffect, useMemo } from 'react';
import { Appointment, AppointmentStatus, User, Business } from '../types';
import { Clock, Calendar as CalendarIcon, Filter, GripVertical, CheckCircle2, Trash2, X, User as UserIcon, Mail, Phone, Undo, Redo, ChevronLeft, ChevronRight, Check } from 'lucide-react';
import { useAppointments, useUpdateAppointment, useDeleteAppointment } from '../hooks/useAppointments';
import { useResources } from '../hooks/useResources';
import { useServices } from '../hooks/useServices';
import { useAppointmentWebSocket } from '../hooks/useAppointmentWebSocket';
import Portal from '../components/Portal';
import EventAutomations from '../components/EventAutomations';

// Time settings
const START_HOUR = 0;  // Midnight
const END_HOUR = 24;   // Midnight next day
const PIXELS_PER_MINUTE = 2.5;
const OVERLAY_HOUR_HEIGHT = 60;
const OVERLAY_RESOURCE_WIDTH = 150; // Width of each resource column in month overlay
const HEADER_HEIGHT = 48;
const SIDEBAR_WIDTH = 250;

// Format duration as hours and minutes when >= 60 min
const formatDuration = (minutes: number): string => {
  if (minutes >= 60) {
    const hours = Math.floor(minutes / 60);
    const mins = minutes % 60;
    return mins > 0 ? `${hours}h ${mins}m` : `${hours}h`;
  }
  return `${minutes} min`;
};

// Layout settings
const MIN_ROW_HEIGHT = 104;
const EVENT_HEIGHT = 88;
const EVENT_GAP = 8;

interface OwnerSchedulerProps {
  user: User;
  business: Business;
}

const OwnerScheduler: React.FC<OwnerSchedulerProps> = ({ user, business }) => {
  type ViewMode = 'day' | 'week' | 'month';
  const [viewMode, setViewMode] = useState<ViewMode>('day');
  const [viewDate, setViewDate] = useState(new Date());

  // Calculate date range for fetching appointments based on current view
  const dateRange = useMemo(() => {
    const getStartOfWeek = (date: Date): Date => {
      const d = new Date(date);
      const day = d.getDay();
      d.setDate(d.getDate() - day);
      d.setHours(0, 0, 0, 0);
      return d;
    };

    if (viewMode === 'day') {
      const start = new Date(viewDate);
      start.setHours(0, 0, 0, 0);
      const end = new Date(start);
      end.setDate(end.getDate() + 1);
      return { startDate: start, endDate: end };
    } else if (viewMode === 'week') {
      const start = getStartOfWeek(viewDate);
      const end = new Date(start);
      end.setDate(end.getDate() + 7);
      return { startDate: start, endDate: end };
    } else {
      // Month view
      const start = new Date(viewDate.getFullYear(), viewDate.getMonth(), 1);
      const end = new Date(viewDate.getFullYear(), viewDate.getMonth() + 1, 1);
      return { startDate: start, endDate: end };
    }
  }, [viewMode, viewDate]);

  // Fetch only appointments in the visible date range (plus pending ones)
  const { data: appointments = [] } = useAppointments(dateRange);
  const { data: resources = [] } = useResources();
  const { data: services = [] } = useServices();
  const updateMutation = useUpdateAppointment();
  const deleteMutation = useDeleteAppointment();

  // Connect to WebSocket for real-time updates
  useAppointmentWebSocket();
  const [zoomLevel, setZoomLevel] = useState(1);
  const [draggedAppointmentId, setDraggedAppointmentId] = useState<string | null>(null);
  const [dragOffsetMinutes, setDragOffsetMinutes] = useState<number>(0); // Track where on appointment drag started
  const [previewState, setPreviewState] = useState<{ resourceId: string; startTime: Date; } | null>(null);
  const [resizeState, setResizeState] = useState<{ appointmentId: string; direction: 'start' | 'end'; startX: number; originalStart: Date; originalDuration: number; newStart?: Date; newDuration?: number; } | null>(null);
  const [selectedAppointment, setSelectedAppointment] = useState<Appointment | null>(null);
  const [isDragging, setIsDragging] = useState(false);
  const [isResizing, setIsResizing] = useState(false);
  const [monthDropTarget, setMonthDropTarget] = useState<{ date: Date; rect: DOMRect } | null>(null);
  const [overlayPreview, setOverlayPreview] = useState<{ resourceId: string; hour: number; minute: number } | null>(null);
  const overlayAutoScrollRef = useRef<NodeJS.Timeout | null>(null);
  const monthOverlayDelayRef = useRef<NodeJS.Timeout | null>(null);
  const pendingMonthDropRef = useRef<{ date: Date; rect: DOMRect } | null>(null);

  // State for editing appointments
  const [editDateTime, setEditDateTime] = useState('');
  const [editResource, setEditResource] = useState('');
  const [editDuration, setEditDuration] = useState(0);

  // Filter state
  const [showFilterMenu, setShowFilterMenu] = useState(false);
  const [filterStatuses, setFilterStatuses] = useState<Set<AppointmentStatus>>(new Set(['PENDING', 'CONFIRMED', 'COMPLETED', 'CANCELLED', 'NO_SHOW']));
  const [filterResources, setFilterResources] = useState<Set<string>>(new Set()); // Empty means all
  const [filterServices, setFilterServices] = useState<Set<string>>(new Set()); // Empty means all
  const filterMenuRef = useRef<HTMLDivElement>(null);

  // Update edit state when selected appointment changes
  useEffect(() => {
    if (selectedAppointment) {
      setEditDateTime(new Date(selectedAppointment.startTime).toISOString().slice(0, 16));
      setEditResource(selectedAppointment.resourceId || '');
      setEditDuration(selectedAppointment.durationMinutes);
    }
  }, [selectedAppointment]);

  // Undo/Redo history
  type HistoryAction = {
    type: 'move' | 'resize';
    appointmentId: string;
    before: { startTime: Date; resourceId: string | null; durationMinutes?: number };
    after: { startTime: Date; resourceId: string | null; durationMinutes?: number };
  };
  const [history, setHistory] = useState<HistoryAction[]>([]);
  const [historyIndex, setHistoryIndex] = useState(-1);

  const scrollContainerRef = useRef<HTMLDivElement>(null);
  const overlayScrollRef = useRef<HTMLDivElement>(null); // Ref for the MonthDropOverlay's scrollable area
  const overlayContainerRef = useRef<HTMLDivElement>(null); // Ref for the overlay container (for wheel events)
  const isOverOverlayRef = useRef(false); // Track if mouse is over the month drop overlay

  // Keyboard shortcuts for undo/redo
  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if ((e.ctrlKey || e.metaKey) && e.key === 'z' && !e.shiftKey) {
        e.preventDefault();
        undo();
      } else if ((e.ctrlKey || e.metaKey) && (e.key === 'y' || (e.key === 'z' && e.shiftKey))) {
        e.preventDefault();
        redo();
      }
    };

    window.addEventListener('keydown', handleKeyDown);
    return () => window.removeEventListener('keydown', handleKeyDown);
  }, [historyIndex, history]);

  // Handle wheel events on month drop overlay for horizontal scrolling
  // Use a callback ref pattern to attach the listener when the element is available
  const overlayWheelHandler = React.useCallback((e: WheelEvent) => {
    e.preventDefault();
    e.stopPropagation();
    if (overlayScrollRef.current) {
      // Convert vertical scroll to horizontal: scroll up = scroll left, scroll down = scroll right
      overlayScrollRef.current.scrollLeft += e.deltaY;
      // Sync the header
      const header = overlayScrollRef.current.previousElementSibling as HTMLElement;
      if (header) header.scrollLeft = overlayScrollRef.current.scrollLeft;
    }
  }, []);

  // Callback ref that attaches the wheel listener when the overlay mounts
  const overlayContainerCallbackRef = React.useCallback((node: HTMLDivElement | null) => {
    // Remove listener from previous node if any
    if (overlayContainerRef.current) {
      overlayContainerRef.current.removeEventListener('wheel', overlayWheelHandler);
    }
    // Store the new ref
    overlayContainerRef.current = node;
    // Add listener to new node
    if (node) {
      node.addEventListener('wheel', overlayWheelHandler, { passive: false });
    }
  }, [overlayWheelHandler]);

  // Close filter menu when clicking outside
  useEffect(() => {
    const handleClickOutside = (e: MouseEvent) => {
      if (filterMenuRef.current && !filterMenuRef.current.contains(e.target as Node)) {
        setShowFilterMenu(false);
      }
    };

    if (showFilterMenu) {
      document.addEventListener('mousedown', handleClickOutside);
      return () => document.removeEventListener('mousedown', handleClickOutside);
    }
  }, [showFilterMenu]);

  // Filter toggle helpers
  const toggleStatusFilter = (status: AppointmentStatus) => {
    const newSet = new Set(filterStatuses);
    if (newSet.has(status)) {
      newSet.delete(status);
    } else {
      newSet.add(status);
    }
    setFilterStatuses(newSet);
  };

  const toggleResourceFilter = (resourceId: string) => {
    const newSet = new Set(filterResources);
    if (newSet.has(resourceId)) {
      newSet.delete(resourceId);
    } else {
      newSet.add(resourceId);
    }
    setFilterResources(newSet);
  };

  const toggleServiceFilter = (serviceId: string) => {
    const newSet = new Set(filterServices);
    if (newSet.has(serviceId)) {
      newSet.delete(serviceId);
    } else {
      newSet.add(serviceId);
    }
    setFilterServices(newSet);
  };

  const clearAllFilters = () => {
    setFilterStatuses(new Set(['PENDING', 'CONFIRMED', 'COMPLETED', 'CANCELLED', 'NO_SHOW']));
    setFilterResources(new Set());
    setFilterServices(new Set());
  };

  const hasActiveFilters = filterStatuses.size < 5 || filterResources.size > 0 || filterServices.size > 0;

  // Scroll to current time on mount (centered in view)
  useEffect(() => {
    if (!scrollContainerRef.current) return;

    const now = new Date();
    const today = new Date(viewDate);
    today.setHours(0, 0, 0, 0);
    const nowDay = new Date(now);
    nowDay.setHours(0, 0, 0, 0);

    // Only scroll if today is in the current view
    if (viewMode === 'day' && nowDay.getTime() !== today.getTime()) return;

    const container = scrollContainerRef.current;
    const containerWidth = container.clientWidth;

    // Calculate current time offset in pixels
    const startOfDay = new Date(now);
    startOfDay.setHours(START_HOUR, 0, 0, 0);
    const minutesSinceStart = (now.getTime() - startOfDay.getTime()) / (1000 * 60);
    const currentTimeOffset = minutesSinceStart * PIXELS_PER_MINUTE * zoomLevel;

    // Scroll so current time is centered
    const scrollPosition = currentTimeOffset - (containerWidth / 2);
    container.scrollLeft = Math.max(0, scrollPosition);
  }, []);

  const addToHistory = (action: HistoryAction) => {
    // Remove any history after current index (when doing new action after undo)
    const newHistory = history.slice(0, historyIndex + 1);
    newHistory.push(action);
    // Limit history to 50 actions
    if (newHistory.length > 50) {
      newHistory.shift();
    } else {
      setHistoryIndex(historyIndex + 1);
    }
    setHistory(newHistory);
  };

  const undo = () => {
    if (historyIndex < 0) return;
    const action = history[historyIndex];
    const appointment = appointments.find(a => a.id === action.appointmentId);
    if (!appointment) return;

    // Revert to "before" state
    updateMutation.mutate({
      id: action.appointmentId,
      updates: {
        startTime: action.before.startTime,
        resourceId: action.before.resourceId,
        ...(action.before.durationMinutes !== undefined && { durationMinutes: action.before.durationMinutes })
      }
    });
    setHistoryIndex(historyIndex - 1);
  };

  const redo = () => {
    if (historyIndex >= history.length - 1) return;
    const action = history[historyIndex + 1];
    const appointment = appointments.find(a => a.id === action.appointmentId);
    if (!appointment) return;

    // Apply "after" state
    updateMutation.mutate({
      id: action.appointmentId,
      updates: {
        startTime: action.after.startTime,
        resourceId: action.after.resourceId,
        ...(action.after.durationMinutes !== undefined && { durationMinutes: action.after.durationMinutes })
      }
    });
    setHistoryIndex(historyIndex + 1);
  };

  // Date navigation helpers
  const getStartOfWeek = (date: Date): Date => {
    const d = new Date(date);
    const day = d.getDay();
    const diff = d.getDate() - day; // Sunday as start of week
    return new Date(d.setDate(diff));
  };

  const getStartOfMonth = (date: Date): Date => {
    return new Date(date.getFullYear(), date.getMonth(), 1);
  };

  const getEndOfMonth = (date: Date): Date => {
    return new Date(date.getFullYear(), date.getMonth() + 1, 0);
  };

  // Generate calendar grid data for month view
  const getMonthCalendarData = () => {
    const firstDay = getStartOfMonth(viewDate);
    const lastDay = getEndOfMonth(viewDate);
    const startDayOfWeek = firstDay.getDay(); // 0 = Sunday
    const daysInMonth = lastDay.getDate();

    // Create array of week rows
    const weeks: (Date | null)[][] = [];
    let currentWeek: (Date | null)[] = [];

    // Add empty cells for days before the first of the month
    for (let i = 0; i < startDayOfWeek; i++) {
      currentWeek.push(null);
    }

    // Add all days of the month
    for (let day = 1; day <= daysInMonth; day++) {
      currentWeek.push(new Date(viewDate.getFullYear(), viewDate.getMonth(), day));
      if (currentWeek.length === 7) {
        weeks.push(currentWeek);
        currentWeek = [];
      }
    }

    // Add empty cells for remaining days after the last of the month
    if (currentWeek.length > 0) {
      while (currentWeek.length < 7) {
        currentWeek.push(null);
      }
      weeks.push(currentWeek);
    }

    return weeks;
  };

  // Get appointments for a specific day (for month view)
  const getAppointmentsForDay = (date: Date) => {
    const dayStart = new Date(date);
    dayStart.setHours(0, 0, 0, 0);
    const dayEnd = new Date(date);
    dayEnd.setHours(23, 59, 59, 999);

    return filteredAppointments.filter(apt => {
      if (!apt.resourceId) return false; // Exclude pending
      const aptDate = new Date(apt.startTime);
      return aptDate >= dayStart && aptDate <= dayEnd;
    }).sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime());
  };

  const navigateDate = (direction: 'prev' | 'next') => {
    const newDate = new Date(viewDate);

    if (viewMode === 'day') {
      newDate.setDate(newDate.getDate() + (direction === 'next' ? 1 : -1));
    } else if (viewMode === 'week') {
      newDate.setDate(newDate.getDate() + (direction === 'next' ? 7 : -7));
    } else if (viewMode === 'month') {
      newDate.setMonth(newDate.getMonth() + (direction === 'next' ? 1 : -1));
    }

    setViewDate(newDate);
  };

  const getDateRangeLabel = (): string => {
    if (viewMode === 'day') {
      return viewDate.toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric', year: 'numeric' });
    } else if (viewMode === 'week') {
      const weekStart = getStartOfWeek(viewDate);
      const weekEnd = new Date(weekStart);
      weekEnd.setDate(weekEnd.getDate() + 6);
      return `${weekStart.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })} - ${weekEnd.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}`;
    } else {
      return viewDate.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
    }
  };

  // Get the date range for filtering appointments
  const getDateRange = (): { start: Date; end: Date; days: Date[] } => {
    if (viewMode === 'day') {
      const start = new Date(viewDate);
      start.setHours(0, 0, 0, 0);
      const end = new Date(start);
      end.setDate(end.getDate() + 1);
      return { start, end, days: [start] };
    } else if (viewMode === 'week') {
      const start = getStartOfWeek(viewDate);
      start.setHours(0, 0, 0, 0);
      const end = new Date(start);
      end.setDate(end.getDate() + 7);
      const days = Array.from({ length: 7 }, (_, i) => {
        const day = new Date(start);
        day.setDate(day.getDate() + i);
        return day;
      });
      return { start, end, days };
    } else {
      const start = getStartOfMonth(viewDate);
      start.setHours(0, 0, 0, 0);
      const end = new Date(getEndOfMonth(viewDate));
      end.setDate(end.getDate() + 1);
      end.setHours(0, 0, 0, 0);
      const daysInMonth = end.getDate() - start.getDate();
      const days = Array.from({ length: daysInMonth }, (_, i) => {
        const day = new Date(start);
        day.setDate(day.getDate() + i);
        return day;
      });
      return { start, end, days };
    }
  };

  const handleResizeStart = (
    e: React.MouseEvent,
    appointment: Appointment,
    direction: 'start' | 'end'
  ) => {
    e.preventDefault();
    e.stopPropagation();
    setIsResizing(true);
    setResizeState({
      appointmentId: appointment.id,
      direction,
      startX: e.clientX,
      originalStart: new Date(appointment.startTime),
      originalDuration: appointment.durationMinutes,
    });
  };

  useEffect(() => {
    if (!resizeState) return;
    const handleMouseMove = (e: MouseEvent) => {
      const pixelDelta = e.clientX - resizeState.startX;
      const minuteDelta = pixelDelta / (PIXELS_PER_MINUTE * zoomLevel);
      const snappedMinutes = Math.round(minuteDelta / 15) * 15;
      if (snappedMinutes === 0 && resizeState.direction === 'end') return;

      const appointment = appointments.find(apt => apt.id === resizeState.appointmentId);
      if (!appointment) return;

      let newStart = new Date(resizeState.originalStart);
      let newDuration = resizeState.originalDuration;
      if (resizeState.direction === 'end') {
        newDuration = Math.max(15, resizeState.originalDuration + snappedMinutes);
      } else {
        if (resizeState.originalDuration - snappedMinutes >= 15) {
          newStart = new Date(resizeState.originalStart.getTime() + snappedMinutes * 60000);
          newDuration = resizeState.originalDuration - snappedMinutes;
        }
      }

      setResizeState(prev => prev ? { ...prev, newStart, newDuration } : null);
    };

    const handleMouseUp = () => {
      if (resizeState && 'newStart' in resizeState && 'newDuration' in resizeState) {
        const appointment = appointments.find(a => a.id === resizeState.appointmentId);
        if (appointment) {
          // Add to history
          addToHistory({
            type: 'resize',
            appointmentId: resizeState.appointmentId,
            before: {
              startTime: resizeState.originalStart,
              resourceId: appointment.resourceId,
              durationMinutes: resizeState.originalDuration
            },
            after: {
              startTime: resizeState.newStart as Date,
              resourceId: appointment.resourceId,
              durationMinutes: resizeState.newDuration as number
            }
          });

          updateMutation.mutate({
            id: resizeState.appointmentId,
            updates: {
              startTime: resizeState.newStart as Date,
              durationMinutes: resizeState.newDuration as number
            }
          });
        }
      }
      setResizeState(null);
      // Reset isResizing after a brief delay to prevent click handler from firing
      setTimeout(() => setIsResizing(false), 100);
    };

    window.addEventListener('mousemove', handleMouseMove);
    window.addEventListener('mouseup', handleMouseUp);
    return () => {
      window.removeEventListener('mousemove', handleMouseMove);
      window.removeEventListener('mouseup', handleMouseUp);
    };
  }, [resizeState, zoomLevel, appointments, updateMutation]);

  const getOffset = (date: Date) => {
    const { days } = getDateRange();

    // Find which day this appointment belongs to
    const appointmentDate = new Date(date);
    appointmentDate.setHours(0, 0, 0, 0);

    let dayIndex = 0;
    for (let i = 0; i < days.length; i++) {
      const day = new Date(days[i]);
      day.setHours(0, 0, 0, 0);
      if (day.getTime() === appointmentDate.getTime()) {
        dayIndex = i;
        break;
      }
    }

    // Calculate offset within the day
    const startOfDay = new Date(date);
    startOfDay.setHours(START_HOUR, 0, 0, 0);
    const diffMinutes = (date.getTime() - startOfDay.getTime()) / (1000 * 60);
    const offsetWithinDay = Math.max(0, diffMinutes * (PIXELS_PER_MINUTE * zoomLevel));

    // Add the day offset
    const dayOffset = dayIndex * dayWidth;
    return dayOffset + offsetWithinDay;
  };

  const getWidth = (durationMinutes: number) => durationMinutes * (PIXELS_PER_MINUTE * zoomLevel);

  const getStatusColor = (status: Appointment['status'], startTime: Date, endTime: Date) => {
    if (status === 'COMPLETED') return 'bg-green-100 border-green-500 text-green-900 dark:bg-green-900/50 dark:border-green-500 dark:text-green-200';
    if (status === 'NO_SHOW') return 'bg-gray-100 border-gray-400 text-gray-600 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-400';
    if (status === 'CANCELLED') return 'bg-gray-100 border-gray-400 text-gray-500 opacity-75 dark:bg-gray-800 dark:border-gray-600 dark:text-gray-400';
    const now = new Date();
    if (now > endTime) return 'bg-red-100 border-red-500 text-red-900 dark:bg-red-900/50 dark:border-red-500 dark:text-red-200';
    if (now >= startTime && now <= endTime) return 'bg-yellow-100 border-yellow-500 text-yellow-900 dark:bg-yellow-900/50 dark:border-yellow-500 dark:text-yellow-200';
    return 'bg-blue-100 border-blue-500 text-blue-900 dark:bg-blue-900/50 dark:border-blue-500 dark:text-blue-200';
  };

  // Simplified status colors for month view (no border classes)
  const getMonthStatusColor = (status: Appointment['status'], startTime: Date, endTime: Date) => {
    if (status === 'COMPLETED') return 'bg-green-100 dark:bg-green-900/50 text-green-800 dark:text-green-200 hover:bg-green-200 dark:hover:bg-green-800/50';
    if (status === 'NO_SHOW') return 'bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-600';
    if (status === 'CANCELLED') return 'bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 opacity-75 hover:bg-gray-200 dark:hover:bg-gray-600';
    const now = new Date();
    if (now > endTime) return 'bg-red-100 dark:bg-red-900/50 text-red-800 dark:text-red-200 hover:bg-red-200 dark:hover:bg-red-800/50';
    if (now >= startTime && now <= endTime) return 'bg-yellow-100 dark:bg-yellow-900/50 text-yellow-800 dark:text-yellow-200 hover:bg-yellow-200 dark:hover:bg-yellow-800/50';
    return 'bg-blue-100 dark:bg-blue-900/50 text-blue-800 dark:text-blue-200 hover:bg-blue-200 dark:hover:bg-blue-800/50';
  };

  // Filter appointments by date range and user filters
  const { start: rangeStart, end: rangeEnd } = getDateRange();
  const filteredAppointments = useMemo(() => {
    return appointments.filter(apt => {
      // Apply status filter
      if (!filterStatuses.has(apt.status)) return false;

      // Apply resource filter (empty set means show all)
      if (filterResources.size > 0 && apt.resourceId && !filterResources.has(apt.resourceId)) return false;

      // Apply service filter (empty set means show all)
      if (filterServices.size > 0 && !filterServices.has(apt.serviceId)) return false;

      // Always include pending requests (no resourceId) if they pass above filters
      if (!apt.resourceId) return true;

      // Filter scheduled appointments by date range
      const aptDate = new Date(apt.startTime);
      return aptDate >= rangeStart && aptDate < rangeEnd;
    });
  }, [appointments, rangeStart, rangeEnd, filterStatuses, filterResources, filterServices]);

  const resourceLayouts = useMemo(() => {
    return resources.map(resource => {
      const allResourceApps = filteredAppointments.filter(a => a.resourceId === resource.id);
      const layoutApps = allResourceApps.filter(a => a.id !== draggedAppointmentId);

      // Add preview for dragged appointment
      if (previewState && previewState.resourceId === resource.id && draggedAppointmentId) {
        const original = filteredAppointments.find(a => a.id === draggedAppointmentId);
        if (original) {
          layoutApps.push({ ...original, startTime: previewState.startTime, id: 'PREVIEW' });
        }
      }

      // Apply resize state to appointments for live preview
      const layoutAppsWithResize = layoutApps.map(apt => {
        if (resizeState && apt.id === resizeState.appointmentId && resizeState.newStart && resizeState.newDuration) {
          return { ...apt, startTime: resizeState.newStart, durationMinutes: resizeState.newDuration };
        }
        return apt;
      });

      layoutAppsWithResize.sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime() || b.durationMinutes - a.durationMinutes);

      const lanes: number[] = [];
      const visibleAppointments = layoutAppsWithResize.map(apt => {
        const start = new Date(apt.startTime).getTime();
        const end = start + apt.durationMinutes * 60000;
        let laneIndex = -1;

        for (let i = 0; i < lanes.length; i++) {
          if (lanes[i] <= start) {
            laneIndex = i;
            lanes[i] = end;
            break;
          }
        }
        if (laneIndex === -1) {
          lanes.push(end);
          laneIndex = lanes.length - 1;
        }
        return { ...apt, laneIndex };
      });

      const laneCount = Math.max(1, lanes.length);
      const requiredHeight = Math.max(MIN_ROW_HEIGHT, (laneCount * (EVENT_HEIGHT + EVENT_GAP)) + EVENT_GAP);
      const finalAppointments = [...visibleAppointments, ...allResourceApps.filter(a => a.id === draggedAppointmentId).map(a => ({ ...a, laneIndex: 0 }))];

      return { resource, height: requiredHeight, appointments: finalAppointments, laneCount };
    });
  }, [filteredAppointments, draggedAppointmentId, previewState, resources, resizeState]);

  const handleDragStart = (e: React.DragEvent, appointmentId: string) => {
    if (resizeState) return e.preventDefault();
    setIsDragging(true);
    e.dataTransfer.setData('appointmentId', appointmentId);
    e.dataTransfer.effectAllowed = 'move';

    // In month view, we don't track minute offset
    if (viewMode === 'month') {
      setDragOffsetMinutes(0);
    } else {
      // Calculate where on the appointment the drag started (relative to appointment, not timeline)
      const target = e.currentTarget as HTMLElement;
      const rect = target.getBoundingClientRect();
      const offsetX = e.clientX - rect.left;  // Just the offset within the appointment itself
      const offsetMinutes = Math.round((offsetX / (PIXELS_PER_MINUTE * zoomLevel)) / 15) * 15;
      setDragOffsetMinutes(offsetMinutes);
    }

    setTimeout(() => setDraggedAppointmentId(appointmentId), 0);
  };

  const handleDragEnd = () => {
    setDraggedAppointmentId(null);
    setPreviewState(null);
    setMonthDropTarget(null);
    setOverlayPreview(null);
    isOverOverlayRef.current = false;
    pendingMonthDropRef.current = null;
    // Clear any pending overlay delay timeout
    if (monthOverlayDelayRef.current) {
      clearTimeout(monthOverlayDelayRef.current);
      monthOverlayDelayRef.current = null;
    }
    // Clear any auto-scroll interval
    if (overlayAutoScrollRef.current) {
      clearInterval(overlayAutoScrollRef.current);
      overlayAutoScrollRef.current = null;
    }
    // Reset isDragging after a short delay to allow click detection
    setTimeout(() => setIsDragging(false), 100);
  };

  const handleMonthCellDragOver = (e: React.DragEvent, date: Date) => {
    if (!draggedAppointmentId) return;
    // Don't update target if mouse is over the overlay
    if (isOverOverlayRef.current) return;

    e.preventDefault();
    e.stopPropagation();

    const target = e.currentTarget as HTMLElement;
    const rect = target.getBoundingClientRect();

    // If we're already showing the overlay for this date, do nothing
    if (monthDropTarget && monthDropTarget.date.getTime() === date.getTime()) {
      return;
    }

    // If we moved to a different date, cancel any pending timeout and clear overlay
    if (pendingMonthDropRef.current && pendingMonthDropRef.current.date.getTime() !== date.getTime()) {
      if (monthOverlayDelayRef.current) {
        clearTimeout(monthOverlayDelayRef.current);
        monthOverlayDelayRef.current = null;
      }
      setMonthDropTarget(null);
    }

    // Store the pending drop target
    pendingMonthDropRef.current = { date, rect };

    // Start a new timeout if not already pending for this date
    if (!monthOverlayDelayRef.current) {
      monthOverlayDelayRef.current = setTimeout(() => {
        if (pendingMonthDropRef.current) {
          setMonthDropTarget(pendingMonthDropRef.current);
        }
        monthOverlayDelayRef.current = null;
      }, 1000); // 1 second delay
    }
  };

  const handleMonthTimeDrop = (e: React.DragEvent, targetHour: number, targetMinute: number, targetResourceId?: string) => {
    e.preventDefault();
    e.stopPropagation();
    if (!draggedAppointmentId || !monthDropTarget) return;

    const appointment = appointments.find(a => a.id === draggedAppointmentId);
    if (appointment) {
      const newStartTime = new Date(monthDropTarget.date);
      newStartTime.setHours(targetHour, targetMinute, 0, 0);

      // Use target resource if provided, otherwise keep existing
      const newResourceId = targetResourceId !== undefined ? targetResourceId : appointment.resourceId;

      // Add to history
      addToHistory({
        type: 'move',
        appointmentId: appointment.id,
        before: {
          startTime: new Date(appointment.startTime),
          resourceId: appointment.resourceId,
          durationMinutes: appointment.durationMinutes
        },
        after: {
          startTime: newStartTime,
          resourceId: newResourceId,
          durationMinutes: appointment.durationMinutes
        }
      });

      updateMutation.mutate({
        id: appointment.id,
        updates: {
          startTime: newStartTime,
          resourceId: newResourceId,
          durationMinutes: appointment.durationMinutes // Required to calculate end_time
        }
      });
    }
    setMonthDropTarget(null);
    setDraggedAppointmentId(null);
    setOverlayPreview(null);
    isOverOverlayRef.current = false;
  };

  const handleOverlayAutoScroll = (e: React.DragEvent) => {
    if (!overlayScrollRef.current) return;

    const rect = overlayScrollRef.current.getBoundingClientRect();
    const mouseX = e.clientX;
    const scrollThreshold = 50; // pixels from edge to start scrolling
    const scrollSpeed = 8;      // pixels per scroll step

    // Clear any existing auto-scroll
    if (overlayAutoScrollRef.current) {
      clearInterval(overlayAutoScrollRef.current);
      overlayAutoScrollRef.current = null;
    }

    // Start auto-scrolling if near left or right edge
    if (mouseX < rect.left + scrollThreshold && overlayScrollRef.current.scrollLeft > 0) {
      // Auto-scroll left
      overlayAutoScrollRef.current = setInterval(() => {
        if (overlayScrollRef.current) {
          overlayScrollRef.current.scrollLeft -= scrollSpeed;
          // Sync header
          const header = overlayScrollRef.current.previousElementSibling as HTMLElement;
          if (header) header.scrollLeft = overlayScrollRef.current.scrollLeft;
        }
      }, 16);
    } else if (mouseX > rect.right - scrollThreshold &&
               overlayScrollRef.current.scrollLeft < overlayScrollRef.current.scrollWidth - overlayScrollRef.current.clientWidth) {
      // Auto-scroll right
      overlayAutoScrollRef.current = setInterval(() => {
        if (overlayScrollRef.current) {
          overlayScrollRef.current.scrollLeft += scrollSpeed;
          // Sync header
          const header = overlayScrollRef.current.previousElementSibling as HTMLElement;
          if (header) header.scrollLeft = overlayScrollRef.current.scrollLeft;
        }
      }, 16);
    }
  };

  const handleAppointmentClick = (appointment: Appointment) => {
    // Only open modal if we didn't actually drag or resize
    if (!isDragging && !isResizing) {
      setSelectedAppointment(appointment);
    }
  };

  const handleSaveAppointment = () => {
    if (!selectedAppointment) return;

    // Validate duration is at least 15 minutes
    const validDuration = editDuration >= 15 ? editDuration : 15;

    const updates: any = {
      startTime: new Date(editDateTime),
      durationMinutes: validDuration,
    };

    if (editResource) {
      updates.resourceId = editResource;
    }

    updateMutation.mutate({
      id: selectedAppointment.id,
      updates
    });

    setSelectedAppointment(null);
  };

  const handleTimelineDragOver = (e: React.DragEvent) => {
    if (resizeState) return;
    e.preventDefault(); e.dataTransfer.dropEffect = 'move';
    if (!scrollContainerRef.current || !draggedAppointmentId) return;

    const container = scrollContainerRef.current;
    const rect = container.getBoundingClientRect();
    const offsetX = e.clientX - rect.left + container.scrollLeft;
    const offsetY = e.clientY - rect.top + container.scrollTop - HEADER_HEIGHT;
    if (offsetY < 0) return;

    let targetResourceId: string | null = null;
    for (let i = 0, currentTop = 0; i < resourceLayouts.length; i++) {
      if (offsetY >= currentTop && offsetY < currentTop + resourceLayouts[i].height) {
        targetResourceId = resourceLayouts[i].resource.id; break;
      }
      currentTop += resourceLayouts[i].height;
    }
    if (!targetResourceId) return;

    // Calculate new start time, accounting for where on the appointment the drag started
    const mouseMinutes = Math.round((offsetX / (PIXELS_PER_MINUTE * zoomLevel)) / 15) * 15;
    const newStartMinutes = mouseMinutes - dragOffsetMinutes;
    const newStartTime = new Date(viewDate);
    newStartTime.setHours(START_HOUR, 0, 0, 0);
    newStartTime.setTime(newStartTime.getTime() + newStartMinutes * 60000);

    if (!previewState || previewState.resourceId !== targetResourceId || previewState.startTime.getTime() !== newStartTime.getTime()) {
      setPreviewState({ resourceId: targetResourceId, startTime: newStartTime });
    }
  };

  const handleTimelineDrop = (e: React.DragEvent) => {
    e.preventDefault(); if (resizeState) return;
    const appointmentId = e.dataTransfer.getData('appointmentId');
    if (appointmentId && previewState) {
      const appointment = appointments.find(a => a.id === appointmentId);
      if (appointment) {
        // Add to history
        addToHistory({
          type: 'move',
          appointmentId,
          before: {
            startTime: new Date(appointment.startTime),
            resourceId: appointment.resourceId,
            durationMinutes: appointment.durationMinutes
          },
          after: {
            startTime: previewState.startTime,
            resourceId: previewState.resourceId,
            durationMinutes: appointment.durationMinutes
          }
        });

        updateMutation.mutate({
          id: appointmentId,
          updates: {
            startTime: previewState.startTime,
            durationMinutes: appointment.durationMinutes,
            resourceId: previewState.resourceId,
            status: appointment.status === 'PENDING' ? 'CONFIRMED' : appointment.status
          }
        });
      }
    }
    setDraggedAppointmentId(null); setPreviewState(null);
  };

  const handleDropToPending = (e: React.DragEvent) => {
    e.preventDefault();
    const appointmentId = e.dataTransfer.getData('appointmentId');
    if (appointmentId) {
      updateMutation.mutate({
        id: appointmentId,
        updates: { resourceId: null, status: 'PENDING' }
      });
    }
    setDraggedAppointmentId(null); setPreviewState(null);
  };

  const handleDropToArchive = (e: React.DragEvent) => {
    e.preventDefault();
    const appointmentId = e.dataTransfer.getData('appointmentId');
    if (appointmentId) {
      deleteMutation.mutate(appointmentId);
    }
    setDraggedAppointmentId(null); setPreviewState(null);
  };

  const handleSidebarDragOver = (e: React.DragEvent) => {
    e.preventDefault(); e.dataTransfer.dropEffect = 'move';
    if (previewState) setPreviewState(null);
  };

  const { days } = getDateRange();
  const dayWidth = (END_HOUR - START_HOUR) * 60 * (PIXELS_PER_MINUTE * zoomLevel);
  const timelineWidth = dayWidth * days.length;
  const timeMarkers = Array.from({ length: END_HOUR - START_HOUR + 1 }, (_, i) => START_HOUR + i);
  const pendingAppointments = filteredAppointments.filter(a => !a.resourceId);

  return (
    <div className="flex flex-col h-full overflow-hidden select-none bg-white dark:bg-gray-900 transition-colors duration-200">
      <div className="flex items-center justify-between px-6 py-4 bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 shadow-sm shrink-0 z-10 transition-colors duration-200">
        <div className="flex items-center gap-4">
          {/* Date Navigation */}
          <div className="flex items-center gap-2">
            <button
              onClick={() => navigateDate('prev')}
              className="p-1.5 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors"
              title="Previous"
            >
              <ChevronLeft size={20} />
            </button>
            <div className="flex items-center gap-2 px-3 py-1.5 bg-gray-100 dark:bg-gray-700 rounded-md text-gray-700 dark:text-gray-200 font-medium transition-colors duration-200 w-[320px] justify-center">
              <CalendarIcon size={16} />
              <span className="text-center">{getDateRangeLabel()}</span>
            </div>
            <button
              onClick={() => navigateDate('next')}
              className="p-1.5 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors"
              title="Next"
            >
              <ChevronRight size={20} />
            </button>
          </div>

          {/* View Mode Switcher */}
          <div className="flex items-center gap-1 border-l border-gray-300 dark:border-gray-600 pl-4">
            <button
              onClick={() => setViewMode('day')}
              className={`px-3 py-1.5 text-sm font-medium rounded transition-colors ${
                viewMode === 'day'
                  ? 'bg-blue-500 text-white'
                  : 'text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'
              }`}
            >
              Day
            </button>
            <button
              onClick={() => setViewMode('week')}
              className={`px-3 py-1.5 text-sm font-medium rounded transition-colors ${
                viewMode === 'week'
                  ? 'bg-blue-500 text-white'
                  : 'text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'
              }`}
            >
              Week
            </button>
            <button
              onClick={() => setViewMode('month')}
              className={`px-3 py-1.5 text-sm font-medium rounded transition-colors ${
                viewMode === 'month'
                  ? 'bg-blue-500 text-white'
                  : 'text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'
              }`}
            >
              Month
            </button>
          </div>
          {viewMode !== 'month' && (
            <div className="flex items-center gap-2">
              <button className="p-1.5 text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors" onClick={() => setZoomLevel(Math.max(0.5, zoomLevel - 0.25))}>-</button>
              <span className="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide">Zoom</span>
              <button className="p-1.5 text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 rounded transition-colors" onClick={() => setZoomLevel(Math.min(2, zoomLevel + 0.25))}>+</button>
            </div>
          )}
          <div className="flex items-center gap-1 border-l border-gray-300 dark:border-gray-600 pl-4">
            <button
              onClick={undo}
              disabled={historyIndex < 0}
              className={`p-2 rounded transition-colors ${
                historyIndex < 0
                  ? 'text-gray-300 dark:text-gray-600 cursor-not-allowed'
                  : 'text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'
              }`}
              title="Undo (Ctrl+Z)"
            >
              <Undo size={18} />
            </button>
            <button
              onClick={redo}
              disabled={historyIndex >= history.length - 1}
              className={`p-2 rounded transition-colors ${
                historyIndex >= history.length - 1
                  ? 'text-gray-300 dark:text-gray-600 cursor-not-allowed'
                  : 'text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700'
              }`}
              title="Redo (Ctrl+Y)"
            >
              <Redo size={18} />
            </button>
          </div>
          {/* Status Legend */}
          <div className="flex items-center gap-3 border-l border-gray-300 dark:border-gray-600 pl-4">
            <span className="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide">Status:</span>
            <div className="flex items-center gap-2">
              <div className="flex items-center gap-1">
                <div className="w-3 h-3 rounded bg-blue-100 border border-blue-500"></div>
                <span className="text-xs text-gray-600 dark:text-gray-400">Upcoming</span>
              </div>
              <div className="flex items-center gap-1">
                <div className="w-3 h-3 rounded bg-yellow-100 border border-yellow-500"></div>
                <span className="text-xs text-gray-600 dark:text-gray-400">In Progress</span>
              </div>
              <div className="flex items-center gap-1">
                <div className="w-3 h-3 rounded bg-red-100 border border-red-500"></div>
                <span className="text-xs text-gray-600 dark:text-gray-400">Overdue</span>
              </div>
              <div className="flex items-center gap-1">
                <div className="w-3 h-3 rounded bg-green-100 border border-green-500"></div>
                <span className="text-xs text-gray-600 dark:text-gray-400">Completed</span>
              </div>
              <div className="flex items-center gap-1">
                <div className="w-3 h-3 rounded bg-gray-100 border border-gray-400"></div>
                <span className="text-xs text-gray-600 dark:text-gray-400">Cancelled</span>
              </div>
            </div>
          </div>
        </div>
        <div className="flex items-center gap-3">
          <button className="flex items-center gap-2 px-4 py-2 text-sm font-medium text-white bg-brand-500 rounded-lg hover:bg-brand-600 transition-colors shadow-sm">
            + New Appointment
          </button>
          {/* Filter Dropdown */}
          <div className="relative" ref={filterMenuRef}>
            <button
              onClick={() => setShowFilterMenu(!showFilterMenu)}
              className={`p-2 rounded-lg border transition-colors relative ${
                hasActiveFilters
                  ? 'text-brand-600 dark:text-brand-400 bg-brand-50 dark:bg-brand-900/30 border-brand-300 dark:border-brand-700 hover:bg-brand-100 dark:hover:bg-brand-900/50'
                  : 'text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 border-gray-200 dark:border-gray-600'
              }`}
            >
              <Filter size={18} />
              {hasActiveFilters && (
                <span className="absolute -top-1 -right-1 w-3 h-3 bg-brand-500 rounded-full"></span>
              )}
            </button>
            {showFilterMenu && (
              <div className="absolute right-0 top-full mt-2 w-72 bg-white dark:bg-gray-800 rounded-lg shadow-xl border border-gray-200 dark:border-gray-700 z-50 overflow-hidden">
                <div className="px-4 py-3 border-b border-gray-200 dark:border-gray-700 flex items-center justify-between">
                  <h3 className="font-semibold text-gray-900 dark:text-white text-sm">Filters</h3>
                  {hasActiveFilters && (
                    <button
                      onClick={clearAllFilters}
                      className="text-xs text-brand-600 dark:text-brand-400 hover:underline"
                    >
                      Clear all
                    </button>
                  )}
                </div>
                <div className="max-h-80 overflow-y-auto">
                  {/* Status Filter */}
                  <div className="px-4 py-3 border-b border-gray-100 dark:border-gray-700">
                    <h4 className="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-2">Status</h4>
                    <div className="space-y-1">
                      {(['PENDING', 'CONFIRMED', 'COMPLETED', 'CANCELLED', 'NO_SHOW'] as AppointmentStatus[]).map(status => (
                        <div
                          key={status}
                          onClick={() => toggleStatusFilter(status)}
                          className="flex items-center gap-2 py-1 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700/50 px-2 -mx-2 rounded"
                        >
                          <div className={`w-4 h-4 rounded border flex items-center justify-center transition-colors ${
                            filterStatuses.has(status)
                              ? 'bg-brand-500 border-brand-500'
                              : 'border-gray-300 dark:border-gray-600'
                          }`}>
                            {filterStatuses.has(status) && <Check size={12} className="text-white" />}
                          </div>
                          <span className="text-sm text-gray-700 dark:text-gray-300 capitalize">
                            {status.toLowerCase().replace('_', ' ')}
                          </span>
                          <div className={`w-2 h-2 rounded-full ml-auto ${
                            status === 'COMPLETED' ? 'bg-green-500' :
                            status === 'CANCELLED' ? 'bg-gray-400' :
                            status === 'NO_SHOW' ? 'bg-gray-400' :
                            status === 'CONFIRMED' ? 'bg-blue-500' :
                            'bg-orange-400'
                          }`}></div>
                        </div>
                      ))}
                    </div>
                  </div>
                  {/* Resource Filter */}
                  <div className="px-4 py-3 border-b border-gray-100 dark:border-gray-700">
                    <h4 className="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-2">Resources</h4>
                    <div className="space-y-1">
                      {resources.map(resource => (
                        <div
                          key={resource.id}
                          onClick={() => toggleResourceFilter(resource.id)}
                          className="flex items-center gap-2 py-1 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700/50 px-2 -mx-2 rounded"
                        >
                          <div className={`w-4 h-4 rounded border flex items-center justify-center transition-colors ${
                            filterResources.size === 0 || filterResources.has(resource.id)
                              ? 'bg-brand-500 border-brand-500'
                              : 'border-gray-300 dark:border-gray-600'
                          }`}>
                            {(filterResources.size === 0 || filterResources.has(resource.id)) && <Check size={12} className="text-white" />}
                          </div>
                          <span className="text-sm text-gray-700 dark:text-gray-300 truncate">{resource.name}</span>
                          <span className="text-xs text-gray-400 dark:text-gray-500 capitalize ml-auto">{resource.type.toLowerCase()}</span>
                        </div>
                      ))}
                    </div>
                  </div>
                  {/* Service Filter */}
                  <div className="px-4 py-3">
                    <h4 className="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-2">Services</h4>
                    <div className="space-y-1">
                      {services.map(service => (
                        <div
                          key={service.id}
                          onClick={() => toggleServiceFilter(service.id)}
                          className="flex items-center gap-2 py-1 cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-700/50 px-2 -mx-2 rounded"
                        >
                          <div className={`w-4 h-4 rounded border flex items-center justify-center transition-colors ${
                            filterServices.size === 0 || filterServices.has(service.id)
                              ? 'bg-brand-500 border-brand-500'
                              : 'border-gray-300 dark:border-gray-600'
                          }`}>
                            {(filterServices.size === 0 || filterServices.has(service.id)) && <Check size={12} className="text-white" />}
                          </div>
                          <span className="text-sm text-gray-700 dark:text-gray-300 truncate">{service.name}</span>
                          <span className="text-xs text-gray-400 dark:text-gray-500 ml-auto">{service.durationMinutes}min</span>
                        </div>
                      ))}
                    </div>
                  </div>
                </div>
              </div>
            )}
          </div>
        </div>
      </div>

      {/* Month View - Calendar Grid */}
      {viewMode === 'month' && (
        <div className="flex flex-1 overflow-hidden">
          {/* Pending Sidebar for Month View */}
          <div className="flex flex-col bg-white dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700 shrink-0 shadow-lg z-20 transition-colors duration-200" style={{ width: SIDEBAR_WIDTH }}>
            <div className={`flex-1 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 p-4 flex flex-col transition-colors duration-200 ${draggedAppointmentId ? 'bg-blue-50/50 dark:bg-blue-900/20' : ''}`}>
              <h3 className="text-xs font-bold text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-3 flex items-center gap-2 shrink-0"><Clock size={12} /> Pending Requests ({pendingAppointments.length})</h3>
              <div className="space-y-2 overflow-y-auto flex-1 mb-2">
                {pendingAppointments.length === 0 && (<div className="text-xs text-gray-400 italic text-center py-4">No pending requests</div>)}
                {pendingAppointments.map(apt => {
                  const service = services.find(s => s.id === apt.serviceId);
                  return (
                    <div
                      key={apt.id}
                      className="p-3 bg-white dark:bg-gray-700 border border-l-4 border-gray-200 dark:border-gray-600 border-l-orange-400 dark:border-l-orange-500 rounded shadow-sm cursor-pointer hover:shadow-md transition-all"
                      onClick={() => handleAppointmentClick(apt)}
                    >
                      <p className="font-semibold text-sm text-gray-900 dark:text-white">{apt.customerName}</p>
                      <p className="text-xs text-gray-500 dark:text-gray-400">{service?.name}</p>
                      <div className="mt-2 flex items-center gap-1 text-xs text-gray-400 dark:text-gray-500">
                        <Clock size={10} /> {formatDuration(apt.durationMinutes)}
                      </div>
                    </div>
                  );
                })}
              </div>
            </div>
          </div>

          {/* Calendar Grid */}
          <div className="flex-1 flex flex-col overflow-hidden bg-white dark:bg-gray-900 transition-colors duration-200">
            <div className="flex-1 overflow-auto p-4">
              {/* Day headers */}
              <div className="grid grid-cols-7 gap-px bg-gray-200 dark:bg-gray-700 rounded-t-lg overflow-hidden">
                {['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'].map(day => (
                  <div key={day} className="bg-gray-50 dark:bg-gray-800 px-2 py-3 text-center text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">
                    {day}
                  </div>
                ))}
              </div>

              {/* Calendar weeks */}
              <div className="grid grid-cols-7 gap-px bg-gray-200 dark:bg-gray-700 rounded-b-lg overflow-hidden">
                {getMonthCalendarData().flat().map((date, index) => {
                  const isToday = date && new Date().toDateString() === date.toDateString();
                  const dayAppointments = date ? getAppointmentsForDay(date) : [];
                  const displayedAppointments = dayAppointments.slice(0, 3);
                  const remainingCount = dayAppointments.length - 3;

                  return (
                    <div
                      key={index}
                      className={`min-h-[120px] p-2 transition-colors relative ${
                        date && date.getMonth() !== viewDate.getMonth()
                          ? 'bg-gray-100 dark:bg-gray-800/70 opacity-50'
                          : date
                            ? 'bg-white dark:bg-gray-900 hover:bg-gray-50 dark:hover:bg-gray-800'
                            : 'bg-gray-50 dark:bg-gray-800/50'
                      } ${monthDropTarget?.date.getTime() === date?.getTime() && date?.getMonth() === viewDate.getMonth() ? 'ring-2 ring-brand-500 ring-inset bg-brand-50 dark:bg-brand-900/20' : ''}`}
                      onClick={() => { if (date) { setViewDate(date); setViewMode('day'); } }}
                      onDragOver={(e) => date && handleMonthCellDragOver(e, date)}
                    >
                      {date && (
                        <>
                          <div className={`text-sm font-medium mb-1 ${
                            isToday
                              ? 'w-7 h-7 flex items-center justify-center rounded-full bg-brand-500 text-white'
                              : 'text-gray-700 dark:text-gray-300'
                          }`}>
                            {date.getDate()}
                          </div>
                          <div className="space-y-1">
                            {displayedAppointments.map(apt => {
                              const service = services.find(s => s.id === apt.serviceId);
                              const resource = resources.find(r => r.id === apt.resourceId);
                              const startTime = new Date(apt.startTime);
                              const isDragged = apt.id === draggedAppointmentId;
                              
                              const endTime = new Date(startTime.getTime() + apt.durationMinutes * 60000);

                              return (
                                <div
                                  key={apt.id}
                                  className={`text-xs p-1.5 rounded truncate cursor-grab active:cursor-grabbing transition-colors ${getMonthStatusColor(apt.status, startTime, endTime)} ${isDragged ? 'opacity-50' : ''}`}
                                  draggable
                                  onDragStart={(e) => handleDragStart(e, apt.id)}
                                  onDragEnd={handleDragEnd}
                                  onClick={(e) => { e.stopPropagation(); handleAppointmentClick(apt); }}
                                  title={`${apt.customerName} - ${service?.name} with ${resource?.name}`}
                                >
                                  <span className="font-medium">{startTime.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })}</span>
                                  {' '}{apt.customerName}
                                </div>
                              );
                            })}
                            {remainingCount > 0 && (
                              <div className="text-xs text-gray-500 dark:text-gray-400 font-medium pl-1">
                                +{remainingCount} more
                              </div>
                            )}
                          </div>
                        </>
                      )}
                    </div>
                  );
                })}
              </div>
            </div>
          </div>
        </div>
      )}
      
      {/* Month View Drop Overlay - Mini Day Scheduler with Resource Rows */}
      {monthDropTarget && draggedAppointmentId && (() => {
        // Pre-calculate resource layouts with lanes for this day
        const dayStart = new Date(monthDropTarget.date);
        dayStart.setHours(0, 0, 0, 0);
        const dayEnd = new Date(dayStart);
        dayEnd.setHours(23, 59, 59, 999);

        const OVERLAY_ROW_HEIGHT = 50; // Height per lane in overlay
        const OVERLAY_LANE_GAP = 2;
        const OVERLAY_PIXELS_PER_MINUTE = 1.5;
        const overlayTimelineWidth = (END_HOUR - START_HOUR) * 60 * OVERLAY_PIXELS_PER_MINUTE;

        // Get the dragged appointment for preview calculations
        const draggedApt = appointments.find(a => a.id === draggedAppointmentId);

        const overlayResourceLayouts = resources.map(resource => {
          // Get existing appointments for this resource on this day (excluding the dragged one)
          let resourceApps = appointments.filter(apt => {
            if (apt.resourceId !== resource.id || apt.id === draggedAppointmentId) return false;
            const t = new Date(apt.startTime);
            return t >= dayStart && t <= dayEnd;
          });

          // Add preview appointment if hovering over this resource
          if (overlayPreview?.resourceId === resource.id && draggedApt) {
            const previewStartMinutes = (overlayPreview.hour - START_HOUR) * 60 + overlayPreview.minute;
            resourceApps = [...resourceApps, {
              ...draggedApt,
              id: 'PREVIEW',
              startTime: new Date(dayStart.getTime() + previewStartMinutes * 60000),
            }];
          }

          // Sort by start time, then by duration (longer first)
          resourceApps.sort((a, b) => new Date(a.startTime).getTime() - new Date(b.startTime).getTime() || b.durationMinutes - a.durationMinutes);

          // Calculate lanes (respecting maxConcurrentEvents)
          const lanes: number[] = [];
          const maxLanes = resource.maxConcurrentEvents === 0 ? Infinity : resource.maxConcurrentEvents;
          const laidOutApps = resourceApps.map(apt => {
            const start = new Date(apt.startTime);
            const startMinutes = (start.getHours() - START_HOUR) * 60 + start.getMinutes();
            const endMinutes = startMinutes + apt.durationMinutes;

            let laneIndex = -1;
            for (let i = 0; i < Math.min(lanes.length, maxLanes); i++) {
              if (lanes[i] <= startMinutes) {
                laneIndex = i;
                lanes[i] = endMinutes;
                break;
              }
            }
            if (laneIndex === -1 && lanes.length < maxLanes) {
              lanes.push(endMinutes);
              laneIndex = lanes.length - 1;
            } else if (laneIndex === -1) {
              laneIndex = Math.min(lanes.length - 1, maxLanes - 1);
            }

            return { ...apt, laneIndex, startMinutes, endMinutes, isPreview: apt.id === 'PREVIEW' };
          });

          const laneCount = Math.max(1, Math.min(lanes.length, maxLanes === Infinity ? lanes.length : maxLanes));
          const rowHeight = laneCount * OVERLAY_ROW_HEIGHT + OVERLAY_LANE_GAP;

          return { resource, appointments: laidOutApps, laneCount, rowHeight };
        });

        const totalRowsHeight = overlayResourceLayouts.reduce((sum, r) => sum + r.rowHeight, 0);

        return (
          <Portal>
            <div
              ref={overlayContainerCallbackRef}
              className="fixed z-50 bg-white dark:bg-gray-800 shadow-xl rounded-lg border border-brand-200 dark:border-brand-800 overflow-hidden flex flex-col"
              style={{
                top: Math.min(window.innerHeight - 400, Math.max(10, monthDropTarget.rect.top - 50)),
                left: Math.max(10, monthDropTarget.rect.left - 200),
                width: Math.min(900, window.innerWidth - 40),
                height: Math.min(400, totalRowsHeight + 80),
                animation: 'fadeIn 0.2s ease-out'
              }}
              onDragOver={(e) => { e.preventDefault(); e.stopPropagation(); }}
              onDragEnter={() => { isOverOverlayRef.current = true; }}
              onDragLeave={(e) => {
                // Only set to false if we're actually leaving the overlay (not entering a child)
                const rect = e.currentTarget.getBoundingClientRect();
                if (e.clientX < rect.left || e.clientX > rect.right || e.clientY < rect.top || e.clientY > rect.bottom) {
                  isOverOverlayRef.current = false;
                }
              }}
            >
              {/* Header */}
              <div className="bg-brand-50 dark:bg-brand-900/30 px-3 py-2 border-b border-brand-100 dark:border-brand-800 flex items-center justify-between shrink-0">
                <span className="text-xs font-bold text-brand-700 dark:text-brand-300 uppercase tracking-wider">
                  {monthDropTarget.date.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' })}
                </span>
                <div className="text-[10px] text-brand-500 dark:text-brand-400 bg-brand-100 dark:bg-brand-800/50 px-1.5 py-0.5 rounded-full">
                  Drop on resource row
                </div>
              </div>

              {/* Main content area */}
              <div className="flex-1 flex overflow-hidden">
                {/* Resource names sidebar - fixed */}
                <div className="w-28 shrink-0 bg-gray-50 dark:bg-gray-900 border-r border-gray-200 dark:border-gray-700 flex flex-col">
                  {/* Corner cell above resource names */}
                  <div className="h-6 border-b border-gray-200 dark:border-gray-700 shrink-0"></div>
                  {/* Resource names - synced scroll */}
                  <div className="flex-1 overflow-y-auto overflow-x-hidden" style={{ scrollbarWidth: 'none' }} ref={(el) => {
                    if (el && overlayScrollRef.current) {
                      el.scrollTop = overlayScrollRef.current.scrollTop;
                    }
                  }}>
                    {overlayResourceLayouts.map(layout => (
                      <div
                        key={layout.resource.id}
                        className="border-b border-gray-100 dark:border-gray-800 px-2 flex items-center"
                        style={{ height: layout.rowHeight }}
                      >
                        <div className="truncate">
                          <span className="text-[10px] font-semibold text-gray-700 dark:text-gray-300 block truncate">
                            {layout.resource.name}
                          </span>
                          {layout.laneCount > 1 && (
                            <span className="text-[8px] text-brand-500 dark:text-brand-400">
                              {layout.laneCount} lanes
                            </span>
                          )}
                        </div>
                      </div>
                    ))}
                  </div>
                </div>

                {/* Timeline area - scrollable both ways */}
                <div className="flex-1 flex flex-col overflow-hidden">
                  {/* Time header - horizontal scroll only */}
                  <div className="h-6 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 overflow-x-auto overflow-y-hidden shrink-0" style={{ scrollbarWidth: 'none' }}>
                    <div className="flex" style={{ width: overlayTimelineWidth }}>
                      {Array.from({ length: END_HOUR - START_HOUR }, (_, i) => START_HOUR + i).map(hour => (
                        <div
                          key={hour}
                          className="border-r border-gray-200 dark:border-gray-700 text-[9px] font-medium text-gray-500 dark:text-gray-400 px-1 flex items-center"
                          style={{ width: 60 * OVERLAY_PIXELS_PER_MINUTE }}
                        >
                          {hour === 0 ? '12a' : hour === 12 ? '12p' : hour > 12 ? `${hour - 12}p` : `${hour}a`}
                        </div>
                      ))}
                    </div>
                  </div>

                  {/* Timeline grid with appointments */}
                  <div className="flex-1 overflow-auto custom-scrollbar" ref={overlayScrollRef} onDragOver={(e) => {
                    e.preventDefault();
                    handleOverlayAutoScroll(e);
                  }} onScroll={(e) => {
                    // Sync horizontal scroll with header
                    const header = e.currentTarget.previousElementSibling as HTMLElement;
                    if (header) header.scrollLeft = e.currentTarget.scrollLeft;
                  }}>
                    <div style={{ width: overlayTimelineWidth, minHeight: totalRowsHeight }}>
                      {overlayResourceLayouts.map((layout) => (
                        <div
                          key={layout.resource.id}
                          className="relative border-b border-gray-100 dark:border-gray-800"
                          style={{ height: layout.rowHeight }}
                        >
                          {/* Hour grid lines */}
                          {Array.from({ length: END_HOUR - START_HOUR }, (_, i) => START_HOUR + i).map(hour => (
                            <React.Fragment key={hour}>
                              {/* Hour line */}
                              <div
                                className="absolute top-0 bottom-0 border-r border-gray-200 dark:border-gray-700"
                                style={{ left: hour * 60 * OVERLAY_PIXELS_PER_MINUTE }}
                              ></div>
                              {/* Half-hour line */}
                              <div
                                className="absolute top-0 bottom-0 border-r border-dashed border-gray-100 dark:border-gray-800"
                                style={{ left: (hour * 60 + 30) * OVERLAY_PIXELS_PER_MINUTE }}
                              ></div>
                            </React.Fragment>
                          ))}

                          {/* Drop zones for each half-hour */}
                          {Array.from({ length: (END_HOUR - START_HOUR) * 2 }, (_, i) => {
                            const hour = START_HOUR + Math.floor(i / 2);
                            const minute = (i % 2) * 30;
                            return (
                              <div
                                key={i}
                                className="absolute top-0 bottom-0"
                                style={{
                                  left: (hour * 60 + minute - START_HOUR * 60) * OVERLAY_PIXELS_PER_MINUTE,
                                  width: 30 * OVERLAY_PIXELS_PER_MINUTE
                                }}
                                onDrop={(e) => handleMonthTimeDrop(e, hour, minute, layout.resource.id)}
                                onDragOver={(e) => {
                                  e.preventDefault();
                                  e.dataTransfer.dropEffect = 'move';
                                  // Set preview state
                                  if (!overlayPreview || overlayPreview.resourceId !== layout.resource.id ||
                                      overlayPreview.hour !== hour || overlayPreview.minute !== minute) {
                                    setOverlayPreview({ resourceId: layout.resource.id, hour, minute });
                                  }
                                }}
                                onDragLeave={() => {
                                  // Only clear if leaving this specific zone
                                  if (overlayPreview?.resourceId === layout.resource.id &&
                                      overlayPreview?.hour === hour && overlayPreview?.minute === minute) {
                                    // Don't clear immediately - let dragover on another zone set it
                                  }
                                }}
                              ></div>
                            );
                          })}

                          {/* Appointments (including preview) */}
                          {layout.appointments.map(apt => {
                            const left = apt.startMinutes * OVERLAY_PIXELS_PER_MINUTE;
                            const width = apt.durationMinutes * OVERLAY_PIXELS_PER_MINUTE;
                            const top = apt.laneIndex * OVERLAY_ROW_HEIGHT + OVERLAY_LANE_GAP;
                            const service = services.find(s => s.id === apt.serviceId);
                            const startTime = new Date(apt.startTime);
                            const endTime = new Date(startTime.getTime() + apt.durationMinutes * 60000);
                            const isPreview = apt.isPreview;

                            return (
                              <div
                                key={apt.id}
                                className={`absolute rounded border-l-2 p-1 overflow-hidden pointer-events-none ${
                                  isPreview
                                    ? 'border-dashed border-brand-400 dark:border-brand-600 bg-brand-50 dark:bg-brand-900/40 shadow-md opacity-80'
                                    : `shadow-sm ${getMonthStatusColor(apt.status, startTime, endTime)}`
                                }`}
                                style={{
                                  left: `${left}px`,
                                  width: `${Math.max(30, width - 2)}px`,
                                  top: `${top}px`,
                                  height: `${OVERLAY_ROW_HEIGHT - OVERLAY_LANE_GAP * 2}px`,
                                  zIndex: isPreview ? 50 : 10
                                }}
                              >
                                <div className={`text-[9px] font-bold truncate leading-tight ${isPreview ? 'text-brand-700 dark:text-brand-300' : ''}`}>
                                  {apt.customerName}
                                </div>
                                <div className={`text-[8px] truncate leading-tight ${isPreview ? 'text-brand-600 dark:text-brand-400' : 'opacity-80'}`}>
                                  {service?.name}
                                </div>
                              </div>
                            );
                          })}
                        </div>
                      ))}
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </Portal>
        );
      })()}

      {/* Day/Week View - Timeline */}
      {viewMode !== 'month' && (
      <div className="flex flex-1 overflow-hidden">
        <div className="flex flex-col bg-white dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700 shrink-0 shadow-lg z-20 transition-colors duration-200" style={{ width: SIDEBAR_WIDTH }}>
          <div className="border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900 flex items-center px-4 font-semibold text-gray-500 dark:text-gray-400 text-xs uppercase tracking-wider shrink-0 transition-colors duration-200" style={{ height: HEADER_HEIGHT }}>Resources</div>
          <div className="flex-1 overflow-hidden flex flex-col">
            <div className="overflow-y-auto flex-1">
              {resourceLayouts.map(layout => (
                <div key={layout.resource.id} className="flex items-center px-4 border-b border-gray-100 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors group" style={{ height: layout.height }}>
                  <div className="flex items-center gap-3 w-full">
                    <div className="flex items-center justify-center w-8 h-8 rounded bg-gray-100 dark:bg-gray-700 text-gray-500 dark:text-gray-400 group-hover:bg-brand-100 dark:group-hover:bg-brand-900 group-hover:text-brand-600 dark:group-hover:text-brand-400 transition-colors shrink-0"><GripVertical size={16} /></div>
                    <div>
                      <p className="font-medium text-sm text-gray-900 dark:text-white">{layout.resource.name}</p>
                      <p className="text-xs text-gray-400 dark:text-gray-500 capitalize flex items-center gap-1">{layout.resource.type.toLowerCase()} {layout.laneCount > 1 && <span className="text-brand-600 dark:text-brand-400 bg-brand-50 dark:bg-brand-900/50 px-1 rounded text-[10px]">{layout.laneCount} lanes</span>}</p>
                    </div>
                  </div>
                </div>
              ))}
            </div>
          </div>
          <div className={`border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 p-4 h-80 flex flex-col transition-colors duration-200 ${draggedAppointmentId ? 'bg-blue-50/50 dark:bg-blue-900/20' : ''}`} onDragOver={handleSidebarDragOver} onDrop={handleDropToPending}>
            <h3 className="text-xs font-bold text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-3 flex items-center gap-2 shrink-0"><Clock size={12} /> Pending Requests ({pendingAppointments.length})</h3>
            <div className="space-y-2 overflow-y-auto flex-1 mb-2">
              {pendingAppointments.length === 0 && !draggedAppointmentId && (<div className="text-xs text-gray-400 italic text-center py-4">No pending requests</div>)}
              {draggedAppointmentId && (<div className="border-2 border-dashed border-blue-300 dark:border-blue-700 rounded-lg p-4 text-center mb-2 bg-blue-50 dark:bg-blue-900/30"><span className="text-sm text-blue-600 dark:text-blue-400 font-medium">Drop here to unassign</span></div>)}
              {pendingAppointments.map(apt => {
                const service = services.find(s => s.id === apt.serviceId);
                return (
                  <div
                    key={apt.id}
                    className={`p-3 bg-white dark:bg-gray-700 border border-l-4 border-gray-200 dark:border-gray-600 border-l-orange-400 dark:border-l-orange-500 rounded shadow-sm cursor-grab active:cursor-grabbing hover:shadow-md transition-all ${draggedAppointmentId === apt.id ? 'opacity-50' : ''}`}
                    draggable
                    onDragStart={(e) => handleDragStart(e, apt.id)}
                    onDragEnd={handleDragEnd}
                    onClick={() => handleAppointmentClick(apt)}
                  >
                    <p className="font-semibold text-sm text-gray-900 dark:text-white">{apt.customerName}</p>
                    <p className="text-xs text-gray-500 dark:text-gray-400">{service?.name}</p>
                    <div className="mt-2 flex items-center gap-1 text-xs text-gray-400 dark:text-gray-500">
                      <Clock size={10} /> {formatDuration(apt.durationMinutes)}
                    </div>
                  </div>
                )
              })}
            </div>
            <div className={`shrink-0 mt-2 border-t border-gray-200 dark:border-gray-700 pt-2 transition-all duration-200 ${draggedAppointmentId ? 'opacity-100 translate-y-0' : 'opacity-50 translate-y-0'}`} onDragOver={handleSidebarDragOver} onDrop={handleDropToArchive}><div className={`flex items-center justify-center gap-2 p-3 rounded-lg border-2 border-dashed transition-colors ${draggedAppointmentId ? 'border-red-300 bg-red-50 text-red-600 dark:border-red-700 dark:bg-red-900/30 dark:text-red-400' : 'border-gray-200 dark:border-gray-700 bg-transparent text-gray-400 hover:border-gray-300 dark:hover:border-gray-600 hover:text-gray-500'}`}><Trash2 size={16} /><span className="text-xs font-medium">Drop here to archive</span></div></div>
          </div>
        </div>

        <div className="flex-1 flex flex-col overflow-hidden bg-white dark:bg-gray-900 relative transition-colors duration-200">
          <div className="flex-1 overflow-auto timeline-scroll" ref={scrollContainerRef} onDragOver={handleTimelineDragOver} onDrop={handleTimelineDrop}>
            <div style={{ width: timelineWidth, minWidth: '100%' }} className="relative min-h-full">
              {/* Timeline Header */}
              <div className="sticky top-0 z-10 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 transition-colors duration-200">
                {viewMode !== 'day' && (
                  <div className="flex border-b border-gray-200 dark:border-gray-700">
                    {days.map((day, dayIndex) => (
                      <div
                        key={dayIndex}
                        className="flex-shrink-0 border-r border-gray-200 dark:border-gray-700 px-2 py-2 text-sm font-semibold text-gray-700 dark:text-gray-300 text-center bg-gray-100 dark:bg-gray-700/50"
                        style={{ width: dayWidth }}
                      >
                        {day.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric' })}
                      </div>
                    ))}
                  </div>
                )}
                <div className="flex" style={{ height: HEADER_HEIGHT }}>
                  {days.map((day, dayIndex) => (
                    <div key={dayIndex} className="flex flex-shrink-0" style={{ width: dayWidth }}>
                      {timeMarkers.map(hour => (
                        <div
                          key={`${dayIndex}-${hour}`}
                          className="flex-shrink-0 border-r border-gray-200 dark:border-gray-700 px-2 py-2 text-xs font-medium text-gray-400 select-none"
                          style={{ width: 60 * (PIXELS_PER_MINUTE * zoomLevel) }}
                        >
                          {hour > 12 ? `${hour - 12} PM` : `${hour} ${hour === 12 ? 'PM' : 'AM'}`}
                        </div>
                      ))}
                    </div>
                  ))}
                </div>
              </div>
              {/* Current time indicator - only show if current day is in view */}
              {days.some(day => {
                const today = new Date();
                const dayDate = new Date(day);
                return today.toDateString() === dayDate.toDateString();
              }) && (
                <div
                  className="absolute top-0 bottom-0 border-l-2 border-red-500 z-30 pointer-events-none"
                  style={{ left: getOffset(new Date()), marginTop: viewMode === 'day' ? HEADER_HEIGHT : HEADER_HEIGHT * 2 }}
                >
                  <div className="absolute -top-1 -left-1.5 w-3 h-3 bg-red-500 rounded-full"></div>
                </div>
              )}
              <div className="relative">
                {/* Vertical grid lines for each day */}
                <div className="absolute inset-0 pointer-events-none">
                  {days.map((day, dayIndex) => (
                    <React.Fragment key={dayIndex}>
                      {timeMarkers.map(hour => (
                        <div
                          key={`${dayIndex}-${hour}`}
                          className="absolute top-0 bottom-0 border-r border-dashed border-gray-100 dark:border-gray-800"
                          style={{ left: (dayIndex * dayWidth) + ((hour - START_HOUR) * 60 * (PIXELS_PER_MINUTE * zoomLevel)) }}
                        ></div>
                      ))}
                    </React.Fragment>
                  ))}
                </div>
                {resourceLayouts.map(layout => (<div key={layout.resource.id} className="relative border-b border-gray-100 dark:border-gray-800 transition-colors" style={{ height: layout.height }}>{layout.appointments.map(apt => {
                  const isPreview = apt.id === 'PREVIEW'; const isDragged = apt.id === draggedAppointmentId; const startTime = new Date(apt.startTime); const endTime = new Date(startTime.getTime() + apt.durationMinutes * 60000); const colorClass = isPreview ? 'bg-brand-50 dark:bg-brand-900/30 border-brand-400 dark:border-brand-700 border-dashed text-brand-700 dark:text-brand-400 opacity-80' : getStatusColor(apt.status, startTime, endTime); const topOffset = (apt.laneIndex * (EVENT_HEIGHT + EVENT_GAP)) + EVENT_GAP;
                  const service = services.find(s => s.id === apt.serviceId);
                  return (<div key={apt.id} className={`absolute rounded p-3 border-l-4 shadow-sm group overflow-hidden transition-all ${colorClass} ${isPreview ? 'z-40' : 'hover:shadow-md hover:z-50'} ${isDragged ? 'opacity-0 pointer-events-none' : ''}`} style={{ left: getOffset(startTime), width: getWidth(apt.durationMinutes), height: EVENT_HEIGHT, top: topOffset, zIndex: isPreview ? 40 : 10 + apt.laneIndex, cursor: resizeState ? 'grabbing' : 'grab', pointerEvents: isPreview ? 'none' : 'auto' }} draggable={!resizeState && !isPreview} onDragStart={(e) => handleDragStart(e, apt.id)} onDragEnd={handleDragEnd} onClick={() => handleAppointmentClick(apt)}>
                    {!isPreview && (<><div className="absolute left-0 top-0 bottom-0 w-3 cursor-ew-resize bg-transparent hover:bg-blue-500/20 z-50" style={{ marginLeft: '-4px' }} onMouseDown={(e) => handleResizeStart(e, apt, 'start')} /><div className="absolute right-0 top-0 bottom-0 w-3 cursor-ew-resize bg-transparent hover:bg-blue-500/20 z-50" style={{ marginRight: '-4px' }} onMouseDown={(e) => handleResizeStart(e, apt, 'end')} /></>)}
                    <div className="font-semibold text-sm truncate pointer-events-none">{apt.customerName}</div><div className="text-xs truncate opacity-80 pointer-events-none">{service?.name}</div><div className="mt-2 flex items-center gap-1 text-xs opacity-75 pointer-events-none truncate">{apt.status === 'COMPLETED' ? <CheckCircle2 size={12} className="flex-shrink-0" /> : <Clock size={12} className="flex-shrink-0" />}<span className="truncate">{startTime.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}</span><span className="mx-1 flex-shrink-0">•</span><span className="truncate">{formatDuration(apt.durationMinutes)}</span></div>
                  </div>);
                })}</div>))}
              </div>
            </div>
          </div>
        </div>
      </div>
      )}

      {/* Appointment Detail/Edit Modal */}
      {selectedAppointment && (
        <Portal>
          <div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm" onClick={() => setSelectedAppointment(null)}>
            <div className="w-full max-w-lg bg-white dark:bg-gray-800 rounded-xl shadow-xl border border-gray-200 dark:border-gray-700 overflow-hidden" onClick={e => e.stopPropagation()}>
              <div className="flex items-center justify-between px-6 py-4 border-b border-gray-200 dark:border-gray-700 bg-gradient-to-r from-brand-50 to-brand-100 dark:from-brand-900/30 dark:to-brand-800/30">
                <h3 className="text-lg font-semibold text-gray-900 dark:text-white">
                  {!selectedAppointment.resourceId ? 'Schedule Appointment' : 'Edit Appointment'}
                </h3>
                <button onClick={() => setSelectedAppointment(null)} className="p-1 text-gray-400 hover:bg-white/50 dark:hover:bg-gray-700/50 rounded-full transition-colors">
                  <X size={20} />
                </button>
              </div>
            <div className="p-6 space-y-4">
                {/* Customer Info */}
                <div className="flex items-start gap-3 p-4 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
                  <div className="flex-shrink-0 w-10 h-10 rounded-full bg-brand-100 dark:bg-brand-900/50 flex items-center justify-center">
                    <UserIcon size={20} className="text-brand-600 dark:text-brand-400" />
                  </div>
                  <div className="flex-1 min-w-0">
                    <p className="text-sm font-medium text-gray-500 dark:text-gray-400">Customer</p>
                    <p className="text-lg font-semibold text-gray-900 dark:text-white">{selectedAppointment.customerName}</p>
                    {selectedAppointment.customerEmail && (
                      <div className="flex items-center gap-2 mt-1 text-sm text-gray-600 dark:text-gray-300">
                        <Mail size={14} />
                        <span>{selectedAppointment.customerEmail}</span>
                      </div>
                    )}
                    {selectedAppointment.customerPhone && (
                      <div className="flex items-center gap-2 mt-1 text-sm text-gray-600 dark:text-gray-300">
                        <Phone size={14} />
                        <span>{selectedAppointment.customerPhone}</span>
                      </div>
                    )}
                  </div>
                </div>

                {/* Service & Status */}
                <div className="grid grid-cols-2 gap-4">
                  <div className="p-3 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
                    <p className="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-1">Service</p>
                    <p className="text-sm font-semibold text-gray-900 dark:text-white">{services.find(s => s.id === selectedAppointment.serviceId)?.name}</p>
                  </div>
                  <div className="p-3 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
                    <p className="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-1">Status</p>
                    <p className="text-sm font-semibold text-gray-900 dark:text-white capitalize">{selectedAppointment.status.toLowerCase().replace('_', ' ')}</p>
                  </div>
                </div>

                {/* Editable Fields */}
                <div className="space-y-4 p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg border border-blue-200 dark:border-blue-800">
                  <h4 className="text-sm font-semibold text-gray-900 dark:text-white">Schedule Details</h4>

                  {/* Date & Time Picker */}
                  <div>
                    <label className="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-2">
                      Date & Time
                    </label>
                    <input
                      type="datetime-local"
                      value={editDateTime}
                      onChange={(e) => setEditDateTime(e.target.value)}
                      className="w-full px-3 py-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg text-sm text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-brand-500"
                    />
                  </div>

                  {/* Resource Selector */}
                  <div>
                    <label className="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-2">
                      Assign to Resource {!selectedAppointment.resourceId && <span className="text-red-500">*</span>}
                    </label>
                    <select
                      value={editResource}
                      onChange={(e) => setEditResource(e.target.value)}
                      className="w-full px-3 py-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg text-sm text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-brand-500"
                    >
                      <option value="">Unassigned</option>
                      {resources.map(resource => (
                        <option key={resource.id} value={resource.id}>
                          {resource.name}
                        </option>
                      ))}
                    </select>
                  </div>

                  {/* Duration Input */}
                  <div>
                    <label className="block text-xs font-medium text-gray-700 dark:text-gray-300 mb-2">
                      Duration (minutes) {!selectedAppointment.resourceId && <span className="text-red-500">*</span>}
                    </label>
                    <input
                      type="number"
                      min="15"
                      step="15"
                      value={editDuration || 15}
                      onChange={(e) => {
                        const value = parseInt(e.target.value);
                        setEditDuration(value >= 15 ? value : 15);
                      }}
                      className="w-full px-3 py-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg text-sm text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-brand-500"
                    />
                  </div>
                </div>

                {/* Notes */}
                {selectedAppointment.notes && (
                  <div className="p-3 bg-gray-50 dark:bg-gray-700/50 rounded-lg">
                    <p className="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-1">Notes</p>
                    <p className="text-sm text-gray-700 dark:text-gray-200">{selectedAppointment.notes}</p>
                  </div>
                )}

                {/* Automations - only show for saved appointments */}
                {selectedAppointment.id && (
                  <div className="p-3 bg-purple-50 dark:bg-purple-900/20 rounded-lg border border-purple-200 dark:border-purple-800">
                    <EventAutomations eventId={selectedAppointment.id} compact />
                  </div>
                )}

                {/* Action Buttons */}
                <div className="pt-4 flex justify-end gap-3 border-t border-gray-200 dark:border-gray-700">
                  <button
                    onClick={() => setSelectedAppointment(null)}
                    className="px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-200 bg-gray-100 dark:bg-gray-700 rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors"
                  >
                    Cancel
                  </button>
                  <button
                    onClick={handleSaveAppointment}
                    disabled={!selectedAppointment.resourceId && (!editResource || !editDuration || editDuration < 15)}
                    className="px-4 py-2 text-sm font-medium text-white bg-brand-600 rounded-lg hover:bg-brand-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
                  >
                    {!selectedAppointment.resourceId ? 'Schedule Appointment' : 'Save Changes'}
                  </button>
                </div>
              </div>
            </div>
          </div>
        </Portal>
      )}
    </div>
  );
};

export default OwnerScheduler;
