import { useState, useEffect } from "react";
import { useQuery, useMutation } from "@tanstack/react-query";
import { addDays, addWeeks, startOfToday, addMinutes } from "date-fns";
import {
  parseLocalDate,
  formatLocalDate,
  formatLocalTime,
  formatDateToHumanReadable,
} from "@/utils/dateUtils";
import { AddAppointmentModal } from "@/components/appointments/add-appointment-modal";
import { Button } from "@/components/ui/button";
import {
  Calendar as CalendarIcon,
  Plus,
  ChevronLeft,
  ChevronRight,
  ChevronsLeft,
  ChevronsRight,
  Move,
  Trash2,
  CreditCard,
  ClipboardList,
  StickyNote,
  Shield,
  FileCheck,
  LoaderCircleIcon,
  Stethoscope,
  Download,
} from "lucide-react";
import { useToast } from "@/hooks/use-toast";
import { Calendar } from "@/components/ui/calendar";
import { apiRequest, queryClient } from "@/lib/queryClient";
import { useAuth } from "@/hooks/use-auth";
import { DndProvider, useDrag, useDrop } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import { Menu, Item, useContextMenu } from "react-contexify";
import "react-contexify/ReactContexify.css";
import { useLocation } from "wouter";
import { DeleteConfirmationDialog } from "@/components/ui/deleteDialog";
import {
  Appointment,
  InsertAppointment,
  Patient,
  PatientStatus,
  UpdateAppointment,
  UpdatePatient,
} from "@repo/db/types";
import { ClaimForm } from "@/components/claims/claim-form";
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover";
import { Label } from "@/components/ui/label";
import { useAppDispatch, useAppSelector } from "@/redux/hooks";
import {
  clearTaskStatus,
  setTaskStatus,
} from "@/redux/slices/seleniumTaskSlice";
import { SeleniumTaskBanner } from "@/components/ui/selenium-task-banner";
import { PatientStatusBadge } from "@/components/appointments/patient-status-badge";
import type { OfficeHoursData } from "@/components/settings/office-hours-card";

// Define types for scheduling
interface TimeSlot {
  time: string;
  displayTime: string;
}

interface Staff {
  id: string;
  name: string;
  role: "doctor" | "hygienist";
  color: string;
}

interface ScheduledAppointment {
  id?: number;
  patientId: number;
  patientName: string;
  eligibilityStatus: PatientStatus;
  patientStatus: PatientStatus;
  patientInsuranceProvider: string | null;
  hasProcedures: boolean;
  hasClaimWithNumber: boolean;
  staffId: number;
  date: string | Date;
  startTime: string | Date;
  endTime: string | Date;
  status: string | null;
  type: string;
}

function appointmentCardColor(apt: ScheduledAppointment): string {
  if (apt.hasClaimWithNumber) return "bg-gray-500 text-white";
  if (apt.hasProcedures) return "bg-blue-500 text-white";
  return "bg-slate-100 text-gray-700 border-slate-300";
}

function resolveAppointmentBadgeStatus(apt: ScheduledAppointment): PatientStatus {
  if (apt.eligibilityStatus === "ACTIVE") return "ACTIVE";
  if (apt.eligibilityStatus === "INACTIVE") return "INACTIVE";
  const isMassHealth = apt.patientInsuranceProvider?.toLowerCase().includes("masshealth");
  if (apt.eligibilityStatus === "UNKNOWN" && isMassHealth) {
    if (apt.patientStatus === "ACTIVE") return "ACTIVE";
    if (apt.patientStatus === "INACTIVE") return "INACTIVE";
  }
  return apt.eligibilityStatus ?? "UNKNOWN";
}

// Define a unique ID for the appointment context menu
const APPOINTMENT_CONTEXT_MENU_ID = "appointment-context-menu";

// 🔑 exported base key
export const QK_APPOINTMENTS_BASE = ["appointments", "day"] as const;
// helper (optional) – mirrors the query key structure
export const qkAppointmentsDay = (date: string) =>
  [...QK_APPOINTMENTS_BASE, date] as const;

