import type { FC } from 'react';
import { useEffect, useState, useCallback } from 'react';
import { Clock, CheckCircle, XCircle, CheckCheck, UserCheck, UserX, Plus, PackageCheck, Sparkles, RotateCcw, LayoutGrid, List, Rows3, Edit2, Ban, Trash2 } from 'lucide-react';
import Layout from '../components/Layout';
import {
  Card,
  Button,
  Input,
  Select,
  TextArea,
  Modal,
  Badge,
  EmptyState,
  MobileCard,
  PageHeader,
  Skeleton,
  toast,
  badgeForAppointmentState,
} from '../components/ui';
import { useSearchParams } from 'react-router-dom';
import { odooApi, type Appointment, type Patient, type Service, type Doctor, type Bloqueo } from '../services/odoo';

const stateOptions = [
  { value: '', label: 'Todos los estados' },
  { value: 'pending', label: 'Pendiente' },
  { value: 'confirmed', label: 'Confirmada' },
  { value: 'done', label: 'Completada' },
  { value: 'cancelled', label: 'Cancelada' },
];

const Agenda: FC = () => {
  const [searchParams] = useSearchParams();
  const [appointments, setAppointments] = useState<Appointment[]>([]);
  const [patients, setPatients] = useState<Patient[]>([]);
  const [services, setServices] = useState<Service[]>([]);
  const [doctors, setDoctors] = useState<Doctor[]>([]);
  const [loading, setLoading] = useState(true);
  const [submitting, setSubmitting] = useState(false);
  const [updating, setUpdating] = useState<number | null>(null);
  const [error, setError] = useState<string | null>(null);
  const [date, setDate] = useState<string>(() => searchParams.get('date') || new Date().toISOString().split('T')[0]);
  const [stateFilter, setStateFilter] = useState('');
  const [doctorFilter, setDoctorFilter] = useState('');
  const [viewMode, setViewMode] = useState<'lista' | 'doctores' | 'horas'>('doctores');
  const [quickFilter, setQuickFilter] = useState<'' | 'libres' | 'primera' | 'checkin' | 'noshow'>('');
  const [detailApt, setDetailApt] = useState<Appointment | null>(null);
  const [editMode, setEditMode] = useState(false);
  const [editForm, setEditForm] = useState({ date: '', time: '', doctor_id: '', notes: '' });
  const [savingEdit, setSavingEdit] = useState(false);
  const [createOpen, setCreateOpen] = useState(false);
  const [slots, setSlots] = useState<{ time: number; time_str: string }[]>([]);
  const [slotsLoading, setSlotsLoading] = useState(false);
  const [bloqueos, setBloqueos] = useState<Bloqueo[]>([]);
  const [bloqueosOpen, setBloqueosOpen] = useState(false);
  const [bloqueoForm, setBloqueoForm] = useState({ doctor_id: '', date: '', all_day: false, time_from: '13:00', time_to: '14:00', motivo: '' });
  const [savingBloqueo, setSavingBloqueo] = useState(false);
  const [dragApt, setDragApt] = useState<Appointment | null>(null);
  const [dropTarget, setDropTarget] = useState<string | null>(null);

  const [form, setForm] = useState({
    patient_id: '',
    service_id: '',
    doctor_id: '',
    date: new Date().toISOString().split('T')[0],
    time: '',
    notes: '',
    sessions: 1,
  });
  const [patientSearch, setPatientSearch] = useState('');
  const [pkgUpdating, setPkgUpdating] = useState<number | null>(null);
  const [expressOpen, setExpressOpen] = useState(false);
  const [expressSubmitting, setExpressSubmitting] = useState(false);
  const [expressForm, setExpressForm] = useState({
    name: '',
    phone: '',
    notes: '',
    withAppointment: false,
    service_id: '',
    doctor_id: '',
    date: new Date().toISOString().split('T')[0],
    time: '',
  });

  const filteredAppointments = appointments.filter((apt) => {
    if (stateFilter && apt.state !== stateFilter) return false;
    if (doctorFilter && String(apt.doctor_id) !== doctorFilter) return false;
    return true;
  });

  // ---- Filtros rápidos ----
  const timeToFloat = (t: string): number | null => {
    const m = /^(\d{1,2}):(\d{2})/.exec((t || '').trim());
    return m ? parseInt(m[1], 10) + parseInt(m[2], 10) / 60 : null;
  };
  const nowDate = new Date();
  const nowFloat = nowDate.getHours() + nowDate.getMinutes() / 60;
  const todayStr = `${nowDate.getFullYear()}-${String(nowDate.getMonth() + 1).padStart(2, '0')}-${String(nowDate.getDate()).padStart(2, '0')}`;

  // Candidata a check-in: confirmada, de hoy, ya empezó y aún no termina
  const isCheckinDue = (a: Appointment): boolean => {
    const start = timeToFloat(a.time);
    if (a.state !== 'confirmed' || a.date !== todayStr || start === null) return false;
    const end = start + (a.duration ?? 30) / 60;
    return start <= nowFloat && nowFloat < end;
  };
  // Candidata a no-show: confirmada, de hoy, ya debió terminar
  const isNoShowDue = (a: Appointment): boolean => {
    const start = timeToFloat(a.time);
    if (a.state !== 'confirmed' || a.date !== todayStr || start === null) return false;
    return start + (a.duration ?? 30) / 60 <= nowFloat;
  };

  const visibleAppointments = filteredAppointments.filter((apt) => {
    if (quickFilter === 'primera' && !apt.is_first_visit) return false;
    if (quickFilter === 'checkin' && !isCheckinDue(apt)) return false;
    if (quickFilter === 'noshow' && !isNoShowDue(apt)) return false;
    return true;
  });

  const countPrimera = filteredAppointments.filter((a) => a.is_first_visit).length;
  const countCheckin = filteredAppointments.filter(isCheckinDue).length;
  const countNoShow = filteredAppointments.filter(isNoShowDue).length;

  // ---- Vista por médico (grid doctores x horas) ----
  const GRID_BASE_START = 9; // horario clínica: 9:00
  const GRID_BASE_END = 18;  // horario clínica: 18:00

  const aptHour = (t: string): number | null => {
    const m = /^(\d{1,2}):?(\d{2})?/.exec((t || '').trim());
    if (!m) return null;
    const h = parseInt(m[1], 10);
    return Number.isNaN(h) ? null : h;
  };

  // Las canceladas no ocupan horario: el hueco queda libre
  const gridAppointments = visibleAppointments.filter((a) => a.state !== 'cancelled');

  const bookedHours = gridAppointments
    .map((a) => aptHour(a.time))
    .filter((h): h is number => h !== null);
  const gridStart = bookedHours.length ? Math.min(GRID_BASE_START, ...bookedHours) : GRID_BASE_START;
  const gridEnd = bookedHours.length ? Math.max(GRID_BASE_END, ...bookedHours) : GRID_BASE_END;
  const gridHours = Array.from({ length: gridEnd - gridStart + 1 }, (_, i) => gridStart + i);

  const gridDoctors = doctorFilter
    ? doctors.filter((d) => String(d.id) === doctorFilter)
    : doctors;
  const hasUnassigned = !doctorFilter && gridAppointments.some((a) => !a.doctor_id);
  const gridColumns: { key: string; label: string; subtitle: string; doctorId: number | null }[] = [
    ...gridDoctors.map((d) => ({ key: `d-${d.id}`, label: d.name, subtitle: d.job_title || '', doctorId: d.id })),
    ...(hasUnassigned ? [{ key: 'none', label: 'Sin asignar', subtitle: '', doctorId: null }] : []),
  ];

  const gridCellAppointments = (doctorId: number | null, hour: number) =>
    gridAppointments.filter((a) =>
      (doctorId ? a.doctor_id === doctorId : !a.doctor_id) && aptHour(a.time) === hour);

  const gridDoctorCount = (doctorId: number | null) =>
    gridAppointments.filter((a) => (doctorId ? a.doctor_id === doctorId : !a.doctor_id)).length;

  // Bloqueo que cubre una celda (médico × hora)
  const bloqueoEnCelda = (doctorId: number | null, hour: number) =>
    doctorId
      ? bloqueos.find((b) => b.doctor_id === doctorId && (b.all_day || (hour >= b.time_from && hour < b.time_to)))
      : undefined;

  // Drag & drop para mover citas (solo pendientes/confirmadas)
  const DRAGGABLE_STATES = ['pending', 'confirmed'];
  const handleDrop = async (doctorId: number | null, hour: number) => {
    if (!dragApt) return;
    const apt = dragApt;
    setDragApt(null);
    setDropTarget(null);
    if ((apt.doctor_id ?? null) === doctorId && aptHour(apt.time) === hour) return;
    const nombreCol = doctorId ? (doctors.find((d) => d.id === doctorId)?.name || '') : 'Sin asignar';
    const horaStr = `${String(hour).padStart(2, '0')}:00`;
    if (!window.confirm(`¿Mover la cita de ${apt.patient} a ${nombreCol} ${horaStr}?`)) return;
    try {
      await odooApi.updateAppointment(apt.id, {
        doctor_id: doctorId ?? null,
        time: hour as unknown as string,
      });
      toast.success('Cita movida');
      await load();
    } catch (err) {
      const msg = (err as { response?: { data?: { message?: string } } })?.response?.data?.message;
      toast.error(msg || 'No se pudo mover la cita');
      console.error(err);
    }
  };

  const chipDragProps = (apt: Appointment) =>
    DRAGGABLE_STATES.includes(apt.state)
      ? {
          draggable: true,
          onDragStart: (e: React.DragEvent) => { e.stopPropagation(); setDragApt(apt); },
          onDragEnd: () => { setDragApt(null); setDropTarget(null); },
        }
      : {};

  const openBloqueos = () => {
    setBloqueoForm({ doctor_id: doctorFilter, date, all_day: false, time_from: '13:00', time_to: '14:00', motivo: '' });
    setBloqueosOpen(true);
  };

  const saveBloqueo = async () => {
    if (!bloqueoForm.doctor_id || !bloqueoForm.date) {
      toast.error('Médico y fecha son obligatorios');
      return;
    }
    try {
      setSavingBloqueo(true);
      await odooApi.createBloqueo({
        doctor_id: parseInt(bloqueoForm.doctor_id, 10),
        date: bloqueoForm.date,
        all_day: bloqueoForm.all_day,
        time_from: parseTimeToFloat(bloqueoForm.time_from) ?? 9,
        time_to: parseTimeToFloat(bloqueoForm.time_to) ?? 10,
        motivo: bloqueoForm.motivo,
      });
      toast.success('Bloqueo creado');
      await load();
    } catch (err) {
      const msg = (err as { response?: { data?: { message?: string } } })?.response?.data?.message;
      toast.error(msg || 'Error al crear bloqueo');
      console.error(err);
    } finally {
      setSavingBloqueo(false);
    }
  };

  const removeBloqueo = async (id: number) => {
    try {
      await odooApi.deleteBloqueo(id);
      toast.success('Bloqueo eliminado');
      await load();
    } catch (err) {
      toast.error('Error al eliminar bloqueo');
      console.error(err);
    }
  };

  const horaFloatStr = (h: number) => {
    const hh = Math.floor(h);
    const mm = Math.round((h - hh) * 60);
    return `${String(hh).padStart(2, '0')}:${String(mm).padStart(2, '0')}`;
  };

  const initials = (name: string) =>
    name.split(' ').filter(Boolean).slice(0, 2).map((w) => w[0]).join('').toUpperCase();

  // Colores por estado siguiendo la paleta de la marca (coral/mint/sky/lilac)
  const gridStateClass: Record<string, string> = {
    pending: 'border-brand-coral bg-theme-accent-bg text-theme-heading',
    confirmed: 'border-emerald-700 bg-brand-mint text-theme-heading',
    arrived: 'border-sky-700 bg-brand-sky text-theme-heading',
    in_progress: 'border-violet-600 bg-brand-lilac text-theme-heading',
    done: 'border-theme-border-strong bg-theme-bg text-theme-muted',
    no_show: 'border-rose-400 bg-rose-50 text-rose-700',
  };

  const gridStateLegend: { state: string; label: string; dot: string }[] = [
    { state: 'pending', label: 'Pendiente', dot: 'bg-brand-coral' },
    { state: 'confirmed', label: 'Confirmada', dot: 'bg-brand-mint border border-emerald-700' },
    { state: 'arrived', label: 'Llegó', dot: 'bg-brand-sky border border-sky-700' },
    { state: 'in_progress', label: 'En curso', dot: 'bg-brand-lilac border border-violet-600' },
    { state: 'done', label: 'Completada', dot: 'bg-theme-border-strong' },
  ];

  const firstName = (full: string) => (full || '').trim().split(' ')[0] || '-';

  const gridLegend = (
    <div className="mt-3 flex flex-wrap items-center gap-x-4 gap-y-2">
      {gridStateLegend.map((item) => (
        <span key={item.state} className="inline-flex items-center gap-1.5 text-xs text-theme-muted">
          <span className={`inline-block h-2.5 w-2.5 rounded-full ${item.dot}`} />
          {item.label}
        </span>
      ))}
      <span className="text-xs text-theme-muted">
        · Las citas canceladas no bloquean horario: su hueco aparece como libre.
      </span>
    </div>
  );

  const load = useCallback(async () => {
    try {
      setLoading(true);
      setError(null);
      const params: Record<string, string> = date ? { date } : {};
      const res = await odooApi.getAppointments(params);
      if (res.status === 'success') {
        setAppointments(res.appointments);
        setBloqueos(res.bloqueos || []);
      }
    } catch (err) {
      setError('Error al cargar citas');
      toast.error('Error al cargar citas');
      console.error(err);
    } finally {
      setLoading(false);
    }
  }, [date]);

  const loadReferences = useCallback(async () => {
    try {
      const [servicesRes, doctorsRes] = await Promise.all([
        odooApi.getServices(),
        odooApi.getDoctors(),
      ]);
      if (servicesRes.status === 'success') setServices(servicesRes.services);
      if (doctorsRes.status === 'success') setDoctors(doctorsRes.doctors);
    } catch (err) {
      console.error(err);
    }
  }, []);

  useEffect(() => {
    load();
    loadReferences();
  }, [load, loadReferences]);

  // Cargar pacientes para el selector (búsqueda server-side)
  useEffect(() => {
    if (!createOpen) return;
    const t = setTimeout(async () => {
      try {
        const res = await odooApi.getPatients({ search: patientSearch, page_size: 50 });
        if (res.status === 'success') setPatients(res.patients);
      } catch (err) {
        console.error(err);
      }
    }, 250);
    return () => clearTimeout(t);
  }, [createOpen, patientSearch]);

  const fetchSlots = useCallback(async (serviceId: string, aptDate: string) => {
    if (!serviceId || !aptDate) {
      setSlots([]);
      return;
    }
    try {
      setSlotsLoading(true);
      const res = await odooApi.getAvailableSlots(aptDate, parseInt(serviceId, 10));
      if (res.status === 'success') setSlots(res.slots);
    } catch {
      toast.error('Error al cargar horarios disponibles');
      setSlots([]);
    } finally {
      setSlotsLoading(false);
    }
  }, []);

  useEffect(() => {
    fetchSlots(form.service_id, form.date);
  }, [form.service_id, form.date, fetchSlots]);

  const handleStatus = async (id: number, action: string) => {
    try {
      setUpdating(id);
      await odooApi.updateAppointmentStatus(id, action);
      toast.success('Estado actualizado');
      setDetailApt(null);
      await load();
    } catch (err) {
      toast.error('Error al actualizar estado');
      console.error(err);
    } finally {
      setUpdating(null);
    }
  };

  const openAptDetail = (apt: Appointment) => {
    setDetailApt(apt);
    setEditMode(false);
    setEditForm({
      date: apt.date,
      time: apt.time,
      doctor_id: apt.doctor_id ? String(apt.doctor_id) : '',
      notes: apt.notes || '',
    });
  };

  const saveAptEdit = async () => {
    if (!detailApt) return;
    if (!editForm.date || !editForm.time) {
      toast.error('Fecha y hora son obligatorias');
      return;
    }
    try {
      setSavingEdit(true);
      await odooApi.updateAppointment(detailApt.id, {
        date: editForm.date,
        time: parseTimeToFloat(editForm.time) as unknown as string,
        doctor_id: editForm.doctor_id ? parseInt(editForm.doctor_id, 10) : undefined,
        notes: editForm.notes,
      });
      toast.success('Reserva actualizada');
      setDetailApt(null);
      await load();
    } catch (err) {
      toast.error('Error al actualizar la reserva');
      console.error(err);
    } finally {
      setSavingEdit(false);
    }
  };

  const aptStateLabels: Record<string, string> = {
    pending: 'Pendiente', confirmed: 'Confirmada', arrived: 'Llegó',
    in_progress: 'En curso', done: 'Completada', cancelled: 'Cancelada', no_show: 'No show',
  };

  const handlePackageFinished = async (id: number, finished: boolean) => {
    try {
      setPkgUpdating(id);
      await odooApi.packageFinished(id, finished);
      toast.success(finished ? 'Paquete marcado como terminado' : 'Marca de paquete revertida');
      await load();
    } catch (err) {
      toast.error('Error al actualizar paquete');
      console.error(err);
    } finally {
      setPkgUpdating(null);
    }
  };

  const parseTimeToFloat = (t: string): number | undefined => {
    if (!t) return undefined;
    const [h, m] = t.split(':').map((x) => parseInt(x, 10));
    if (Number.isNaN(h)) return undefined;
    return h + (Number.isNaN(m) ? 0 : m / 60);
  };

  const resetExpressForm = () =>
    setExpressForm({
      name: '', phone: '', notes: '', withAppointment: false,
      service_id: '', doctor_id: '', date: new Date().toISOString().split('T')[0], time: '',
    });

  const submitExpress = async () => {
    if (!expressForm.name.trim() || !expressForm.phone.trim()) {
      toast.error('Nombre y teléfono son obligatorios');
      return;
    }
    if (expressForm.withAppointment && (!expressForm.service_id || !expressForm.date || !expressForm.time)) {
      toast.error('Para la cita de valoración selecciona servicio, fecha y hora');
      return;
    }
    try {
      setExpressSubmitting(true);
      const patientPayload: Partial<Patient> & { notes?: string } = {
        name: expressForm.name.trim(),
        phone: expressForm.phone.trim(),
        source: 'walkin',
        notes: expressForm.notes || undefined,
      };
      const res = await odooApi.createPatient(patientPayload);
      const newId = res.patient?.id;
      if (expressForm.withAppointment && newId) {
        await odooApi.createAppointment({
          patient_id: newId,
          service_id: parseInt(expressForm.service_id, 10),
          doctor_id: expressForm.doctor_id ? parseInt(expressForm.doctor_id, 10) : undefined,
          date: expressForm.date,
          time: parseTimeToFloat(expressForm.time) as unknown as string,
          notes: 'Valoración express',
          state: 'confirmed',
        });
      }
      toast.success(expressForm.withAppointment ? 'Prospecto y cita de valoración creados' : 'Prospecto creado');
      setExpressOpen(false);
      resetExpressForm();
      await load();
    } catch (err) {
      toast.error('Error en valoración express');
      console.error(err);
    } finally {
      setExpressSubmitting(false);
    }
  };

  const createAppointment = async () => {
    if (!form.patient_id || !form.service_id || !form.date || !form.time) {
      toast.error('Completa los campos obligatorios');
      return;
    }
    const service = services.find((s) => String(s.id) === form.service_id);
    const sessions = form.sessions > 1 ? form.sessions : ((service?.package_sessions ?? 0) > 1 ? (service?.package_sessions ?? 1) : 1);
    try {
      setSubmitting(true);
      await odooApi.createAppointment({
        patient_id: parseInt(form.patient_id, 10),
        service_id: parseInt(form.service_id, 10),
        doctor_id: form.doctor_id ? parseInt(form.doctor_id, 10) : undefined,
        date: form.date,
        time: form.time,
        notes: form.notes,
        sessions,
      });
      toast.success(sessions > 1 ? `Paquete creado: ${sessions} citas` : 'Cita creada');
      setCreateOpen(false);
      setForm({
        patient_id: '',
        service_id: '',
        doctor_id: '',
        date: new Date().toISOString().split('T')[0],
        time: '',
        notes: '',
        sessions: 1,
      });
      await load();
    } catch (err) {
      toast.error('Error al crear cita');
      console.error(err);
    } finally {
      setSubmitting(false);
    }
  };

  const doctorOptions = [
    { value: '', label: 'Todos los médicos' },
    ...doctors.map((d) => ({ value: String(d.id), label: d.name })),
  ];

  const patientOptions = [
    { value: '', label: 'Seleccionar paciente...' },
    ...patients.map((p) => ({ value: String(p.id), label: `${p.name} (${p.phone})` })),
  ];

  const serviceOptions = [
    { value: '', label: 'Seleccionar servicio...' },
    ...services.map((s) => ({
      value: String(s.id),
      label: (s.package_price ?? 0) > 0 && (s.package_sessions ?? 0) > 0
        ? `${s.name} — $${s.price} / ${s.package_sessions} sesiones $${s.package_price}`
        : `${s.name} — $${s.price}`,
    })),
  ];

  const slotOptions = slots.length
    ? [
        { value: '', label: 'Seleccionar hora...' },
        ...slots.map((s) => ({ value: s.time_str, label: s.time_str })),
      ]
    : [
        { value: '', label: 'Selecciona servicio y fecha primero' },
      ];

  const TableHeader = () => (
    <thead className="bg-theme-bg">
      <tr>
        <th className="text-left p-3 text-xs font-medium text-theme-muted uppercase">Hora</th>
        <th className="text-left p-3 text-xs font-medium text-theme-muted uppercase">Paciente</th>
        <th className="text-left p-3 text-xs font-medium text-theme-muted uppercase hidden sm:table-cell">Servicio</th>
        <th className="text-left p-3 text-xs font-medium text-theme-muted uppercase hidden md:table-cell">Doctor</th>
        <th className="text-left p-3 text-xs font-medium text-theme-muted uppercase">Estado</th>
        <th className="text-left p-3 text-xs font-medium text-theme-muted uppercase hidden sm:table-cell">Pago</th>
        <th className="text-left p-3 text-xs font-medium text-theme-muted uppercase">Acciones</th>
      </tr>
    </thead>
  );

  const ActionButtons = ({ apt }: { apt: Appointment }) => (
    <div className="flex items-center gap-1">
      {apt.state !== 'confirmed' && apt.state !== 'done' && apt.state !== 'cancelled' && (
        <Button
          variant="ghost"
          size="sm"
          onClick={() => handleStatus(apt.id, 'confirm')}
          disabled={updating === apt.id}
          title="Confirmar"
        >
          <CheckCircle size={16} className="text-theme-heading" />
        </Button>
      )}
      {apt.state === 'confirmed' && (
        <Button
          variant="ghost"
          size="sm"
          onClick={() => handleStatus(apt.id, 'arrive')}
          disabled={updating === apt.id}
          title="Llegada"
        >
          <UserCheck size={16} className="text-blue-600" />
        </Button>
      )}
      {apt.state !== 'done' && apt.state !== 'cancelled' && (
        <Button
          variant="ghost"
          size="sm"
          onClick={() => handleStatus(apt.id, 'done')}
          disabled={updating === apt.id}
          title="Completar"
        >
          <CheckCheck size={16} className="text-blue-600" />
        </Button>
      )}
      {(apt.state === 'confirmed' || apt.state === 'arrived') && (
        <Button
          variant="ghost"
          size="sm"
          onClick={() => handleStatus(apt.id, 'no_show')}
          disabled={updating === apt.id}
          title="No se presentó (no show)"
        >
          <UserX size={16} className="text-rose-500" />
        </Button>
      )}
      {apt.service_category === 'paquete' && apt.state !== 'cancelled' && (
        apt.package_finished ? (
          <Button
            variant="ghost"
            size="sm"
            onClick={() => handlePackageFinished(apt.id, false)}
            disabled={pkgUpdating === apt.id}
            title="Paquete terminado — click para revertir"
          >
            <RotateCcw size={16} className="text-amber-600" />
          </Button>
        ) : (
          <Button
            variant="ghost"
            size="sm"
            onClick={() => handlePackageFinished(apt.id, true)}
            disabled={pkgUpdating === apt.id}
            title="Marcar paquete terminado"
          >
            <PackageCheck size={16} className="text-violet-600" />
          </Button>
        )
      )}
      {apt.state !== 'cancelled' && (
        <Button
          variant="ghost"
          size="sm"
          onClick={() => handleStatus(apt.id, 'cancel')}
          disabled={updating === apt.id}
          title="Cancelar"
        >
          <XCircle size={16} className="text-rose-600" />
        </Button>
      )}
    </div>
  );

  return (
    <Layout title="Agenda" subtitle="Gestión de citas">
      <PageHeader title="Agenda" subtitle="Filtra por fecha, estado o médico">
        <Button variant="outline" onClick={openBloqueos}>
          <Ban size={16} className="mr-2" />
          Bloqueos
        </Button>
        <Button variant="outline" onClick={() => setExpressOpen(true)}>
          <Sparkles size={16} className="mr-2" />
          Valoración express
        </Button>
        <Button onClick={() => setCreateOpen(true)}>
          <Plus size={16} className="mr-2" />
          Nueva cita
        </Button>
      </PageHeader>

      <Card>
        <Card.Body>
          <div className="flex flex-col sm:flex-row sm:items-end gap-3 sm:gap-4 mb-4 sm:mb-6">
            <Input
              label="Fecha"
              type="date"
              value={date}
              onChange={(e) => setDate(e.target.value)}
              className="sm:max-w-[180px]"
            />
            <Select
              label="Estado"
              options={stateOptions}
              value={stateFilter}
              onChange={(e) => setStateFilter(e.target.value)}
              className="sm:max-w-[180px]"
            />
            <Select
              label="Médico"
              options={doctorOptions}
              value={doctorFilter}
              onChange={(e) => setDoctorFilter(e.target.value)}
              className="sm:max-w-[220px]"
            />
            <div className="flex gap-1 sm:ml-auto">
              <Button
                variant={viewMode === 'lista' ? 'primary' : 'outline'}
                size="sm"
                onClick={() => setViewMode('lista')}
                title="Vista de lista"
              >
                <List size={16} className="mr-1" />
                Lista
              </Button>
              <Button
                variant={viewMode === 'doctores' ? 'primary' : 'outline'}
                size="sm"
                onClick={() => setViewMode('doctores')}
                title="Médicos en columnas, horas en filas"
              >
                <LayoutGrid size={16} className="mr-1" />
                Por médico
              </Button>
              <Button
                variant={viewMode === 'horas' ? 'primary' : 'outline'}
                size="sm"
                onClick={() => setViewMode('horas')}
                title="Médicos en filas, horas en columnas"
              >
                <Rows3 size={16} className="mr-1" />
                Por hora
              </Button>
            </div>
          </div>

          {/* Filtros rápidos */}
          <div className="flex flex-wrap items-center gap-2 mb-4 sm:mb-5">
            {viewMode !== 'lista' && (
              <button
                type="button"
                onClick={() => setQuickFilter(quickFilter === 'libres' ? '' : 'libres')}
                className={`px-3 py-1.5 rounded-full text-xs font-medium border transition-all ${
                  quickFilter === 'libres'
                    ? 'bg-brand-mint text-theme-heading border-emerald-700'
                    : 'bg-theme-bg text-theme-muted border-theme-border hover:border-theme-border-strong'
                }`}
              >
                Espacios libres
              </button>
            )}
            {([
              { key: 'primera', label: 'Primera vez', count: countPrimera },
              { key: 'checkin', label: 'Check-in', count: countCheckin },
              { key: 'noshow', label: 'No show', count: countNoShow },
            ] as const).map((chip) => (
              <button
                key={chip.key}
                type="button"
                onClick={() => setQuickFilter(quickFilter === chip.key ? '' : chip.key)}
                className={`px-3 py-1.5 rounded-full text-xs font-medium border transition-all ${
                  quickFilter === chip.key
                    ? 'bg-theme-accent text-theme-inverse border-theme-accent'
                    : 'bg-theme-bg text-theme-muted border-theme-border hover:border-theme-border-strong'
                }`}
              >
                {chip.label}
                <span className={`ml-1.5 inline-flex items-center justify-center min-w-[18px] h-[18px] px-1 rounded-full text-[10px] ${
                  quickFilter === chip.key ? 'bg-theme-surface/30 text-theme-inverse' : 'bg-theme-surface text-theme-heading border border-theme-border'
                }`}>
                  {chip.count}
                </span>
              </button>
            ))}
            {quickFilter && (
              <button
                type="button"
                onClick={() => setQuickFilter('')}
                className="text-xs text-theme-muted underline underline-offset-2 hover:text-theme-heading"
              >
                Quitar filtro
              </button>
            )}
          </div>

          {error && <p className="text-rose-600 text-sm mb-4">{error}</p>}

          {loading ? (
            <Skeleton count={6} className="h-12 w-full" />
          ) : viewMode === 'doctores' ? (
            gridColumns.length === 0 ? (
              <EmptyState
                title="Sin médicos"
                subtitle="No hay médicos dados de alta para mostrar en la vista por médico."
              />
            ) : (
              <div>
                <div className="overflow-x-auto rounded-2xl border border-theme-border shadow-card">
                  <table className="w-full border-collapse bg-theme-surface">
                    <thead>
                      <tr>
                        <th className="sticky left-0 z-10 w-20 bg-theme-surface p-3 text-left text-xs font-medium text-theme-muted uppercase border-b border-theme-border">
                          Hora
                        </th>
                        {gridColumns.map((col) => (
                          <th key={col.key} className="min-w-[180px] p-3 border-b border-l border-theme-border">
                            <div className="flex items-center gap-2.5">
                              <span className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-brand-lilac text-xs font-bold text-theme-heading">
                                {initials(col.label)}
                              </span>
                              <div className="min-w-0 text-left">
                                <p className="truncate text-sm font-semibold text-theme-heading normal-case">
                                  {col.label}
                                </p>
                                <p className="truncate text-[11px] font-normal text-theme-muted normal-case">
                                  {col.subtitle ? `${col.subtitle} · ` : ''}
                                  {gridDoctorCount(col.doctorId)} {gridDoctorCount(col.doctorId) === 1 ? 'cita' : 'citas'}
                                </p>
                              </div>
                            </div>
                          </th>
                        ))}
                      </tr>
                    </thead>
                    <tbody>
                      {gridHours.map((hour) => (
                        <tr key={hour}>
                          <td className="sticky left-0 z-10 bg-theme-surface p-3 text-xs font-semibold text-theme-muted align-top whitespace-nowrap border-b border-theme-border">
                            {`${String(hour).padStart(2, '0')}:00`}
                          </td>
                          {gridColumns.map((col) => {
                            const cell = gridCellAppointments(col.doctorId, hour);
                            const bloqueo = bloqueoEnCelda(col.doctorId, hour);
                            const cellKey = `${col.key}-${hour}`;
                            return (
                              <td
                                key={col.key}
                                onDragOver={(e) => { if (dragApt && !bloqueo) { e.preventDefault(); setDropTarget(cellKey); } }}
                                onDragLeave={() => setDropTarget((t) => (t === cellKey ? null : t))}
                                onDrop={(e) => { e.preventDefault(); handleDrop(col.doctorId, hour); }}
                                className={`p-1.5 align-top border-b border-l border-theme-border transition-colors ${dropTarget === cellKey ? 'bg-theme-accent-bg' : ''}`}
                              >
                                {bloqueo ? (
                                  <div
                                    className="rounded-xl border border-theme-border py-3.5 text-center text-[11px] font-medium text-theme-muted"
                                    style={{ backgroundImage: 'repeating-linear-gradient(45deg, transparent, transparent 6px, rgba(0,0,0,0.05) 6px, rgba(0,0,0,0.05) 12px)' }}
                                    title={`Bloqueado — ${bloqueo.motivo || 'sin motivo'}`}
                                  >
                                    <Ban size={12} className="inline mr-1" />
                                    Bloqueado{bloqueo.motivo ? ` — ${bloqueo.motivo}` : ''}
                                  </div>
                                ) : cell.length === 0 ? (
                                  <div className={`rounded-xl border py-3.5 text-center text-[11px] font-medium transition-colors ${
                                    quickFilter === 'libres'
                                      ? 'bg-brand-mint border-emerald-700 text-theme-heading font-semibold'
                                      : 'border-dashed border-theme-border-strong text-theme-muted hover:bg-theme-accent-bg'
                                  }`}>
                                    Libre
                                  </div>
                                ) : (
                                  cell.map((apt) => (
                                    <div
                                      key={apt.id}
                                      onClick={() => openAptDetail(apt)}
                                      {...chipDragProps(apt)}
                                      className={`mb-1.5 rounded-xl border-l-4 px-2.5 py-1.5 text-xs shadow-card transition-shadow hover:shadow-soft ${
                                        DRAGGABLE_STATES.includes(apt.state) ? 'cursor-grab active:cursor-grabbing' : 'cursor-pointer'
                                      } ${gridStateClass[apt.state] || 'border-theme-border-strong bg-theme-bg text-theme-heading'} ${quickFilter === 'libres' ? 'opacity-40' : ''}`}
                                      title={`${apt.time} · ${apt.patient} · ${apt.service} · ${apt.state}`}
                                    >
                                      <div className="font-semibold truncate">
                                        {apt.time} · {apt.patient}
                                      </div>
                                      <div className="truncate opacity-75">{apt.service}</div>
                                    </div>
                                  ))
                                )}
                              </td>
                            );
                          })}
                        </tr>
                      ))}
                    </tbody>
                  </table>
                </div>
                {gridLegend}
              </div>
            )
          ) : viewMode === 'horas' ? (
            gridColumns.length === 0 ? (
              <EmptyState
                title="Sin médicos"
                subtitle="No hay médicos dados de alta para mostrar en la vista por hora."
              />
            ) : (
              <div>
                <div className="overflow-x-auto rounded-2xl border border-theme-border shadow-card">
                  <table className="w-full border-collapse bg-theme-surface">
                    <thead>
                      <tr>
                        <th className="sticky left-0 z-10 min-w-[190px] bg-theme-surface p-2.5 text-left text-xs font-medium text-theme-muted uppercase border-b border-theme-border">
                          Médico
                        </th>
                        {gridHours.map((hour) => (
                          <th key={hour} className="min-w-[96px] p-2.5 text-center text-xs font-medium text-theme-muted border-b border-l border-theme-border">
                            {`${String(hour).padStart(2, '0')}:00`}
                          </th>
                        ))}
                      </tr>
                    </thead>
                    <tbody>
                      {gridColumns.map((col) => (
                        <tr key={col.key}>
                          <td className="sticky left-0 z-10 bg-theme-surface p-2.5 border-b border-theme-border">
                            <div className="flex items-center gap-2">
                              <span className="flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-brand-lilac text-[10px] font-bold text-theme-heading">
                                {initials(col.label)}
                              </span>
                              <div className="min-w-0">
                                <p className="truncate text-xs font-semibold text-theme-heading">
                                  {col.label}
                                </p>
                                <p className="text-[10px] text-theme-muted">
                                  {gridDoctorCount(col.doctorId)} {gridDoctorCount(col.doctorId) === 1 ? 'cita' : 'citas'}
                                </p>
                              </div>
                            </div>
                          </td>
                          {gridHours.map((hour) => {
                            const cell = gridCellAppointments(col.doctorId, hour);
                            const bloqueo = bloqueoEnCelda(col.doctorId, hour);
                            const cellKey = `${col.key}-${hour}`;
                            return (
                              <td
                                key={hour}
                                onDragOver={(e) => { if (dragApt && !bloqueo) { e.preventDefault(); setDropTarget(cellKey); } }}
                                onDragLeave={() => setDropTarget((t) => (t === cellKey ? null : t))}
                                onDrop={(e) => { e.preventDefault(); handleDrop(col.doctorId, hour); }}
                                className={`p-1 align-top border-b border-l border-theme-border transition-colors ${dropTarget === cellKey ? 'bg-theme-accent-bg' : ''}`}
                              >
                                {bloqueo ? (
                                  <div
                                    className="rounded-lg border border-theme-border py-2 text-center text-[10px] font-medium text-theme-muted"
                                    style={{ backgroundImage: 'repeating-linear-gradient(45deg, transparent, transparent 6px, rgba(0,0,0,0.05) 6px, rgba(0,0,0,0.05) 12px)' }}
                                    title={`Bloqueado — ${bloqueo.motivo || 'sin motivo'}`}
                                  >
                                    <Ban size={11} className="inline" />
                                  </div>
                                ) : cell.length === 0 ? (
                                  <div
                                    className={`rounded-lg border py-2 transition-colors ${
                                      quickFilter === 'libres'
                                        ? 'bg-brand-mint border-emerald-700'
                                        : 'border-dashed border-theme-border hover:bg-theme-accent-bg'
                                    }`}
                                    title="Libre"
                                  />
                                ) : (
                                  cell.map((apt) => (
                                    <div
                                      key={apt.id}
                                      onClick={() => openAptDetail(apt)}
                                      {...chipDragProps(apt)}
                                      className={`mb-1 rounded-lg border-l-2 px-1.5 py-1 text-[11px] leading-tight shadow-card transition-shadow hover:shadow-soft ${
                                        DRAGGABLE_STATES.includes(apt.state) ? 'cursor-grab active:cursor-grabbing' : 'cursor-pointer'
                                      } ${gridStateClass[apt.state] || 'border-theme-border-strong bg-theme-bg text-theme-heading'} ${quickFilter === 'libres' ? 'opacity-40' : ''}`}
                                      title={`${apt.time} · ${apt.patient} · ${apt.service} · ${apt.state}`}
                                    >
                                      <div className="font-semibold truncate">
                                        {apt.time} · {firstName(apt.patient)}
                                      </div>
                                    </div>
                                  ))
                                )}
                              </td>
                            );
                          })}
                        </tr>
                      ))}
                    </tbody>
                  </table>
                </div>
                {gridLegend}
              </div>
            )
          ) : visibleAppointments.length === 0 ? (
            <EmptyState
              title="Sin citas"
              subtitle="No hay citas para los filtros seleccionados."
              actionLabel="Nueva cita"
              onAction={() => setCreateOpen(true)}
            />
          ) : (
            <>
              {/* Desktop table */}
              <div className="hidden sm:block overflow-x-auto">
                <table className="w-full">
                  <TableHeader />
                  <tbody className="divide-y">
                    {visibleAppointments.map((apt) => (
                      <tr key={apt.id} className="hover:bg-theme-bg">
                        <td className="p-3 text-sm text-theme-heading">
                          <Clock size={14} className="inline mr-1 text-theme-muted" />
                          {apt.time}
                        </td>
                        <td className="p-3 text-sm font-medium text-theme-heading">{apt.patient}</td>
                        <td className="p-3 text-sm text-theme-muted hidden sm:table-cell">
                          <button type="button" onClick={() => openAptDetail(apt)} className="hover:underline hover:text-theme-heading text-left">
                            {apt.service}
                          </button>
                        </td>
                        <td className="p-3 text-sm text-theme-muted hidden md:table-cell">{apt.doctor || '-'}</td>
                        <td className="p-3">
                          <Badge variant={badgeForAppointmentState(apt.state)}>{apt.state}</Badge>
                        </td>
                        <td className="p-3 text-sm text-theme-muted hidden sm:table-cell">{apt.payment_state}</td>
                        <td className="p-3">
                          <ActionButtons apt={apt} />
                        </td>
                      </tr>
                    ))}
                  </tbody>
                </table>
              </div>

              {/* Mobile cards */}
              <div className="sm:hidden space-y-3">
                {visibleAppointments.map((apt) => (
                  <MobileCard
                    key={apt.id}
                    title={apt.patient}
                    subtitle={apt.service}
                    rows={[
                      { label: 'Hora', value: apt.time },
                      { label: 'Doctor', value: apt.doctor || '-' },
                      { label: 'Estado', value: <Badge variant={badgeForAppointmentState(apt.state)}>{apt.state}</Badge> },
                      { label: 'Pago', value: apt.payment_state },
                    ]}
                    actions={<ActionButtons apt={apt} />}
                  />
                ))}
              </div>
            </>
          )}
        </Card.Body>
      </Card>

      {/* Modal de bloqueos de agenda */}
      <Modal
        isOpen={bloqueosOpen}
        onClose={() => setBloqueosOpen(false)}
        title={`Bloqueos de agenda — ${date}`}
        maxWidth="lg"
        footer={<Button variant="outline" onClick={() => setBloqueosOpen(false)}>Cerrar</Button>}
      >
        <div className="space-y-4">
          {/* Alta rápida */}
          <div className="p-3 bg-theme-bg rounded-xl space-y-3">
            <p className="text-xs font-medium text-theme-muted uppercase">Nuevo bloqueo</p>
            <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
              <Select
                label="Médico *"
                options={[{ value: '', label: 'Seleccionar médico...' }, ...doctors.map((d) => ({ value: String(d.id), label: d.name }))]}
                value={bloqueoForm.doctor_id}
                onChange={(e) => setBloqueoForm({ ...bloqueoForm, doctor_id: e.target.value })}
              />
              <Input
                label="Fecha *"
                type="date"
                value={bloqueoForm.date}
                onChange={(e) => setBloqueoForm({ ...bloqueoForm, date: e.target.value })}
              />
              <Input
                label="Motivo"
                placeholder="Comida, Vacaciones, Junta..."
                value={bloqueoForm.motivo}
                onChange={(e) => setBloqueoForm({ ...bloqueoForm, motivo: e.target.value })}
              />
              <label className="flex items-center gap-2 self-end pb-2 cursor-pointer">
                <input
                  type="checkbox"
                  checked={bloqueoForm.all_day}
                  onChange={(e) => setBloqueoForm({ ...bloqueoForm, all_day: e.target.checked })}
                  className="w-4 h-4 rounded border-theme-border-strong"
                />
                <span className="text-sm text-theme-heading">Todo el día</span>
              </label>
              {!bloqueoForm.all_day && (
                <>
                  <Input
                    label="Desde"
                    type="time"
                    value={bloqueoForm.time_from}
                    onChange={(e) => setBloqueoForm({ ...bloqueoForm, time_from: e.target.value })}
                  />
                  <Input
                    label="Hasta"
                    type="time"
                    value={bloqueoForm.time_to}
                    onChange={(e) => setBloqueoForm({ ...bloqueoForm, time_to: e.target.value })}
                  />
                </>
              )}
            </div>
            <div className="flex justify-end">
              <Button size="sm" onClick={saveBloqueo} loading={savingBloqueo}>
                <Plus size={14} className="mr-1" />
                Crear bloqueo
              </Button>
            </div>
          </div>

          {/* Lista del día */}
          <div>
            <p className="text-xs font-medium text-theme-muted uppercase mb-2">Bloqueos de este día</p>
            {bloqueos.length === 0 ? (
              <p className="text-sm text-theme-muted py-4 text-center">No hay bloqueos en la fecha seleccionada.</p>
            ) : (
              <ul className="space-y-1.5 max-h-56 overflow-y-auto">
                {bloqueos.map((b) => (
                  <li key={b.id} className="flex items-center justify-between p-2.5 bg-theme-bg rounded-lg text-sm">
                    <span className="text-theme-heading">
                      <span className="font-medium">{b.doctor}</span>
                      <span className="text-theme-muted">
                        {' · '}{b.all_day ? 'Todo el día' : `${horaFloatStr(b.time_from)}–${horaFloatStr(b.time_to)}`}
                        {b.motivo ? ` · ${b.motivo}` : ''}
                      </span>
                    </span>
                    <Button variant="ghost" size="sm" onClick={() => removeBloqueo(b.id)} title="Eliminar">
                      <Trash2 size={14} className="text-rose-500" />
                    </Button>
                  </li>
                ))}
              </ul>
            )}
          </div>
        </div>
      </Modal>

      <Modal
        isOpen={createOpen}
        onClose={() => setCreateOpen(false)}
        title="Nueva cita"
        maxWidth="lg"
        footer={
          <>
            <Button variant="outline" onClick={() => setCreateOpen(false)}>Cancelar</Button>
            <Button onClick={createAppointment} loading={submitting}>Guardar cita</Button>
          </>
        }
      >
        <div className="space-y-4">
          <Input
            label="Buscar paciente"
            placeholder="Escribe nombre o teléfono..."
            value={patientSearch}
            onChange={(e) => setPatientSearch(e.target.value)}
          />
          <Select
            label="Paciente *"
            options={patientOptions}
            value={form.patient_id}
            onChange={(e) => setForm({ ...form, patient_id: e.target.value })}
          />
          <Select
            label="Servicio *"
            options={serviceOptions}
            value={form.service_id}
            onChange={(e) => setForm({ ...form, service_id: e.target.value })}
          />
          <Select
            label="Médico"
            options={[{ value: '', label: 'Sin preferencia' }, ...doctors.map((d) => ({ value: String(d.id), label: d.name }))]}
            value={form.doctor_id}
            onChange={(e) => setForm({ ...form, doctor_id: e.target.value })}
          />
          <div className="grid grid-cols-2 gap-4">
            <Input
              label="Fecha *"
              type="date"
              value={form.date}
              onChange={(e) => setForm({ ...form, date: e.target.value })}
            />
            <Select
              label="Hora *"
              options={slotOptions}
              value={form.time}
              onChange={(e) => setForm({ ...form, time: e.target.value })}
              disabled={slotsLoading || !form.service_id || !form.date}
            />
          </div>
          <div className="grid grid-cols-2 gap-4">
            <Select
              label="Sesiones"
              options={[
                { value: '1', label: '1 sesión' },
                { value: '2', label: '2 sesiones' },
                { value: '3', label: '3 sesiones' },
                { value: '4', label: '4 sesiones' },
                { value: '5', label: '5 sesiones' },
                { value: '6', label: '6 sesiones' },
              ]}
              value={String(form.sessions)}
              onChange={(e) => setForm({ ...form, sessions: parseInt(e.target.value, 10) })}
            />
            <div className="flex items-end">
              <p className="text-xs text-theme-muted pb-2">
                Si el servicio tiene paquete, se sugerirá el número de sesiones del paquete.
              </p>
            </div>
          </div>
          {slotsLoading && <p className="text-xs text-theme-muted">Cargando horarios disponibles...</p>}
          <TextArea
            label="Notas"
            value={form.notes}
            onChange={(e) => setForm({ ...form, notes: e.target.value })}
          />
        </div>
      </Modal>

      <Modal
        isOpen={expressOpen}
        onClose={() => setExpressOpen(false)}
        title="Valoración express"
        maxWidth="md"
        footer={
          <>
            <Button variant="outline" onClick={() => setExpressOpen(false)}>Cancelar</Button>
            <Button onClick={submitExpress} loading={expressSubmitting}>Guardar</Button>
          </>
        }
      >
        <div className="space-y-4">
          <p className="text-sm text-theme-muted">
            Captura rápida de prospecto. Opcionalmente agenda su cita de valoración en el mismo paso.
          </p>
          <Input
            label="Nombre *"
            placeholder="Nombre del prospecto"
            value={expressForm.name}
            onChange={(e) => setExpressForm({ ...expressForm, name: e.target.value })}
          />
          <Input
            label="Teléfono *"
            placeholder="+52 ..."
            value={expressForm.phone}
            onChange={(e) => setExpressForm({ ...expressForm, phone: e.target.value })}
          />
          <TextArea
            label="Notas"
            placeholder="Motivo de consulta, zona a tratar, etc."
            value={expressForm.notes}
            onChange={(e) => setExpressForm({ ...expressForm, notes: e.target.value })}
          />
          <label className="inline-flex items-center gap-2 text-sm text-theme-heading cursor-pointer">
            <input
              type="checkbox"
              checked={expressForm.withAppointment}
              onChange={(e) => setExpressForm({ ...expressForm, withAppointment: e.target.checked })}
              className="rounded border-theme-border-strong"
            />
            Crear cita de valoración ahora
          </label>
          {expressForm.withAppointment && (
            <div className="space-y-4 rounded-xl border border-theme-border p-4 bg-theme-bg">
              <Select
                label="Servicio *"
                options={serviceOptions}
                value={expressForm.service_id}
                onChange={(e) => setExpressForm({ ...expressForm, service_id: e.target.value })}
              />
              <Select
                label="Médico"
                options={[{ value: '', label: 'Sin preferencia' }, ...doctors.map((d) => ({ value: String(d.id), label: d.name }))]}
                value={expressForm.doctor_id}
                onChange={(e) => setExpressForm({ ...expressForm, doctor_id: e.target.value })}
              />
              <div className="grid grid-cols-2 gap-4">
                <Input
                  label="Fecha *"
                  type="date"
                  value={expressForm.date}
                  onChange={(e) => setExpressForm({ ...expressForm, date: e.target.value })}
                />
                <Input
                  label="Hora *"
                  type="time"
                  value={expressForm.time}
                  onChange={(e) => setExpressForm({ ...expressForm, time: e.target.value })}
                />
              </div>
            </div>
          )}
        </div>
      </Modal>

      {/* Modal detalle de cita: confirmar / no show / editar reserva */}
      <Modal
        isOpen={!!detailApt}
        onClose={() => setDetailApt(null)}
        title={editMode ? 'Editar reserva' : 'Detalle de la cita'}
        maxWidth="md"
        footer={
          editMode ? (
            <>
              <Button variant="outline" onClick={() => setEditMode(false)}>Volver</Button>
              <Button onClick={saveAptEdit} loading={savingEdit}>Guardar cambios</Button>
            </>
          ) : undefined
        }
      >
        {detailApt && !editMode && (
          <div className="space-y-4">
            <div className="flex flex-wrap items-center gap-2">
              <Badge variant={badgeForAppointmentState(detailApt.state)}>
                {aptStateLabels[detailApt.state] || detailApt.state}
              </Badge>
              {detailApt.is_first_visit && (
                <Badge variant="primary">Primera vez</Badge>
              )}
            </div>
            <div className="grid grid-cols-2 gap-3">
              <div className="p-3 bg-theme-bg rounded-xl">
                <p className="text-xs text-theme-muted mb-1">Paciente</p>
                <p className="text-sm font-medium text-theme-heading">{detailApt.patient}</p>
              </div>
              <div className="p-3 bg-theme-bg rounded-xl">
                <p className="text-xs text-theme-muted mb-1">Servicio</p>
                <p className="text-sm font-medium text-theme-heading">{detailApt.service}</p>
              </div>
              <div className="p-3 bg-theme-bg rounded-xl">
                <p className="text-xs text-theme-muted mb-1">Fecha y hora</p>
                <p className="text-sm font-medium text-theme-heading">{detailApt.date} · {detailApt.time}</p>
              </div>
              <div className="p-3 bg-theme-bg rounded-xl">
                <p className="text-xs text-theme-muted mb-1">Médico</p>
                <p className="text-sm font-medium text-theme-heading">{detailApt.doctor || 'Sin asignar'}</p>
              </div>
              <div className="p-3 bg-theme-bg rounded-xl">
                <p className="text-xs text-theme-muted mb-1">Duración</p>
                <p className="text-sm font-medium text-theme-heading">{detailApt.duration ? `${detailApt.duration} min` : '-'}</p>
              </div>
              <div className="p-3 bg-theme-bg rounded-xl">
                <p className="text-xs text-theme-muted mb-1">Precio</p>
                <p className="text-sm font-medium text-theme-heading">${detailApt.price}</p>
              </div>
            </div>
            {detailApt.notes && (
              <div className="p-3 bg-theme-bg rounded-xl">
                <p className="text-xs text-theme-muted mb-1">Notas</p>
                <p className="text-sm text-theme-heading whitespace-pre-wrap">{detailApt.notes}</p>
              </div>
            )}
            <div className="flex flex-wrap gap-2 pt-2 border-t border-theme-border">
              {detailApt.state === 'pending' && (
                <Button size="sm" onClick={() => handleStatus(detailApt.id, 'confirm')} disabled={updating === detailApt.id}>
                  <CheckCircle size={16} className="mr-1.5" /> Confirmar
                </Button>
              )}
              {detailApt.state === 'confirmed' && (
                <Button size="sm" onClick={() => handleStatus(detailApt.id, 'arrive')} disabled={updating === detailApt.id}>
                  <UserCheck size={16} className="mr-1.5" /> Llegó
                </Button>
              )}
              {(detailApt.state === 'confirmed' || detailApt.state === 'arrived') && (
                <Button size="sm" variant="danger" onClick={() => handleStatus(detailApt.id, 'no_show')} disabled={updating === detailApt.id}>
                  <UserX size={16} className="mr-1.5" /> No se presentó
                </Button>
              )}
              {detailApt.state !== 'done' && detailApt.state !== 'cancelled' && detailApt.state !== 'no_show' && (
                <Button size="sm" variant="outline" onClick={() => handleStatus(detailApt.id, 'done')} disabled={updating === detailApt.id}>
                  <CheckCheck size={16} className="mr-1.5" /> Completar
                </Button>
              )}
              <Button size="sm" variant="outline" onClick={() => setEditMode(true)}>
                <Edit2 size={16} className="mr-1.5" /> Editar reserva
              </Button>
            </div>
          </div>
        )}
        {detailApt && editMode && (
          <div className="space-y-4">
            <p className="text-sm text-theme-muted">
              {detailApt.patient} · {detailApt.service}
            </p>
            <div className="grid grid-cols-2 gap-4">
              <Input
                label="Fecha *"
                type="date"
                value={editForm.date}
                onChange={(e) => setEditForm({ ...editForm, date: e.target.value })}
              />
              <Input
                label="Hora *"
                type="time"
                value={editForm.time}
                onChange={(e) => setEditForm({ ...editForm, time: e.target.value })}
              />
            </div>
            <Select
              label="Médico"
              options={[{ value: '', label: 'Sin cambios' }, ...doctors.map((d) => ({ value: String(d.id), label: d.name }))]}
              value={editForm.doctor_id}
              onChange={(e) => setEditForm({ ...editForm, doctor_id: e.target.value })}
            />
            <TextArea
              label="Notas"
              value={editForm.notes}
              onChange={(e) => setEditForm({ ...editForm, notes: e.target.value })}
            />
          </div>
        )}
      </Modal>
    </Layout>
  );
};

export default Agenda;
