import { useState, useEffect, useMemo } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faUserPen } from '@fortawesome/pro-duotone-svg-icons';
import { Input } from '@/components/base/input/input';
import { Select } from '@/components/base/select/select';
import { TextArea } from '@/components/base/textarea/textarea';
import { Button } from '@/components/base/buttons/button';
import { DatePicker } from '@/components/application/date-picker/date-picker';
import { parseDate, today, getLocalTimeZone } from '@internationalized/date';
import { apiClient } from '@/lib/api-client';
import { cx } from '@/utils/cx';
import { notify } from '@/lib/toast';
import { EditPatientConfirmModal } from '@/components/modals/edit-patient-confirm-modal';

type ExistingAppointment = {
    id: string;
    scheduledAt: string;
    doctorName: string;
    doctorId?: string;
    department: string;
    clinicId?: string;
    reasonForVisit?: string;
    status: string;
};

type AppointmentFormProps = {
    isOpen: boolean;
    onOpenChange: (open: boolean) => void;
    callerNumber?: string | null;
    leadName?: string | null;
    leadId?: string | null;
    patientId?: string | null;
    // Called after a successful save. Passes back what actually happened so
    // the parent can pre-lock the disposition (BOOKED vs RESCHEDULED vs
    // CANCELLED each map to distinct disposition outcomes).
    onSaved?: (outcome: 'BOOKED' | 'RESCHEDULED' | 'CANCELLED') => void;
    existingAppointment?: ExistingAppointment | null;
    // When true, the form shows the existing appointment's data in a
    // disabled state — no input editing, no Save/Cancel. Only a Close
    // button. Used by the reschedule-confirm flow when the agent picks
    // "No, just view" on an upcoming-appointment pill.
    readOnly?: boolean;
};

type DoctorRecord = { id: string; name: string; department: string; clinic: string };

// Clinics are fetched dynamically from the platform — no hardcoded list.
// If the workspace has no clinics configured, the dropdown shows empty.

const genderItems = [
    { id: 'male', label: 'Male' },
    { id: 'female', label: 'Female' },
    { id: 'other', label: 'Other' },
];

// Time slots are fetched from /api/masterdata/slots based on
// doctor + date. No hardcoded times.

