import { useState, useEffect } from "react";
import { useQuery, useMutation } from "@tanstack/react-query";
import { addDays, 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,
  Move,
  Trash2,
  CreditCard,
  ClipboardList,
  StickyNote,
  Shield,
  FileCheck,
  LoaderCircleIcon,
} 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,
} from "@repo/db/types";
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/seleniumEligibilityBatchCheckTaskSlice";
import { SeleniumTaskBanner } from "@/components/ui/selenium-task-banner";
import { PatientStatusBadge } from "@/components/appointments/patient-status-badge";

// 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;
  staffId: number;
  date: string | Date;
  startTime: string | Date;
  endTime: string | Date;
  status: string | null;
  type: string;
}

// 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.seleniumEligibilityBatchCheckTask
  );
  const [isCheckingAllElig, setIsCheckingAllElig] = useState(false);
  const [processedAppointmentIds, setProcessedAppointmentIds] = useState<
    Record<number, boolean>
  >({});

  const [, setLocation] = useLocation();

  // 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 ?? [];

  // 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",
  }));

  // Generate time slots from 8:00 AM to 6:00 PM in 15-minute increments
  const timeSlots: TimeSlot[] = [];
  for (let hour = 8; hour <= 18; 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 18:00 (last start for 30-min appointment)
      if (timeStr > "18: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
      );
      return await res.json();
    },
    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 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,
      staffId,
      status: apt.status ?? null,
      date: formatLocalDate(apt.date),
      startTime: normalizedStart,
      endTime: normalizedEnd,
    } as ScheduledAppointment;

    return processed;
  });

  // 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);

    // Update appointment data
    const { id, createdAt, ...sanitizedAppointment } = appointment;
    const updatedAppointment: UpdateAppointment = {
      ...sanitizedAppointment,
      startTime: newTimeSlot.time, // Already in HH:MM format
      endTime: endTime, // Already in HH:MM format
      notes: `Appointment with ${staff?.name}`,
      staffId: newStaffId, // Update staffId
    };
    // 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={`${staff.color} border border-white shadow-md text-white rounded p-1 text-xs h-full overflow-hidden 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={appointment.eligibilityStatus ?? "UNKNOWN"}
          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,
    appointment,
    staff,
  }: {
    timeSlot: TimeSlot;
    staffId: number;
    appointment: ScheduledAppointment | undefined;
    staff: Staff;
  }) {
    const [{ isOver, canDrop }, drop] = useDrop(() => ({
      accept: ItemTypes.APPOINTMENT,
      drop: (item: { id: number }) => {
        handleMoveAppointment(item.id, timeSlot, staffId);
      },
      canDrop: (item: { id: number }) => {
        // Prevent dropping if there's already an appointment here
        return !appointment;
      },
      collect: (monitor) => ({
        isOver: !!monitor.isOver(),
        canDrop: !!monitor.canDrop(),
      }),
    }));

    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" : ""}`}
      >
        {appointment ? (
          <DraggableAppointment appointment={appointment} staff={staff} />
        ) : (
          <button
            className={`w-full h-full ${isOver && canDrop ? "bg-green-100" : "text-gray-400 hover:bg-gray-100"} rounded flex items-center justify-center`}
            onClick={() => handleCreateAppointmentAtSlot(timeSlot, staffId)}
          >
            <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 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

    // Start: set redux task status (visible globally)
    dispatch(
      setTaskStatus({
        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}`,
        {}
      );

      // 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({
            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({
          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({
          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
    }
  };
  return (
    <div>
      <SeleniumTaskBanner
        status={batchTask.status}
        message={batchTask.message}
        show={batchTask.show}
        onClear={() => dispatch(clearTaskStatus())}
      />

      <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 justify-between gap-2">
            <Button
              onClick={() => {
                setEditingAppointment(undefined);
                setIsAddModalOpen(true);
              }}
              disabled={isLoading}
            >
              <Plus className="h-4 w-4" />
              New Appointment
            </Button>

            <Button
              onClick={() => handleCheckAllEligibilities()}
              disabled={isLoading || isCheckingAllElig}
            >
              {isCheckingAllElig ? (
                <>
                  <LoaderCircleIcon className="h-4 w-4 mr-2 animate-spin" />
                  Checking...
                </>
              ) : (
                <>
                  <Shield className="h-4 w-4 mr-2" />
                  Check all eligibilities
                </>
              )}
            </Button>
          </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>

          {/* Check Eligibility */}
          <Item
            onClick={({ props }) => handleCheckClaimStatus(props.appointmentId)}
          >
            <span className="flex items-center gap-2">
              <Shield className="h-4 w-4" />
              Claim Status
            </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>
        </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-2">
                  <Button
                    variant="outline"
                    size="icon"
                    onClick={() => setSelectedDate(addDays(selectedDate, -1))}
                  >
                    <ChevronLeft className="h-4 w-4" />
                  </Button>
                  <h2 className="text-xl font-semibold">
                    {formattedSelectedDate}
                  </h2>
                  <Button
                    variant="outline"
                    size="icon"
                    onClick={() => setSelectedDate(addDays(selectedDate, 1))}
                  >
                    <ChevronRight 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) => (
                        <th
                          key={staff.id}
                          className={`p-2 border bg-gray-50 ${staff.role === "doctor" ? "font-bold" : ""}`}
                        >
                          {staff.name}
                          <div className="text-xs text-gray-500">
                            {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) => (
                          <DroppableTimeSlot
                            key={`${timeSlot.time}-${staff.id}`}
                            timeSlot={timeSlot}
                            staffId={Number(staff.id)}
                            appointment={getAppointmentAtSlot(
                              timeSlot,
                              Number(staff.id)
                            )}
                            staff={staff}
                          />
                        ))}
                      </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)}
      />
    </div>
  );
}