export default function AppointmentsPage() {
  const { toast } = useToast();
  const { user } = useAuth();
  const [isAddModalOpen, setIsAddModalOpen] = useState(false);
  const [calendarOpen, setCalendarOpen] = useState(false);
  const [editingAppointment, setEditingAppointment] = useState<
    Appointment | undefined
  >(undefined);
  const [selectedDate, setSelectedDate] = useState<Date>(startOfToday());
  const [location] = useLocation();
  const [confirmDeleteState, setConfirmDeleteState] = useState<{
    open: boolean;
    appointmentId?: number;
  }>({ open: false });
  const dispatch = useAppDispatch();
  const batchTask = useAppSelector(
    (state) => state.seleniumTasks.eligibilityBatchCheck
  );
  const claimBatchTask = useAppSelector(
    (state) => state.seleniumTasks.claimBatchCheck
  );
  const [isCheckingAllElig, setIsCheckingAllElig] = useState(false);
  const [processedAppointmentIds, setProcessedAppointmentIds] = useState<
    Record<number, boolean>
  >({});
  const [selectedStaffColumns, setSelectedStaffColumns] = useState<Set<number>>(new Set());

  const toggleStaffColumn = (staffId: number) => {
    setSelectedStaffColumns((prev) => {
      const next = new Set(prev);
      if (next.has(staffId)) next.delete(staffId);
      else next.add(staffId);
      return next;
    });
  };

  const [selectedClaimColumns, setSelectedClaimColumns] = useState<Set<number>>(new Set());
  const [isClaimingColumn, setIsClaimingColumn] = useState(false);
  const [selectedReminderColumns, setSelectedReminderColumns] = useState<Set<number>>(new Set());
  const [selectedDownloadPdfColumns, setSelectedDownloadPdfColumns] = useState<Set<number>>(new Set());
  const [isDownloadingClaimPdfs, setIsDownloadingClaimPdfs] = useState(false);
  const [columnLabels, setColumnLabels] = useState<Record<string, string>>({});
  const [editingLabelStaffId, setEditingLabelStaffId] = useState<string | null>(null);
  const [pendingOverride, setPendingOverride] = useState<{
    type: "move" | "create";
    appointmentId?: number;
    timeSlot: TimeSlot;
    staffId: number;
  } | null>(null);

  const toggleReminderColumn = (staffId: number) => {
    setSelectedReminderColumns((prev) => {
      const next = new Set(prev);
      if (next.has(staffId)) next.delete(staffId);
      else next.add(staffId);
      return next;
    });
  };

  const toggleClaimColumn = (staffId: number) => {
    setSelectedClaimColumns((prev) => {
      const next = new Set(prev);
      if (next.has(staffId)) next.delete(staffId);
      else next.add(staffId);
      return next;
    });
  };

  const toggleDownloadPdfColumn = (staffId: number) => {
    setSelectedDownloadPdfColumns((prev) => {
      const next = new Set(prev);
      if (next.has(staffId)) next.delete(staffId);
      else next.add(staffId);
      return next;
    });
  };

  const [, setLocation] = useLocation();

  // Select Procedures modal state (opened inline, no navigation)
  const [isSelectProceduresOpen, setIsSelectProceduresOpen] = useState(false);
  const [selectProceduresPatientId, setSelectProceduresPatientId] = useState<number | null>(null);
  const [selectProceduresAppointmentId, setSelectProceduresAppointmentId] = useState<number | null>(null);

  // Create context menu hook
  const { show } = useContextMenu({
    id: APPOINTMENT_CONTEXT_MENU_ID,
  });

  // ----------------------
  // Day-level fetch: appointments + patients for selectedDate (lightweight)
  // ----------------------
  const formattedSelectedDate = formatLocalDate(selectedDate);
  type DayPayload = { appointments: Appointment[]; patients: Patient[] };
  const {
    data: dayPayload = {
      appointments: [] as Appointment[],
      patients: [] as Patient[],
    },
    isLoading: isLoadingAppointments,
    refetch: refetchAppointments,
  } = useQuery<
    DayPayload,
    Error,
    DayPayload,
    readonly [string, string, string]
  >({
    queryKey: qkAppointmentsDay(formattedSelectedDate),
    queryFn: async () => {
      const res = await apiRequest(
        "GET",
        `/api/appointments/day?date=${formattedSelectedDate}`
      );
      if (!res.ok) {
        throw new Error("Failed to load appointments for date");
      }
      return res.json();
    },
    enabled: !!user && !!formattedSelectedDate,
    // placeholderData: keepPreviousData,
  });

  const appointments = dayPayload.appointments ?? [];
  const patientsFromDay = dayPayload.patients ?? [];

  // Office hours (used to enforce scheduling rules)
  const { data: officeHours } = useQuery<OfficeHoursData | null>({
    queryKey: ["/api/office-hours"],
    queryFn: async () => {
      const res = await apiRequest("GET", "/api/office-hours");
      if (!res.ok) return null;
      return res.json();
    },
    enabled: !!user,
  });

  // Staff memebers
  const { data: staffMembersRaw = [] as Staff[] } = useQuery<Staff[]>({
    queryKey: ["/api/staffs/"],
    queryFn: async () => {
      const res = await apiRequest("GET", "/api/staffs/");
      return res.json();
    },
    enabled: !!user,
  });

  const colors = [
    "bg-sky-500", // light blue
    "bg-teal-500", // teal
    "bg-indigo-500", // soft indigo
    "bg-rose-400", // muted rose
    "bg-amber-400", // muted amber
    "bg-orange-500", // softer warm orange
  ];

  // Assign colors cycling through the list
  const staffMembers = staffMembersRaw.map((staff, index) => ({
    ...staff,

    color: colors[index % colors.length] || "bg-gray-400",
  }));

  // Initialize column labels from localStorage; default A, B, C… for new staff
  useEffect(() => {
    if (!staffMembersRaw.length) return;
    const stored = JSON.parse(
      localStorage.getItem("scheduleColumnLabels") || "{}"
    ) as Record<string, string>;
    let changed = false;
    staffMembersRaw.forEach((staff, index) => {
      if (!(String(staff.id) in stored)) {
        stored[String(staff.id)] = String.fromCharCode(65 + index);
        changed = true;
      }
    });
    if (changed) localStorage.setItem("scheduleColumnLabels", JSON.stringify(stored));
    setColumnLabels(stored);
  }, [staffMembersRaw]);

  const handleLabelSave = (staffId: string, value: string) => {
    const trimmed = value.trim() || String.fromCharCode(65 + staffMembersRaw.findIndex((s) => String(s.id) === staffId));
    setColumnLabels((prev) => {
      const updated = { ...prev, [staffId]: trimmed };
      localStorage.setItem("scheduleColumnLabels", JSON.stringify(updated));
      return updated;
    });
    setEditingLabelStaffId(null);
  };

  // Returns true when a time slot is within the configured office hours for that staff group
  const isWithinOfficeHours = (timeStr: string, staffIndex: number): boolean => {
    if (!officeHours) return true; // no config = unrestricted
    // If today is an override date, all slots are open
    const todayYMD = selectedDate.toLocaleDateString("en-CA");
    if (officeHours.overrideDates?.includes(todayYMD)) return true;
    const dayNames = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"] as const;
    const dayName = dayNames[selectedDate.getDay()] as keyof typeof officeHours.doctors;
    const group = staffIndex <= 2 ? officeHours.doctors : officeHours.hygienists;
    const dayHours = group[dayName];
    if (!dayHours || !dayHours.enabled) return false;
    return (
      (timeStr >= dayHours.amStart && timeStr <= dayHours.amEnd) ||
      (timeStr >= dayHours.pmStart && timeStr <= dayHours.pmEnd)
    );
  };

  // Generate time slots from 8:00 AM to 9:00 PM in 15-minute increments
  const timeSlots: TimeSlot[] = [];
  for (let hour = 8; hour <= 21; hour++) {
    for (let minute = 0; minute < 60; minute += 15) {
      const pad = (n: number) => n.toString().padStart(2, "0");
      const timeStr = `${pad(hour)}:${pad(minute)}`;

      // Only allow start times up to 21:00 (9 PM)
      if (timeStr > "21:00") continue;

      const hour12 = hour > 12 ? hour - 12 : hour;
      const period = hour >= 12 ? "PM" : "AM";
      const displayTime = `${hour12}:${pad(minute)} ${period}`;
      timeSlots.push({ time: timeStr, displayTime });
    }
  }

  // Check for newPatient parameter in URL
  useEffect(() => {
    // Parse URL search params to check for newPatient
    const params = new URLSearchParams(window.location.search);
    const newPatientId = params.get("newPatient");

    if (newPatientId) {
      const patientId = parseInt(newPatientId);
      // Choose first available staff safely (fallback to 1 if none)
      const firstStaff =
        staffMembers && staffMembers.length > 0 ? staffMembers[0] : undefined;
      const staffId = firstStaff ? Number(firstStaff.id) : 1;
      // Find first time slot today (9:00 AM is a common starting time)
      const defaultTimeSlot =
        timeSlots.find((slot) => slot.time === "09:00") || timeSlots[0];
      if (!defaultTimeSlot) {
        toast({
          title: "Unable to schedule",
          description:
            "No available time slots to schedule the new patient right now.",
          variant: "destructive",
        });
        return;
      }

      // Merge any existing "newAppointmentData" with the patient info BEFORE opening modal
      try {
        const existingRaw = sessionStorage.getItem("newAppointmentData");
        const existing = existingRaw ? JSON.parse(existingRaw) : {};
        const newAppointmentData = {
          ...existing,
          patientId: patientId,
        };
        sessionStorage.setItem(
          "newAppointmentData",
          JSON.stringify(newAppointmentData)
        );
      } catch (err) {
        // If sessionStorage parsing fails, overwrite with a fresh object
        sessionStorage.setItem(
          "newAppointmentData",
          JSON.stringify({ patientId: patientId })
        );
      }

      // Open/create the appointment modal (will read sessionStorage in the modal)
      handleCreateAppointmentAtSlot(defaultTimeSlot, Number(staffId));

      // Remove the query param from the URL so this doesn't re-run on navigation/refresh
      params.delete("newPatient");
      const newSearch = params.toString();
      const newUrl = `${window.location.pathname}${newSearch ? `?${newSearch}` : ""}${window.location.hash || ""}`;
      window.history.replaceState({}, "", newUrl);
    }
  }, [location]);

  // Create/upsert appointment mutation
  const createAppointmentMutation = useMutation({
    mutationFn: async (appointment: InsertAppointment) => {
      const res = await apiRequest(
        "POST",
        "/api/appointments/upsert",
        appointment
      );
      const body = await res.json();
      if (!res.ok) throw new Error(body?.message || "Failed to create appointment");
      return body;
    },
    onSuccess: (appointment) => {
      toast({
        title: "Appointment Scheduled",
        description: appointment.message || "Appointment created successfully.",
      });
      // Invalidate both appointments and patients queries
      queryClient.invalidateQueries({
        queryKey: qkAppointmentsDay(formattedSelectedDate),
      });
      setIsAddModalOpen(false);
    },
    onError: (error: Error) => {
      toast({
        title: "Error",
        description: `Failed to create appointment: ${error.message}`,
        variant: "destructive",
      });
    },
  });

  // Update appointment mutation
  const updateAppointmentMutation = useMutation({
    mutationFn: async ({
      id,
      appointment,
    }: {
      id: number;
      appointment: UpdateAppointment;
    }) => {
      const res = await apiRequest(
        "PUT",
        `/api/appointments/${id}`,
        appointment
      );
      return await res.json();
    },
    onSuccess: (appointment) => {
      toast({
        title: "Appointment Scheduled",
        description: appointment.message || "Appointment updated successfully.",
      });
      queryClient.invalidateQueries({
        queryKey: qkAppointmentsDay(formattedSelectedDate),
      });
      setEditingAppointment(undefined);
      setIsAddModalOpen(false);
    },
    onError: (error: Error) => {
      toast({
        title: "Error",
        description: `Failed to update appointment: ${error.message}`,
        variant: "destructive",
      });
    },
  });

  // Delete appointment mutation
  const deleteAppointmentMutation = useMutation({
    mutationFn: async (id: number) => {
      await apiRequest("DELETE", `/api/appointments/${id}`);
    },
    onSuccess: () => {
      toast({
        title: "Success",
        description: "Appointment deleted successfully.",
      });
      // Invalidate both appointments and patients queries
      queryClient.invalidateQueries({
        queryKey: qkAppointmentsDay(formattedSelectedDate),
      });
      setConfirmDeleteState({ open: false });
    },
    onError: (error: Error) => {
      toast({
        title: "Error",
        description: `Failed to delete appointment: ${error.message}`,
        variant: "destructive",
      });
    },
  });

  // Handle appointment submission (create or update)
  const handleAppointmentSubmit = (
    appointmentData: InsertAppointment | UpdateAppointment
  ) => {
    // Converts local date to exact UTC date with no offset issues
    const rawDate = parseLocalDate(appointmentData.date);

    const updatedData = {
      ...appointmentData,
      date: formatLocalDate(rawDate),
    };

    // Check if we're editing an existing appointment with a valid ID
    if (
      editingAppointment &&
      "id" in editingAppointment &&
      typeof editingAppointment.id === "number"
    ) {
      updateAppointmentMutation.mutate({
        id: editingAppointment.id,
        appointment: updatedData as unknown as UpdateAppointment,
      });
    } else {
      // This is a new appointment
      if (user) {
        createAppointmentMutation.mutate({
          ...(updatedData as unknown as InsertAppointment),
          userId: user.id,
        });
      }
    }
  };

  // Handle edit appointment
  const handleEditAppointment = (appointment: Appointment) => {
    setEditingAppointment(appointment);
    setIsAddModalOpen(true);
  };

  // When user confirms delete in dialog
  const handleConfirmDelete = () => {
    if (!confirmDeleteState.appointmentId) return;
    deleteAppointmentMutation.mutate(confirmDeleteState.appointmentId);
  };

  const handleDeleteAppointment = (id: number) => {
    setConfirmDeleteState({
      open: true,
      appointmentId: id,
    });
  };

  // Process appointments for the scheduler view
  const processedAppointments: ScheduledAppointment[] = (
    appointments ?? []
  ).map((apt) => {
    // Find patient name
    const patient = patientsFromDay.find((p) => p.id === apt.patientId);

    const patientName = patient
      ? `${patient.firstName} ${patient.lastName}`
      : "Unknown Patient";

    const eligibilityStatus = (apt as any).eligibilityStatus as PatientStatus;
    const patientStatus = (patient as any)?.status as PatientStatus ?? "UNKNOWN";
    const patientInsuranceProvider = (patient as any)?.insuranceProvider as string | null ?? null;

    const staffId = Number(apt.staffId ?? 1);

    const normalizedStart =
      typeof apt.startTime === "string"
        ? apt.startTime.substring(0, 5)
        : formatLocalTime(apt.startTime);
    const normalizedEnd =
      typeof apt.endTime === "string"
        ? apt.endTime.substring(0, 5)
        : formatLocalTime(apt.endTime);

    const processed = {
      ...apt,
      patientName,
      eligibilityStatus,
      patientStatus,
      patientInsuranceProvider,
      hasProcedures: !!(apt as any).hasProcedures,
      hasClaimWithNumber: !!(apt as any).hasClaimWithNumber,
      staffId,
      status: apt.status ?? null,
      date: formatLocalDate(apt.date),
      startTime: normalizedStart,
      endTime: normalizedEnd,
    } as ScheduledAppointment;

    return processed;
  });

  // Compute how many 15-min slots an appointment occupies
  const getSlotSpan = (apt: ScheduledAppointment): number => {
    const startStr = (typeof apt.startTime === "string" ? apt.startTime : formatLocalTime(apt.startTime)).substring(0, 5);
    const endStr = (typeof apt.endTime === "string" ? apt.endTime : formatLocalTime(apt.endTime)).substring(0, 5);
    const startParts = startStr.split(":");
    const endParts = endStr.split(":");
    const startH = parseInt(startParts[0] ?? "0", 10);
    const startM = parseInt(startParts[1] ?? "0", 10);
    const endH = parseInt(endParts[0] ?? "0", 10);
    const endM = parseInt(endParts[1] ?? "0", 10);
    const diff = (endH * 60 + endM) - (startH * 60 + startM);
    return Math.max(1, Math.round(diff / 15));
  };

  // Slots that are "continued" rows of a multi-slot appointment (should not render a td)
  const coveredSlots = new Set<string>();
  (processedAppointments ?? []).forEach((apt) => {
    const span = getSlotSpan(apt);
    if (span <= 1) return;
    const startStr = (typeof apt.startTime === "string" ? apt.startTime : formatLocalTime(apt.startTime)).substring(0, 5);
    const [startH, startM] = startStr.split(":").map(Number);
    for (let i = 1; i < span; i++) {
      const totalMin = (startH ?? 0) * 60 + (startM ?? 0) + i * 15;
      const h = Math.floor(totalMin / 60).toString().padStart(2, "0");
      const m = (totalMin % 60).toString().padStart(2, "0");
      coveredSlots.add(`${h}:${m}-${apt.staffId}`);
    }
  });

  // Check if appointment exists at a specific time slot and staff
  const getAppointmentAtSlot = (timeSlot: TimeSlot, staffId: number) => {
    if (!processedAppointments || processedAppointments.length === 0)
      return undefined;

    // In appointments for a given time slot, we'll just display the first one
    // In a real application, you might want to show multiple or stack them
    const appointmentsAtSlot = processedAppointments.filter((apt) => {
      const dbTime =
        typeof apt.startTime === "string" ? apt.startTime.substring(0, 5) : "";
      const timeMatches = dbTime === timeSlot.time;
      const staffMatches = apt.staffId === staffId;
      return timeMatches && staffMatches;
    });

    return appointmentsAtSlot.length > 0 ? appointmentsAtSlot[0] : undefined;
  };

  const isLoading =
    isLoadingAppointments ||
    createAppointmentMutation.isPending ||
    updateAppointmentMutation.isPending ||
    deleteAppointmentMutation.isPending;

  // Define drag item types
  const ItemTypes = {
    APPOINTMENT: "appointment",
  };

  // Handle creating a new appointment at a specific time slot and for a specific staff member
  const handleCreateAppointmentAtSlot = (
    timeSlot: TimeSlot,
    staffId: number
  ) => {
    // Calculate end time (30 minutes after start time)
    const startHour = parseInt(timeSlot.time.split(":")[0] as string);
    const startMinute = parseInt(timeSlot.time.split(":")[1] as string);
    const startDate = parseLocalDate(selectedDate);
    startDate.setHours(startHour, startMinute, 0);

    const endDate = addMinutes(startDate, 30);
    const endTime = `${endDate.getHours().toString().padStart(2, "0")}:${endDate.getMinutes().toString().padStart(2, "0")}`;

    // Find staff member
    const staff = staffMembers.find((s) => Number(s.id) === Number(staffId));

    // Try to read any existing prefill data (may include patientId from the URL handler)
    let existingStored: any = null;
    try {
      const raw = sessionStorage.getItem("newAppointmentData");
      existingStored = raw ? JSON.parse(raw) : null;
    } catch (e) {
      // ignore parse errors and treat as no existing stored data
      existingStored = null;
    }

    // Build the prefill appointment object and merge existing stored data
    const newAppointment = {
      // base defaults
      date: formatLocalDate(selectedDate),
      startTime: timeSlot.time, // This is in "HH:MM" format
      endTime: endTime,
      type: staff?.role === "doctor" ? "checkup" : "cleaning",
      status: "scheduled",
      title: `Appointment with ${staff?.name}`,
      notes: `Appointment with ${staff?.name}`,
      staff: Number(staffId), // consistent field name that matches update mutation
      // if existingStored has patientId (or other fields) merge them below
      ...(existingStored || {}),
    };

    // Ensure explicit values from this function override stale values from storage
    // (for example, prefer current slot and staff)
    const mergedAppointment = {
      ...newAppointment,
      date: newAppointment.date,
      startTime: newAppointment.startTime,
      endTime: newAppointment.endTime,
      staff: Number(staffId),
    };

    // Persist merged prefill so the modal/form can read it
    try {
      sessionStorage.setItem(
        "newAppointmentData",
        JSON.stringify(mergedAppointment)
      );
    } catch (e) {
      // ignore sessionStorage write failures
      console.error("Failed to write newAppointmentData to sessionStorage", e);
    }

    // For new appointments, set editingAppointment to undefined
    setEditingAppointment(undefined);

    // Open modal
    setIsAddModalOpen(true);
  };

  // Handle moving an appointment to a new time slot and staff
  const handleMoveAppointment = (
    appointmentId: number,
    newTimeSlot: TimeSlot,
    newStaffId: number
  ) => {
    const appointment = appointments.find((a) => a.id === appointmentId);
    if (!appointment) return;

    // Calculate new end time (30 minutes from start)
    const startHour = parseInt(newTimeSlot.time.split(":")[0] as string);
    const startMinute = parseInt(newTimeSlot.time.split(":")[1] as string);
    const startDate = parseLocalDate(selectedDate);
    startDate.setHours(startHour, startMinute, 0);

    const endDate = addMinutes(startDate, 30);
    const endTime = `${endDate.getHours().toString().padStart(2, "0")}:${endDate.getMinutes().toString().padStart(2, "0")}`;

    // Find staff member
    const staff = staffMembers.find((s) => Number(s.id) === newStaffId);

    // Send only the real DB fields — the appointment object may contain computed
    // fields (hasProcedures, hasClaimWithNumber, etc.) that the Zod schema rejects
    const apt = appointment as any;
    const updatedAppointment: UpdateAppointment = {
      patientId: apt.patientId,
      userId: apt.userId,
      staffId: newStaffId,
      title: apt.title,
      date: apt.date,
      type: apt.type,
      status: apt.status ?? undefined,
      startTime: newTimeSlot.time,
      endTime: endTime,
      notes: `Appointment with ${staff?.name}`,
    };
    // Call update mutation
    updateAppointmentMutation.mutate({
      id: appointmentId,
      appointment: updatedAppointment,
    });
  };

  // Function to display context menu
  const handleContextMenu = (e: React.MouseEvent, appointmentId: number) => {
    // Prevent the default browser context menu
    e.preventDefault();

    // Show our custom context menu with appointment ID as data
    show({
      event: e,
      props: {
        appointmentId,
      },
    });
  };

  // Create a draggable appointment component
  function DraggableAppointment({
    appointment,
    staff,
  }: {
    appointment: ScheduledAppointment;
    staff: Staff;
  }) {
    const [{ isDragging }, drag] = useDrag(() => ({
      type: ItemTypes.APPOINTMENT,
      item: { id: appointment.id },
      collect: (monitor) => ({
        isDragging: !!monitor.isDragging(),
      }),
    }));

    return (
      <div
        ref={drag as unknown as React.RefObject<HTMLDivElement>} // Type assertion to make TypeScript happy
        className={`${appointmentCardColor(appointment)} border shadow-md rounded p-1 text-xs h-full overflow-visible cursor-move relative ${
          isDragging ? "opacity-50" : "opacity-100"
        }`}
        style={{ fontWeight: 500 }}
        onClick={(e) => {
          // Only allow edit on click if we're not dragging
          if (!isDragging) {
            const fullAppointment = appointments.find(
              (a) => a.id === appointment.id
            );
            if (fullAppointment) {
              e.stopPropagation();
              handleEditAppointment(fullAppointment);
            }
          }
        }}
        onContextMenu={(e) => handleContextMenu(e, appointment.id ?? 0)}
      >
        <PatientStatusBadge
          status={resolveAppointmentBadgeStatus(appointment)}
          className="pointer-events-auto" // ensure tooltip works
          size={30} // bump size up from 10 → 14
        />

        <div className="font-bold truncate flex items-center gap-1">
          <Move className="h-3 w-3" />
          {appointment.patientName}
        </div>
        <div className="truncate">{appointment.type}</div>
      </div>
    );
  }

  // Create a drop target for appointments
  function DroppableTimeSlot({
    timeSlot,
    staffId,
    staffIndex,
    appointment,
    staff,
    rowSpan = 1,
  }: {
    timeSlot: TimeSlot;
    staffId: number;
    staffIndex: number;
    appointment: ScheduledAppointment | undefined;
    staff: Staff;
    rowSpan?: number;
  }) {
    const blocked = !isWithinOfficeHours(timeSlot.time, staffIndex);

    const [{ isOver, canDrop }, drop] = useDrop(() => ({
      accept: ItemTypes.APPOINTMENT,
      drop: (item: { id: number }) => {
        if (blocked) {
          // Store plain data — never store callbacks in state across re-renders
          setPendingOverride({ type: "move", appointmentId: item.id, timeSlot, staffId });
        } else {
          handleMoveAppointment(item.id, timeSlot, staffId);
        }
      },
      canDrop: () => !appointment,
      collect: (monitor) => ({
        isOver: !!monitor.isOver(),
        canDrop: !!monitor.canDrop(),
      }),
    }));

    const handleClickEmpty = () => {
      if (blocked) {
        setPendingOverride({ type: "create", timeSlot, staffId });
      } else {
        handleCreateAppointmentAtSlot(timeSlot, staffId);
      }
    };

    return (
      <td
        ref={drop as unknown as React.RefObject<HTMLTableCellElement>}
        key={`${timeSlot.time}-${staffId}`}
        className={`px-1 py-1 border relative h-14 ${
          isOver && canDrop ? "bg-green-100" : blocked ? "bg-gray-100" : ""
        }`}
        rowSpan={rowSpan > 1 ? rowSpan : undefined}
        title={blocked ? "Outside office hours — click to override" : undefined}
      >
        {appointment ? (
          <DraggableAppointment appointment={appointment} staff={staff} />
        ) : (
          <button
            className={`w-full h-full rounded flex items-center justify-center ${
              isOver && canDrop
                ? "bg-green-100"
                : blocked
                ? "text-gray-300 hover:bg-gray-200"
                : "text-gray-400 hover:bg-gray-100"
            }`}
            onClick={handleClickEmpty}
          >
            <Plus className="h-4 w-4" />
          </button>
        )}
      </td>
    );
  }

  // -------------------
  // appointment page — update these handlers
  const handleCheckEligibility = (appointmentId: number) => {
    setLocation(
      `/insurance-status?appointmentId=${appointmentId}&action=eligibility`
    );
  };

  const handleCheckClaimStatus = (appointmentId: number) => {
    setLocation(
      `/insurance-status?appointmentId=${appointmentId}&action=claim`
    );
  };

  const handleClaimsPreAuth = (appointmentId: number) => {
    setLocation(`/claims?appointmentId=${appointmentId}`);
  };

  const handleSelectProcedures = (appointmentId: number) => {
    const appt = appointments.find((a) => a.id === appointmentId);
    if (!appt) return;
    setSelectProceduresAppointmentId(appointmentId);
    setSelectProceduresPatientId(appt.patientId);
    setIsSelectProceduresOpen(true);
  };

  const handlePayments = (appointmentId: number) => {
    setLocation(`/payments?appointmentId=${appointmentId}`);
  };

  const handleChartPlan = (appointmentId: number) => {
    console.log(
      `Viewing chart/treatment plan for appointment: ${appointmentId}`
    );
  };

  const handleClinicNotes = (appointmentId: number) => {
    console.log(`Opening clinic notes for appointment: ${appointmentId}`);
  };

  const handleCheckAllEligibilities = async () => {
    if (!user) {
      toast({
        title: "Unauthorized",
        description: "Please login to perform this action.",
        variant: "destructive",
      });
      return;
    }

    const dateParam = formattedSelectedDate; // existing variable in your component

    const staffIdsParam = `&staffIds=${Array.from(selectedStaffColumns).join(",")}`;

    // Start: set redux task status (visible globally)
    dispatch(
      setTaskStatus({
        key: "eligibilityBatchCheck",
        status: "pending",
        message: `Checking eligibility for appointments on ${dateParam}...`,
      })
    );

    setIsCheckingAllElig(true);
    setProcessedAppointmentIds({});

    try {
      const res = await apiRequest(
        "POST",
        `/api/insurance-status/appointments/check-all-eligibilities?date=${dateParam}${staffIdsParam}`,
        {}
      );

      // read body for all cases so we can show per-appointment info
      let body: any;
      try {
        body = await res.json();
      } catch (e) {
        body = null;
      }

      if (!res.ok) {
        const errMsg = body?.error ?? `Server error ${res.status}`;
        // global error
        dispatch(
          setTaskStatus({
            key: "eligibilityBatchCheck",
            status: "error",
            message: `Batch eligibility failed: ${errMsg}`,
          })
        );
        toast({
          title: "Batch check failed",
          description: errMsg,
          variant: "destructive",
        });
        return;
      }

      const results: any[] = Array.isArray(body?.results) ? body.results : [];

      // Map appointmentId -> appointment so we can show human friendly toasts
      const appointmentMap = new Map<number, Appointment>();
      for (const a of appointments) {
        if (a && typeof a.id === "number")
          appointmentMap.set(a.id as number, a);
      }

      // Counters for summary
      let successCount = 0;
      let skippedCount = 0;
      let warningCount = 0;

      // Show toast for each skipped appointment (error) and for warnings.
      for (const r of results) {
        const aptId = Number(r.appointmentId);
        const apt = appointmentMap.get(aptId);
        const patientName = apt
          ? patientsFromDay.find((p) => p.id === apt.patientId)
            ? `${patientsFromDay.find((p) => p.id === apt.patientId)!.firstName ?? ""} ${patientsFromDay.find((p) => p.id === apt.patientId)!.lastName ?? ""}`.trim()
            : `patient#${apt.patientId ?? "?"}`
          : `appointment#${aptId}`;

        const aptTime = apt ? `${apt.date ?? ""} ${apt.startTime ?? ""}` : "";

        if (r.error) {
          skippedCount++;
          toast({
            title: `Skipped: ${patientName}`,
            description: `${aptTime} — ${r.error}`,
            variant: "destructive",
          });
          console.warn("[batch skipped]", aptId, r.error);
        } else if (r.warning) {
          warningCount++;
          toast({
            title: `Warning: ${patientName}`,
            description: `${aptTime} — ${r.warning}`,
            variant: "destructive",
          });
          console.info("[batch warning]", aptId, r.warning);
        } else if (r.processed) {
          successCount++;
          // optional: show small non-intrusive toast or nothing for each success.
          // comment-in to notify successes (may create many toasts):
          // toast({ title: `Processed: ${patientName}`, description: `${aptTime}`, variant: "default" });
        } else {
          // fallback: treat as skipped
          skippedCount++;
          toast({
            title: `Skipped: ${patientName}`,
            description: `${aptTime} — Unknown reason`,
            variant: "destructive",
          });
        }
      }

      // Invalidate queries so UI repaints with updated patient statuses
      queryClient.invalidateQueries({
        queryKey: qkAppointmentsDay(formattedSelectedDate),
      });

      // global success status (summary)
      dispatch(
        setTaskStatus({
          key: "eligibilityBatchCheck",
          status: skippedCount > 0 ? "error" : "success",
          message: `Batch processed ${results.length} appointments — success: ${successCount}, warnings: ${warningCount}, skipped: ${skippedCount}.`,
        })
      );

      // also show final toast summary
      toast({
        title: "Batch complete",
        description: `Processed ${results.length} appointments — success: ${successCount}, warnings: ${warningCount}, skipped: ${skippedCount}.`,
        variant: skippedCount > 0 ? "destructive" : "default",
      });
    } catch (err: any) {
      console.error("[check-all-eligibilities] error", err);
      dispatch(
        setTaskStatus({
          key: "eligibilityBatchCheck",
          status: "error",
          message: `Batch eligibility error: ${err?.message ?? String(err)}`,
        })
      );
      toast({
        title: "Batch check failed",
        description: err?.message ?? String(err),
        variant: "destructive",
      });
    } finally {
      setIsCheckingAllElig(false);
      // intentionally do not clear task status here so banner persists until user dismisses it
    }
  };

  const handleClaimForColumn = async () => {
    if (!user || selectedClaimColumns.size === 0) return;

    const staffIdsParam = Array.from(selectedClaimColumns).join(",");

    dispatch(
      setTaskStatus({
        key: "claimBatchCheck",
        status: "pending",
        message: `Submitting claims for selected columns on ${formattedSelectedDate}...`,
      })
    );
    setIsClaimingColumn(true);

    try {
      const res = await apiRequest(
        "POST",
        `/api/claims/batch-column?date=${formattedSelectedDate}&staffIds=${staffIdsParam}`,
        {}
      );

      let body: any;
      try { body = await res.json(); } catch { body = null; }

      if (!res.ok) {
        const errMsg = body?.error ?? `Server error ${res.status}`;
        dispatch(setTaskStatus({ key: "claimBatchCheck", status: "error", message: `Batch claim failed: ${errMsg}` }));
        toast({ title: "Batch claim failed", description: errMsg, variant: "destructive" });
        return;
      }

      const results: any[] = Array.isArray(body?.results) ? body.results : [];
      const appointmentMap = new Map<number, Appointment>();
      for (const a of appointments) {
        if (a && typeof a.id === "number") appointmentMap.set(a.id, a);
      }

      let queued = 0, skippedNoProcedures = 0, skippedAlreadyClaimed = 0, errCount = 0;

      for (const r of results) {
        const aptId = Number(r.appointmentId);
        const apt = appointmentMap.get(aptId);
        const patientName = apt
          ? patientsFromDay.find((p) => p.id === apt.patientId)
            ? `${patientsFromDay.find((p) => p.id === apt.patientId)!.firstName ?? ""} ${patientsFromDay.find((p) => p.id === apt.patientId)!.lastName ?? ""}`.trim()
            : `patient#${apt.patientId}`
          : `appointment#${aptId}`;

        if (r.skipped && r.error === "Already claimed") {
          skippedAlreadyClaimed++;
        } else if (r.skipped) {
          skippedNoProcedures++;
        } else if (r.error) {
          errCount++;
          toast({ title: `Skipped: ${patientName}`, description: r.error, variant: "destructive" });
        } else if (r.processed) {
          queued++;
        }
      }

      queryClient.invalidateQueries({ queryKey: qkAppointmentsDay(formattedSelectedDate) });

      dispatch(
        setTaskStatus({
          key: "claimBatchCheck",
          status: errCount > 0 ? "error" : "success",
          message: `Claims queued: ${queued}, already claimed: ${skippedAlreadyClaimed}, no procedures: ${skippedNoProcedures}, errors: ${errCount}.`,
        })
      );
      toast({
        title: "Claim batch queued",
        description: `Queued: ${queued}, already claimed: ${skippedAlreadyClaimed}, no procedures: ${skippedNoProcedures}, errors: ${errCount}.`,
        variant: errCount > 0 ? "destructive" : "default",
      });
    } catch (err: any) {
      dispatch(setTaskStatus({ key: "claimBatchCheck", status: "error", message: `Batch claim error: ${err?.message ?? String(err)}` }));
      toast({ title: "Batch claim failed", description: err?.message ?? String(err), variant: "destructive" });
    } finally {
      setIsClaimingColumn(false);
    }
  };

  const handleDownloadClaimPdfs = async () => {
    if (!user || selectedDownloadPdfColumns.size === 0) return;
    const staffIdsParam = Array.from(selectedDownloadPdfColumns).join(",");
    setIsDownloadingClaimPdfs(true);
    try {
      const res = await apiRequest(
        "GET",
        `/api/claims/batch-pdf?date=${formattedSelectedDate}&staffIds=${staffIdsParam}`
      );
      if (!res.ok) {
        let errMsg = `Server error ${res.status}`;
        try { const body = await res.json(); errMsg = body?.error ?? errMsg; } catch { /* ignore */ }
        toast({ title: "Download failed", description: errMsg, variant: "destructive" });
        return;
      }
      const blob = await res.blob();
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.href = url;
      a.download = `claims_${formattedSelectedDate}.zip`;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(url);
      toast({ title: "Download started", description: `Claim PDFs for ${formattedSelectedDate} saved to your Downloads folder.` });
    } catch (err: any) {
      toast({ title: "Download failed", description: err?.message ?? String(err), variant: "destructive" });
    } finally {
      setIsDownloadingClaimPdfs(false);
    }
  };

  return (
    <div>
      <SeleniumTaskBanner
        status={batchTask.status}
        message={batchTask.message}
        show={batchTask.show}
        onClear={() => dispatch(clearTaskStatus("eligibilityBatchCheck"))}
      />
      <SeleniumTaskBanner
        status={claimBatchTask.status}
        message={claimBatchTask.message}
        show={claimBatchTask.show}
        onClear={() => dispatch(clearTaskStatus("claimBatchCheck"))}
      />

      <div className="container mx-auto">
        <div className="flex justify-between items-center mb-6">
          <div>
            <h1 className="text-3xl font-bold tracking-tight">
              Appointment Schedule
            </h1>
            <p className="text-muted-foreground">
              View and manage the dental practice schedule
            </p>
          </div>

          <div className="flex items-center gap-3 flex-wrap">
            <Button
              onClick={() => {
                setEditingAppointment(undefined);
                setIsAddModalOpen(true);
              }}
              disabled={isLoading}
            >
              <Plus className="h-4 w-4" />
              New Appointment
            </Button>

            {/* Check Eligibility for Column section */}
            <div className="flex items-center gap-2 border rounded-md px-3 py-2 bg-white shadow-sm">
              <Button
                onClick={() => handleCheckAllEligibilities()}
                disabled={isLoading || isCheckingAllElig || selectedStaffColumns.size === 0}
                size="sm"
              >
                {isCheckingAllElig ? (
                  <>
                    <LoaderCircleIcon className="h-4 w-4 mr-1 animate-spin" />
                    Checking...
                  </>
                ) : (
                  <>
                    <Shield className="h-4 w-4 mr-1" />
                    Check Eligibility for Column
                  </>
                )}
              </Button>
              {staffMembers.map((staff, index) => (
                <label
                  key={staff.id}
                  className="flex items-center gap-1 cursor-pointer select-none"
                >
                  <input
                    type="checkbox"
                    className="w-4 h-4 rounded border-gray-400 accent-teal-600"
                    checked={selectedStaffColumns.has(Number(staff.id))}
                    onChange={() => toggleStaffColumn(Number(staff.id))}
                  />
                  <span className="text-sm font-medium">
                    {columnLabels[String(staff.id)] ?? String.fromCharCode(65 + index)}
                  </span>
                </label>
              ))}
            </div>

            {/* Claim for Column section */}
            <div className="flex items-center gap-2 border rounded-md px-3 py-2 bg-white shadow-sm">
              <Button
                onClick={() => handleClaimForColumn()}
                disabled={isLoading || isClaimingColumn || selectedClaimColumns.size === 0}
                size="sm"
              >
                {isClaimingColumn ? (
                  <>
                    <LoaderCircleIcon className="h-4 w-4 mr-1 animate-spin" />
                    Submitting...
                  </>
                ) : (
                  <>
                    <FileCheck className="h-4 w-4 mr-1" />
                    Claim for Column
                  </>
                )}
              </Button>
              {staffMembers.map((staff, index) => (
                <label
                  key={staff.id}
                  className="flex items-center gap-1 cursor-pointer select-none"
                >
                  <input
                    type="checkbox"
                    className="w-4 h-4 rounded border-gray-400 accent-teal-600"
                    checked={selectedClaimColumns.has(Number(staff.id))}
                    onChange={() => toggleClaimColumn(Number(staff.id))}
                  />
                  <span className="text-sm font-medium">
                    {columnLabels[String(staff.id)] ?? String.fromCharCode(65 + index)}
                  </span>
                </label>
              ))}
            </div>

            {/* Text Reminder for Column section */}
            <div className="flex items-center gap-2 border rounded-md px-3 py-2 bg-white shadow-sm">
              <Button
                disabled={true}
                size="sm"
              >
                Text Reminder for Column
              </Button>
              {staffMembers.map((staff, index) => (
                <label
                  key={staff.id}
                  className="flex items-center gap-1 cursor-pointer select-none"
                >
                  <input
                    type="checkbox"
                    className="w-4 h-4 rounded border-gray-400 accent-teal-600"
                    checked={selectedReminderColumns.has(Number(staff.id))}
                    onChange={() => toggleReminderColumn(Number(staff.id))}
                  />
                  <span className="text-sm font-medium">
                    {columnLabels[String(staff.id)] ?? String.fromCharCode(65 + index)}
                  </span>
                </label>
              ))}
            </div>

            {/* Download Claim PDF for Column section */}
            <div className="flex items-center gap-2 border rounded-md px-3 py-2 bg-white shadow-sm">
              <Button
                onClick={() => handleDownloadClaimPdfs()}
                disabled={isLoading || isDownloadingClaimPdfs || selectedDownloadPdfColumns.size === 0}
                size="sm"
              >
                {isDownloadingClaimPdfs ? (
                  <>
                    <LoaderCircleIcon className="h-4 w-4 mr-1 animate-spin" />
                    Downloading...
                  </>
                ) : (
                  <>
                    <Download className="h-4 w-4 mr-1" />
                    Download Claim PDF for Column
                  </>
                )}
              </Button>
              {staffMembers.map((staff, index) => (
                <label
                  key={staff.id}
                  className="flex items-center gap-1 cursor-pointer select-none"
                >
                  <input
                    type="checkbox"
                    className="w-4 h-4 rounded border-gray-400 accent-teal-600"
                    checked={selectedDownloadPdfColumns.has(Number(staff.id))}
                    onChange={() => toggleDownloadPdfColumn(Number(staff.id))}
                  />
                  <span className="text-sm font-medium">
                    {columnLabels[String(staff.id)] ?? String.fromCharCode(65 + index)}
                  </span>
                </label>
              ))}
            </div>
          </div>
        </div>

        {/* Context Menu */}
        <Menu id={APPOINTMENT_CONTEXT_MENU_ID} animation="fade">
          <Item
            onClick={({ props }) => {
              const fullAppointment = appointments.find(
                (a) => a.id === props.appointmentId
              );
              if (fullAppointment) {
                handleEditAppointment(fullAppointment);
              }
            }}
          >
            <span className="flex items-center gap-2">
              <CalendarIcon className="h-4 w-4" />
              Edit Appointment
            </span>
          </Item>
          <Item
            onClick={({ props }) =>
              handleDeleteAppointment(props.appointmentId)
            }
          >
            <span className="flex items-center gap-2 text-red-600">
              <Trash2 className="h-4 w-4" />
              Delete Appointment
            </span>
          </Item>

          {/* Check Eligibility */}
          <Item
            onClick={({ props }) => handleCheckEligibility(props.appointmentId)}
          >
            <span className="flex items-center gap-2">
              <Shield className="h-4 w-4" />
              Eligibility Status
            </span>
          </Item>

          {/* Select Procedures */}
          <Item
            onClick={({ props }) => handleSelectProcedures(props.appointmentId)}
          >
            <span className="flex items-center gap-2 text-purple-600">
              <Stethoscope className="h-4 w-4" />
              Select Procedures
            </span>
          </Item>

          {/* Claims / PreAuth */}
          <Item
            onClick={({ props }) => handleClaimsPreAuth(props.appointmentId)}
          >
            <span className="flex items-center gap-2">
              <FileCheck className="h-4 w-4" />
              Claims/PreAuth
            </span>
          </Item>

          {/* Payments */}
          <Item onClick={({ props }) => handlePayments(props.appointmentId)}>
            <span className="flex items-center gap-2 text-green-600">
              <CreditCard className="h-4 w-4" />
              Payments
            </span>
          </Item>

          {/* Chart / Treatment Plan */}
          <Item onClick={({ props }) => handleChartPlan(props.appointmentId)}>
            <span className="flex items-center gap-2">
              <ClipboardList className="h-4 w-4" />
              Chart / Treatment Plan
            </span>
          </Item>

          {/* Clinic Notes */}
          <Item onClick={({ props }) => handleClinicNotes(props.appointmentId)}>
            <span className="flex items-center gap-2 text-yellow-600">
              <StickyNote className="h-4 w-4" />
              Clinic Notes
            </span>
          </Item>

          {/* Claim Status */}
          <Item
            onClick={({ props }) => handleCheckClaimStatus(props.appointmentId)}
          >
            <span className="flex items-center gap-2">
              <Shield className="h-4 w-4" />
              Claim Status
            </span>
          </Item>
        </Menu>

        {/* Main Content  */}
        <div className="flex flex-col lg:flex-row gap-6">
          {/* Schedule Grid */}
          <div className="w-full overflow-x-auto bg-white rounded-md shadow">
            <div className="p-4 border-b">
              <div className="flex items-center justify-between">
                <div className="flex items-center space-x-1">
                  {/* << prev week */}
                  <Button
                    variant="outline"
                    size="icon"
                    title="Previous week"
                    onClick={() => setSelectedDate(addWeeks(selectedDate, -1))}
                  >
                    <ChevronsLeft className="h-4 w-4" />
                  </Button>
                  {/* < prev day */}
                  <Button
                    variant="outline"
                    size="icon"
                    title="Previous day"
                    onClick={() => setSelectedDate(addDays(selectedDate, -1))}
                  >
                    <ChevronLeft className="h-4 w-4" />
                  </Button>
                  {/* today circle */}
                  <Button
                    variant="outline"
                    title="Go to today"
                    onClick={() => setSelectedDate(startOfToday())}
                    className="rounded-full w-auto px-3 font-semibold text-sm"
                  >
                    {formattedSelectedDate}
                  </Button>
                  {/* > next day */}
                  <Button
                    variant="outline"
                    size="icon"
                    title="Next day"
                    onClick={() => setSelectedDate(addDays(selectedDate, 1))}
                  >
                    <ChevronRight className="h-4 w-4" />
                  </Button>
                  {/* >> next week */}
                  <Button
                    variant="outline"
                    size="icon"
                    title="Next week"
                    onClick={() => setSelectedDate(addWeeks(selectedDate, 1))}
                  >
                    <ChevronsRight className="h-4 w-4" />
                  </Button>
                </div>

                {/* Top button with popover calendar */}
                <div className="flex items-center gap-2">
                  <Label className="hidden sm:flex">Selected</Label>
                  <Popover open={calendarOpen} onOpenChange={setCalendarOpen}>
                    <PopoverTrigger asChild>
                      <Button
                        variant="outline"
                        className="w-[160px] justify-start text-left font-normal"
                      >
                        <CalendarIcon className="mr-2 h-4 w-4" />
                        {selectedDate
                          ? formatDateToHumanReadable(selectedDate)
                          : "Pick a date"}
                      </Button>
                    </PopoverTrigger>

                    <PopoverContent className="w-auto">
                      <Calendar
                        mode="single"
                        selected={selectedDate}
                        onSelect={(date) => {
                          if (date) setSelectedDate(date);
                        }}
                        onClose={() => setCalendarOpen(false)}
                      />
                    </PopoverContent>
                  </Popover>
                </div>
              </div>
            </div>

            {/* Schedule Grid with Drag and Drop */}
            <DndProvider backend={HTML5Backend}>
              <div className="overflow-x-auto">
                <table className="w-full border-collapse min-w-[800px]">
                  <thead>
                    <tr>
                      <th className="p-2 border bg-gray-50 w-[100px]">Time</th>
                      {staffMembers.map((staff, index) => (
                        <th
                          key={staff.id}
                          className={`p-2 border bg-gray-50 ${staff.role === "doctor" ? "font-bold" : ""}`}
                        >
                          {editingLabelStaffId === String(staff.id) ? (
                            <input
                              className="w-16 text-center border rounded text-sm font-bold px-1"
                              defaultValue={columnLabels[String(staff.id)] ?? String.fromCharCode(65 + index)}
                              onBlur={(e) => handleLabelSave(String(staff.id), e.target.value)}
                              onKeyDown={(e) => {
                                if (e.key === "Enter") handleLabelSave(String(staff.id), e.currentTarget.value);
                                if (e.key === "Escape") setEditingLabelStaffId(null);
                              }}
                              autoFocus
                            />
                          ) : (
                            <div
                              className="font-bold text-sm cursor-pointer hover:bg-gray-200 rounded px-1 inline-block"
                              title="Click to rename"
                              onClick={() => setEditingLabelStaffId(String(staff.id))}
                            >
                              {columnLabels[String(staff.id)] ?? String.fromCharCode(65 + index)}
                            </div>
                          )}
                          <div className="text-xs text-gray-500">{staff.name}</div>
                          <div className="text-xs text-gray-400">{staff.role}</div>
                        </th>
                      ))}
                    </tr>
                  </thead>
                  <tbody>
                    {timeSlots.map((timeSlot) => (
                      <tr key={timeSlot.time}>
                        <td className="border px-2 py-1 text-xs text-gray-600 font-medium">
                          {timeSlot.displayTime}
                        </td>
                        {staffMembers.map((staff, staffIndex) => {
                          if (coveredSlots.has(`${timeSlot.time}-${staff.id}`)) return null;
                          const apt = getAppointmentAtSlot(timeSlot, Number(staff.id));
                          const span = apt ? getSlotSpan(apt) : 1;
                          return (
                            <DroppableTimeSlot
                              key={`${timeSlot.time}-${staff.id}`}
                              timeSlot={timeSlot}
                              staffId={Number(staff.id)}
                              staffIndex={staffIndex}
                              appointment={apt}
                              staff={staff}
                              rowSpan={span}
                            />
                          );
                        })}
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>
            </DndProvider>
          </div>
        </div>
      </div>

      {/* Add/Edit Appointment Modal */}
      <AddAppointmentModal
        open={isAddModalOpen}
        onOpenChange={setIsAddModalOpen}
        onSubmit={handleAppointmentSubmit}
        isLoading={
          createAppointmentMutation.isPending ||
          updateAppointmentMutation.isPending
        }
        appointment={editingAppointment}
        onDelete={handleDeleteAppointment}
      />

      <DeleteConfirmationDialog
        isOpen={confirmDeleteState.open}
        onConfirm={handleConfirmDelete}
        onCancel={() => setConfirmDeleteState({ open: false })}
        entityName={String(confirmDeleteState.appointmentId)}
      />

      {/* Outside-office-hours override dialog */}
      {pendingOverride && (
        <div className="fixed inset-0 bg-black/40 flex items-center justify-center z-50">
          <div className="bg-white rounded-lg shadow-lg p-6 max-w-sm w-full mx-4">
            <h3 className="text-base font-semibold mb-2">Outside Office Hours</h3>
            <p className="text-sm text-gray-600 mb-5">
              This time slot is outside the configured office hours. Do you want to schedule here anyway?
            </p>
            <div className="flex justify-end gap-3">
              <button
                className="px-4 py-2 text-sm border rounded hover:bg-gray-50"
                onClick={() => setPendingOverride(null)}
              >
                Cancel
              </button>
              <button
                className="px-4 py-2 text-sm bg-teal-600 text-white rounded hover:bg-teal-700"
                onClick={() => {
                  if (pendingOverride.type === "move" && pendingOverride.appointmentId != null) {
                    handleMoveAppointment(pendingOverride.appointmentId, pendingOverride.timeSlot, pendingOverride.staffId);
                  } else {
                    handleCreateAppointmentAtSlot(pendingOverride.timeSlot, pendingOverride.staffId);
                  }
                  setPendingOverride(null);
                }}
              >
                Schedule Anyway
              </button>
            </div>
          </div>
        </div>
      )}

      {/* Select Procedures modal — stays on appointments page */}
      {isSelectProceduresOpen && selectProceduresPatientId !== null && (
        <ClaimForm
          patientId={selectProceduresPatientId}
          appointmentId={selectProceduresAppointmentId ?? undefined}
          proceduresOnly={true}
          onClose={() => {
            setIsSelectProceduresOpen(false);
            setSelectProceduresPatientId(null);
            setSelectProceduresAppointmentId(null);
          }}
          onSubmit={async () => { throw new Error("not used in proceduresOnly mode"); }}
          onHandleAppointmentSubmit={async () => 0}
          onHandleUpdatePatient={(_patient: UpdatePatient & { id: number }) => {}}
          onHandleForMHSeleniumClaim={() => {}}
          onHandleForMHSeleniumClaimPreAuth={() => {}}
        />
      )}
    </div>
  );
}