export const AppointmentForm = ({
    isOpen,
    onOpenChange,
    callerNumber,
    leadName,
    leadId,
    patientId,
    onSaved,
    existingAppointment,
    readOnly = false,
}: AppointmentFormProps) => {
    const isEditMode = !!existingAppointment;

    // Doctor data from platform
    const [doctors, setDoctors] = useState<DoctorRecord[]>([]);

    // Initial name captured at form open — used to detect whether the
    // agent actually changed the name before we commit any destructive
    // updatePatient / updateLead.contactName mutations.
    const initialLeadName = (leadName ?? '').trim();

    // Form state — initialized from existing appointment in edit mode
    const [patientName, setPatientName] = useState(leadName ?? '');
    // The patient-name input is locked by default when there's an
    // existing caller name (to prevent accidental rename-on-save), and
    // unlocked only after the agent clicks the Edit button and confirms
    // in the warning modal. First-time callers with no existing name
    // start unlocked because there's nothing to protect.
    const [isNameEditable, setIsNameEditable] = useState(initialLeadName.length === 0);
    const [editConfirmOpen, setEditConfirmOpen] = useState(false);
    const [patientPhone, setPatientPhone] = useState(callerNumber ?? '');
    const [age, setAge] = useState('');
    const [gender, setGender] = useState<string | null>(null);
    // Preload clinic from the existing appointment when editing — so the
    // select lands on the right branch instead of being empty and forcing
    // the agent to re-pick. Only historical rows that predate clinicId
    // persistence will fall through to the auto-select-from-slot logic.
    const [clinic, setClinic] = useState<string | null>(existingAppointment?.clinicId ?? null);
    const [clinicItems, setClinicItems] = useState<Array<{ id: string; label: string }>>([]);
    const [department, setDepartment] = useState<string | null>(existingAppointment?.department ?? null);
    const [doctor, setDoctor] = useState<string | null>(existingAppointment?.doctorId ?? null);
    const [date, setDate] = useState(() => {
        if (existingAppointment?.scheduledAt) return existingAppointment.scheduledAt.split('T')[0];
        return new Date().toISOString().split('T')[0];
    });
    const [timeSlot, setTimeSlot] = useState<string | null>(() => {
        if (existingAppointment?.scheduledAt) {
            const dt = new Date(existingAppointment.scheduledAt);
            return `${dt.getHours().toString().padStart(2, '0')}:${dt.getMinutes().toString().padStart(2, '0')}`;
        }
        return null;
    });
    const [chiefComplaint, setChiefComplaint] = useState(existingAppointment?.reasonForVisit ?? '');
    const [source, setSource] = useState('Inbound Call');
    const [agentNotes, setAgentNotes] = useState('');
    const [timeSlotItems, setTimeSlotItems] = useState<Array<{ id: string; label: string }>>([]);

    // Fetch available time slots when doctor + date change
    useEffect(() => {
        if (!doctor || !date) {
            setTimeSlotItems([]);
            return;
        }
        apiClient.get<Array<{ time: string; label: string; clinicId: string; clinicName: string }>>(
            `/api/masterdata/slots?doctorId=${doctor}&date=${date}`,
        ).then(slots => {
            // Filter by selected clinic — doctor may visit multiple branches
            const filtered = clinic ? slots.filter(s => s.clinicId === clinic) : slots;
            let items = filtered.map(s => ({ id: s.time, label: s.label }));

            // In edit mode, the saved timeSlot may have been filtered out
            // (past-slot filter, schedule change, clinic mismatch). Inject
            // it as a synthetic option so the dropdown still shows the
            // existing value — otherwise the agent sees a cleared field
            // and assumes the save-time was lost.
            if (timeSlot && !items.some(i => i.id === timeSlot)) {
                items = [{ id: timeSlot, label: `${timeSlot} (current)` }, ...items];
            }

            setTimeSlotItems(items);
            // Auto-select clinic from the slot's clinic only if no clinic chosen
            if (filtered.length === 0 && slots.length > 0 && !clinic) {
                setClinic(slots[0].clinicId);
                const autoItems = slots.filter(s => s.clinicId === slots[0].clinicId).map(s => ({ id: s.time, label: s.label }));
                if (timeSlot && !autoItems.some(i => i.id === timeSlot)) {
                    autoItems.unshift({ id: timeSlot, label: `${timeSlot} (current)` });
                }
                setTimeSlotItems(autoItems);
            }
        }).catch(() => setTimeSlotItems([]));
    // eslint-disable-next-line react-hooks/exhaustive-deps — clinic and timeSlot
    // deliberately excluded. Including clinic causes a loop: the effect calls
    // setClinic() for auto-selection → clinic changes → effect re-fires → loop.
    // timeSlot is only needed for the synthetic "current" option injection which
    // is a read, not a trigger. Re-fetch should only happen on doctor/date change.
    }, [doctor, date]);

    // Availability state
    const [bookedSlots, setBookedSlots] = useState<string[]>([]);
    const [loadingSlots, setLoadingSlots] = useState(false);

    const [isSaving, setIsSaving] = useState(false);
    const [error, setError] = useState<string | null>(null);

    // Fetch doctors on mount. Doctors are hospital-wide — no single
    // `clinic` field anymore. We pull the full visit-slot list via the
    // Fetch clinics + doctors from the master data endpoint (Redis-cached).
    // This is faster than direct GraphQL and returns pre-formatted data.
    useEffect(() => {
        if (!isOpen) return;
        apiClient.get<Array<{ id: string; name: string; phone: string; address: string }>>('/api/masterdata/clinics')
            .then(clinics => {
                setClinicItems(clinics.map(c => ({ id: c.id, label: c.name || 'Unnamed Clinic' })));
            }).catch(() => {});
    }, [isOpen]);

    useEffect(() => {
        if (!isOpen) return;
        apiClient.get<Array<{ id: string; name: string; department: string; qualifications: string }>>('/api/masterdata/doctors')
            .then(docs => {
                setDoctors(docs.map(d => ({
                    id: d.id,
                    name: d.name,
                    department: d.department,
                    clinic: '', // clinic assignment via visit slots, not on doctor directly
                })));
            }).catch(() => {});
    }, [isOpen]);

    // Fetch booked slots when doctor + date selected
    useEffect(() => {
        if (!doctor || !date) {
            setBookedSlots([]);
            return;
        }

        setLoadingSlots(true);
        apiClient.graphql<{ appointments: { edges: Array<{ node: any }> } }>(
            `{ appointments(filter: {
                doctorId: { eq: "${doctor}" },
                scheduledAt: { gte: "${date}T00:00:00", lte: "${date}T23:59:59" }
            }) { edges { node { id scheduledAt durationMin status } } } }`,
        ).then(data => {
            // Filter out cancelled/completed appointments client-side
            const activeAppointments = data.appointments.edges.filter(e => {
                const status = e.node.status;
                return status !== 'CANCELLED' && status !== 'COMPLETED' && status !== 'NO_SHOW';
            });
            const slots = activeAppointments.map(e => {
                const dt = new Date(e.node.scheduledAt);
                return `${dt.getHours().toString().padStart(2, '0')}:${dt.getMinutes().toString().padStart(2, '0')}`;
            });
            // In edit mode, don't block the current appointment's slot
            if (isEditMode && existingAppointment) {
                const currentDt = new Date(existingAppointment.scheduledAt);
                const currentSlot = `${currentDt.getHours().toString().padStart(2, '0')}:${currentDt.getMinutes().toString().padStart(2, '0')}`;
                setBookedSlots(slots.filter(s => s !== currentSlot));
            } else {
                setBookedSlots(slots);
            }
        }).catch(() => setBookedSlots([]))
        .finally(() => setLoadingSlots(false));
    }, [doctor, date, isEditMode, existingAppointment]);

    // Reset doctor when department changes
    useEffect(() => {
        setDoctor(null);
        setTimeSlot(null);
    }, [department]);

    // Reset time slot when doctor or date changes
    useEffect(() => {
        setTimeSlot(null);
    }, [doctor, date]);

    // Departments from master data (or fallback to deriving from doctors)
    const [departmentItems, setDepartmentItems] = useState<Array<{ id: string; label: string }>>([]);
    useEffect(() => {
        if (!isOpen) return;
        apiClient.get<string[]>('/api/masterdata/departments')
            .then(depts => setDepartmentItems(depts.map(d => ({ id: d, label: d }))))
            .catch(() => {
                // Fallback: derive from doctor list
                const derived = [...new Set(doctors.map(d => d.department).filter(Boolean))];
                setDepartmentItems(derived.map(d => ({ id: d, label: d })));
            });
    }, [isOpen, doctors]);

    const filteredDoctors = department
        ? doctors.filter(d => d.department === department)
        : doctors;
    // Always include the currently-selected doctor even if the department
    // filter would exclude them. Needed for edit mode: the saved
    // Appointment.department may be stored as a display string ("ENT") or
    // a legacy value that doesn't match the doctor's current department
    // enum — without this, the Select renders blank.
    const doctorSelectItems = useMemo(() => {
        const items = filteredDoctors.map(d => ({ id: d.id, label: d.name }));
        if (doctor && !items.some(i => i.id === doctor)) {
            const selected = doctors.find(d => d.id === doctor);
            if (selected) items.unshift({ id: selected.id, label: selected.name });
        }
        return items;
    }, [filteredDoctors, doctors, doctor]);

    const timeSlotSelectItems = useMemo(() => timeSlotItems.map(slot => ({
        ...slot,
        isDisabled: bookedSlots.includes(slot.id),
        label: bookedSlots.includes(slot.id) ? `${slot.label} (Booked)` : slot.label,
    })), [timeSlotItems, bookedSlots]);

    const handleSave = async () => {
        if (!date || !timeSlot || !doctor || !department) {
            setError('Please fill in the required fields: date, time, doctor, and department.');
            return;
        }

        const today = new Date().toISOString().split('T')[0];
        if (!isEditMode && date < today) {
            setError('Appointment date cannot be in the past.');
            return;
        }

        setIsSaving(true);
        setError(null);

        try {
            const scheduledAt = new Date(`${date}T${timeSlot}:00`).toISOString();
            const selectedDoctor = doctors.find(d => d.id === doctor);

            if (isEditMode && existingAppointment) {
                // Update existing appointment. Flip status to RESCHEDULED so
                // the Appointments > Rescheduled tab reflects it and the
                // patient timeline records the reschedule event.
                await apiClient.graphql(
                    `mutation UpdateAppointment($id: UUID!, $data: AppointmentUpdateInput!) {
                        updateAppointment(id: $id, data: $data) { id }
                    }`,
                    {
                        id: existingAppointment.id,
                        data: {
                            scheduledAt,
                            doctorName: selectedDoctor?.name ?? '',
                            department: selectedDoctor?.department ?? '',
                            doctorId: doctor,
                            reasonForVisit: chiefComplaint || null,
                            status: 'RESCHEDULED',
                        },
                    },
                );

                // Propagate name change during reschedule. Same gate as the
                // create branch — nameChanged implies isNameEditable=true,
                // which means the agent went through EditPatientConfirmModal.
                const trimmedName = patientName.trim();
                const nameChanged = isNameEditable && trimmedName.length > 0 && trimmedName !== initialLeadName;
                if (nameChanged) {
                    const nameParts = { firstName: trimmedName.split(' ')[0], lastName: trimmedName.split(' ').slice(1).join(' ') || '' };
                    if (patientId) {
                        await apiClient.graphql(
                            `mutation($id: UUID!, $data: PatientUpdateInput!) { updatePatient(id: $id, data: $data) { id } }`,
                            { id: patientId, data: { fullName: nameParts } },
                        ).catch((err: unknown) => console.warn('Failed to update patient name:', err));
                    }
                    if (leadId) {
                        await apiClient.graphql(
                            `mutation($id: UUID!, $data: LeadUpdateInput!) { updateLead(id: $id, data: $data) { id } }`,
                            { id: leadId, data: { contactName: nameParts } },
                        ).catch((err: unknown) => console.warn('Failed to update lead name:', err));
                    }
                }

                notify.success('Appointment Updated');
            } else {
                // If no patient record exists yet (new caller), create one now
                let resolvedPatientId = patientId;
                if (!resolvedPatientId && callerNumber) {
                    const trimmedName = patientName.trim();
                    const nameParts = {
                        firstName: trimmedName.split(' ')[0] || '',
                        lastName: trimmedName.split(' ').slice(1).join(' ') || '',
                    };
                    // Normalize phone to +91XXXXXXXXXX format
                    const phoneDigits = callerNumber.replace(/\D/g, '').slice(-10);
                    const phoneE164 = `+91${phoneDigits}`;
                    try {
                        const patientData: Record<string, any> = {
                            fullName: nameParts,
                            phones: { primaryPhoneNumber: phoneE164 },
                            patientType: 'NEW',
                        };
                        if (age) patientData.dateOfBirth = new Date(Date.now() - parseInt(age) * 365.25 * 86400000).toISOString().split('T')[0];
                        if (gender) patientData.gender = gender.toUpperCase();
                        const created = await apiClient.graphql<{ createPatient: { id: string } }>(
                            `mutation($data: PatientCreateInput!) { createPatient(data: $data) { id } }`,
                            { data: patientData },
                        );
                        resolvedPatientId = created.createPatient.id;
                    } catch (err) {
                        console.warn('Failed to create patient:', err);
                    }
                }

                // Create appointment
                const appointmentData: Record<string, any> = {
                    scheduledAt,
                    durationMin: 30,
                    appointmentType: 'CONSULTATION',
                    status: 'SCHEDULED',
                    doctorName: selectedDoctor?.name ?? '',
                    department: selectedDoctor?.department ?? '',
                    doctorId: doctor,
                    reasonForVisit: chiefComplaint || null,
                    ...(resolvedPatientId ? { patientId: resolvedPatientId } : {}),
                    ...(clinic ? { clinicId: clinic } : {}),
                    ...(agentNotes ? { agentNotes } : {}),
                    ...(source ? { source } : {}),
                };
                console.log('[APPOINTMENT] Creating appointment:', JSON.stringify(appointmentData));
                await apiClient.graphql(
                    `mutation CreateAppointment($data: AppointmentCreateInput!) {
                        createAppointment(data: $data) { id }
                    }`,
                    { data: appointmentData },
                );

                // Determine whether the agent actually renamed the patient.
                // Only a non-empty, changed-from-initial name counts — empty
                // strings or an unchanged name never trigger the rename
                // chain, even if the field was unlocked.
                const trimmedName = patientName.trim();
                const nameChanged = isNameEditable && trimmedName.length > 0 && trimmedName !== initialLeadName;

                // Update patient name when the agent explicitly renamed.
                // `nameChanged` already requires isNameEditable=true (the
                // agent went through EditPatientConfirmModal), so the
                // rename intent is unambiguous. Bug #527's silent-overwrite
                // case can no longer happen because the confirm modal
                // gates the input.
                if (nameChanged && patientId) {
                    const nameParts = { firstName: trimmedName.split(' ')[0], lastName: trimmedName.split(' ').slice(1).join(' ') || '' };
                    apiClient.graphql(
                        `mutation($id: UUID!, $data: PatientUpdateInput!) { updatePatient(id: $id, data: $data) { id } }`,
                        { id: patientId, data: { fullName: nameParts } },
                    ).catch((err: unknown) => console.warn('Failed to update patient name:', err));
                }

                // Update lead status/lastContacted on every appointment book
                // (those are genuinely about this appointment), but only
                // touch lead.contactName if the agent explicitly renamed.
                //
                // NOTE: field name is `status`, NOT `leadStatus` — the
                // staging platform schema renamed this. The old name is
                // rejected by LeadUpdateInput.
                if (leadId) {
                    await apiClient.graphql(
                        `mutation UpdateLead($id: UUID!, $data: LeadUpdateInput!) {
                            updateLead(id: $id, data: $data) { id }
                        }`,
                        {
                            id: leadId,
                            data: {
                                status: 'APPOINTMENT_SET',
                                lastContacted: new Date().toISOString(),
                                ...(nameChanged ? { contactName: { firstName: trimmedName.split(' ')[0], lastName: trimmedName.split(' ').slice(1).join(' ') || '' } } : {}),
                            },
                        },
                    ).catch((err: unknown) => console.warn('Failed to update lead:', err));
                }

                // If the agent actually renamed the patient, kick off the
                // side-effect chain: regenerate the AI summary against the
                // corrected identity. Fire-and-forget; the save toast
                // fires immediately regardless.
                if (nameChanged && leadId) {
                    apiClient.post(`/api/lead/${leadId}/enrich`, { phone: callerNumber ?? undefined }, { silent: true }).catch(() => {});
                }
            }

            onSaved?.(isEditMode ? 'RESCHEDULED' : 'BOOKED');
        } catch (err) {
            console.error('Failed to save appointment:', err);
            setError(err instanceof Error ? err.message : 'Failed to save appointment. Please try again.');
        } finally {
            setIsSaving(false);
        }
    };

    const handleCancel = async () => {
        if (!existingAppointment) return;
        setIsSaving(true);
        try {
            await apiClient.graphql(
                `mutation CancelAppointment($id: UUID!, $data: AppointmentUpdateInput!) {
                    updateAppointment(id: $id, data: $data) { id }
                }`,
                {
                    id: existingAppointment.id,
                    data: { status: 'CANCELLED' },
                },
            );
            notify.success('Appointment Cancelled');
            onSaved?.('CANCELLED');
        } catch (err) {
            setError(err instanceof Error ? err.message : 'Failed to cancel appointment');
        } finally {
            setIsSaving(false);
        }
    };

    if (!isOpen) return null;

    return (
        <div className="flex flex-col flex-1 min-h-0">
            {/* Form fields — scrollable */}
            <div className="flex-1 overflow-y-auto">
            <div className="flex flex-col gap-3">
                {/* Patient Info — only for new appointments */}
                {!isEditMode && (
                    <>
                        <div className="flex flex-col gap-1">
                            <span className="text-xs font-bold uppercase tracking-wider text-tertiary">
                                Patient Information
                            </span>
                        </div>

                        {/* Patient name — locked by default for existing
                            callers, unlocked for new callers with no
                            prior name on record. The Edit button opens
                            a confirm modal before unlocking; see
                            EditPatientNameModal for the rationale. */}
                        <div className="flex items-end gap-2">
                            <div className="flex-1">
                                <Input
                                    label="Patient Name"
                                    placeholder="Full name"
                                    value={patientName}
                                    onChange={setPatientName}
                                    isDisabled={readOnly || !isNameEditable}
                                />
                            </div>
                            {!isNameEditable && initialLeadName.length > 0 && (
                                <Button
                                    size="sm"
                                    color="secondary"
                                    iconLeading={({ className }: { className?: string }) => (
                                        <FontAwesomeIcon icon={faUserPen} className={className} />
                                    )}
                                    onClick={() => setEditConfirmOpen(true)}
                                >
                                    Edit
                                </Button>
                            )}
                        </div>

                        <div className="grid grid-cols-2 gap-3">
                            <Input
                                label="Phone"
                                placeholder="Phone number"
                                value={patientPhone}
                                onChange={setPatientPhone}
                            />
                            <Input
                                label="Age"
                                placeholder="Age"
                                type="number"
                                value={age}
                                onChange={setAge}
                            />
                        </div>

                        <Select
                            label="Gender"
                            placeholder="Select gender"
                            items={genderItems}
                            selectedKey={gender}
                            onSelectionChange={(key) => setGender(key as string)}
                        >
                            {(item) => <Select.Item id={item.id} label={item.label} />}
                        </Select>

                        <div className="border-t border-secondary" />
                    </>
                )}

                {/* Appointment Details */}
                <div className="flex flex-col gap-1">
                    <span className="text-xs font-bold uppercase tracking-wider text-tertiary">
                        Appointment Details
                    </span>
                </div>

                {!isEditMode && (
                    <Select
                        label="Clinic / Branch"
                        placeholder="Select clinic"
                        items={clinicItems}
                        selectedKey={clinic}
                        onSelectionChange={(key) => setClinic(key as string)}
                    >
                        {(item) => <Select.Item id={item.id} label={item.label} />}
                    </Select>
                )}

                <div className="grid grid-cols-2 gap-3">
                    <Select
                        label="Department *"
                        placeholder={doctors.length === 0 ? 'Loading...' : 'Select department'}
                        items={departmentItems}
                        selectedKey={department}
                        onSelectionChange={(key) => setDepartment(key as string)}
                        isDisabled={readOnly || doctors.length === 0}
                    >
                        {(item) => <Select.Item id={item.id} label={item.label} />}
                    </Select>

                    <Select
                        label="Doctor *"
                        placeholder={!department ? 'Select department first' : 'Select doctor'}
                        items={doctorSelectItems}
                        selectedKey={doctor}
                        onSelectionChange={(key) => setDoctor(key as string)}
                        isDisabled={readOnly || !department}
                    >
                        {(item) => <Select.Item id={item.id} label={item.label} />}
                    </Select>
                </div>

                <div className="flex flex-col gap-1">
                    <span className="text-xs font-medium text-secondary">Date <span className="text-error-primary">*</span></span>
                    <DatePicker
                        value={date ? parseDate(date) : null}
                        onChange={(val) => setDate(val ? val.toString() : '')}
                        granularity="day"
                        isDisabled={readOnly || !doctor}
                        // Block past dates — appointments can't be booked or
                        // rescheduled into the past. React Aria's DatePicker
                        // honours minValue in both the calendar grid and the
                        // typed-input fallback.
                        minValue={today(getLocalTimeZone())}
                    />
                </div>

                {/* Time slot grid */}
                {doctor && date && (
                    <div className="flex flex-col gap-2">
                        <span className="text-xs font-semibold text-secondary">
                            {loadingSlots ? 'Checking availability...' : 'Available Slots'}
                        </span>
                        <div className="grid grid-cols-4 gap-1.5">
                            {timeSlotSelectItems.map(slot => {
                                const isBooked = slot.isDisabled;
                                const isSelected = timeSlot === slot.id;
                                return (
                                    <button
                                        key={slot.id}
                                        type="button"
                                        disabled={readOnly || isBooked}
                                        onClick={() => setTimeSlot(slot.id)}
                                        className={cx(
                                            'rounded-lg py-2 px-1 text-xs font-medium transition duration-100 ease-linear',
                                            isBooked
                                                ? 'cursor-not-allowed bg-disabled text-disabled line-through'
                                                : isSelected
                                                    ? 'bg-brand-solid text-white ring-2 ring-brand'
                                                    : 'cursor-pointer bg-secondary text-secondary hover:bg-secondary_hover hover:text-secondary_hover',
                                        )}
                                    >
                                        {timeSlotItems.find(t => t.id === slot.id)?.label ?? slot.id}
                                    </button>
                                );
                            })}
                        </div>
                    </div>
                )}

                {!doctor || !date ? (
                    <p className="text-xs text-tertiary">Select a doctor and date to see available time slots</p>
                ) : null}

                <TextArea
                    label="Chief Complaint"
                    placeholder="Describe the reason for visit..."
                    value={chiefComplaint}
                    onChange={setChiefComplaint}
                    isDisabled={readOnly}
                    rows={2}
                />

                {/* Additional Info — only for new appointments */}
                {!isEditMode && (
                    <>
                        <div className="border-t border-secondary" />

                        <Input
                            label="Source / Referral"
                            placeholder="How did the patient reach us?"
                            value={source}
                            onChange={setSource}
                        />

                        <TextArea
                            label="Agent Notes"
                            placeholder="Any additional notes..."
                            value={agentNotes}
                            onChange={setAgentNotes}
                            rows={2}
                        />
                    </>
                )}

                {error && (
                    <div className="rounded-lg bg-error-primary p-3 text-sm text-error-primary">
                        {error}
                    </div>
                )}
            </div>
            </div>

            {/* Footer — pinned */}
            <div className="shrink-0 flex items-center justify-between pt-4 border-t border-secondary">
                <div>
                    {isEditMode && !readOnly && (
                        <Button size="sm" color="primary-destructive" isLoading={isSaving} onClick={handleCancel}>
                            Cancel Appointment
                        </Button>
                    )}
                </div>
                <div className="flex items-center gap-3">
                    <Button size="sm" color="secondary" onClick={() => onOpenChange(false)}>
                        Close
                    </Button>
                    {!readOnly && (
                        <Button size="sm" color="primary" isLoading={isSaving} showTextWhileLoading onClick={handleSave}>
                            {isSaving ? 'Saving...' : isEditMode ? 'Update Appointment' : 'Book Appointment'}
                        </Button>
                    )}
                </div>
            </div>

            <EditPatientConfirmModal
                isOpen={editConfirmOpen}
                onOpenChange={setEditConfirmOpen}
                onConfirm={() => {
                    setIsNameEditable(true);
                    setEditConfirmOpen(false);
                }}
                description={
                    <>
                        You&apos;re about to change the name on this patient&apos;s record. This will
                        update their profile across Helix Engage, including past appointments,
                        lead history, and AI summary. Only proceed if the current name is
                        actually wrong — for all other cases, cancel and continue with the
                        appointment as-is.
                    </>
                }
            />
        </div>
    );
};
