import { EstadoCita, Role } from '@prisma/client';
import type { Request, Response } from 'express';
import prisma from '../config/db';
import { recordAppointmentCommunications } from '../services/communication.store';
import {
  DoctorAvailabilityConflictError,
  DoctorScheduleNotFoundError,
  InvalidDoctorScheduleError,
  assertDoctorSlotAvailability,
} from '../services/doctor-availability.service';
import { findFallbackUserById, listFallbackUsers } from '../services/settings.store';
import {
  FallbackAppointmentNotFoundError,
  MissingFallbackDentistError,
  MissingFallbackPatientError,
  createFallbackAppointment,
  listFallbackAppointments,
  updateFallbackAppointment,
} from '../services/appointment.store';

const appointmentStatuses = [
  'PROGRAMADA',
  'CONFIRMADA',
  'EN_SALA',
  'ATENDIDA',
  'CANCELADA',
  'NO_ASISTIO',
] as const;

type AppointmentStatus = (typeof appointmentStatuses)[number];

const assignableRoles = [Role.ADMIN, Role.DENTIST] as const;
const nonOccupyingStatuses: AppointmentStatus[] = [EstadoCita.CANCELADA, EstadoCita.NO_ASISTIO];
const isAssignableRole = (role: Role) => role === Role.ADMIN || role === Role.DENTIST;

type DatabaseAppointment = {
  id: string;
  fecha: Date;
  motivo: string;
  estado: AppointmentStatus;
  notas: string | null;
  pacienteId: string;
  paciente: {
    id: string;
    nombres: string;
    apellidos: string;
    email: string | null;
    telefono: string | null;
  };
  dentista: {
    id: string;
    name: string;
    email: string;
  };
};

type FallbackAppointment = {
  id: string;
  fecha: string;
  motivo: string;
  estado: AppointmentStatus;
  notas: string | null;
  pacienteId: string;
  pacienteNombres: string;
  pacienteApellidos: string;
  pacienteEmail: string | null;
  pacienteTelefono: string | null;
  dentistaId: string;
  dentistaNombre: string;
};

const systemSchedulerEmail = 'agenda@dentaflow.local';

const appointmentConsumesAvailability = (status: AppointmentStatus) => !nonOccupyingStatuses.includes(status);

const shouldValidateNextSlot = (input: {
  currentStatus?: AppointmentStatus;
  nextStatus: AppointmentStatus;
  hasDateField: boolean;
  hasDentistField: boolean;
}) => {
  if (!appointmentConsumesAvailability(input.nextStatus)) {
    return false;
  }

  if (!input.currentStatus) {
    return true;
  }

  return (
    input.hasDateField ||
    input.hasDentistField ||
    !appointmentConsumesAvailability(input.currentStatus)
  );
};

const resolveRequestedDate = (value: unknown) => {
  if (typeof value !== 'string' || !value.trim()) {
    const today = new Date();
    today.setHours(0, 0, 0, 0);
    return today;
  }

  const directDate = new Date(value);

  if (!Number.isNaN(directDate.getTime())) {
    directDate.setHours(0, 0, 0, 0);
    return directDate;
  }

  const dateOnlyValue = new Date(`${value}T00:00:00`);

  if (!Number.isNaN(dateOnlyValue.getTime())) {
    return dateOnlyValue;
  }

  const today = new Date();
  today.setHours(0, 0, 0, 0);
  return today;
};

const resolveRouteParam = (value: string | string[] | undefined) => (Array.isArray(value) ? value[0] : value);

const getAppointmentIdFromRequest = (req: Request, res: Response) => {
  const appointmentId = resolveRouteParam(req.params.id);

  if (!appointmentId) {
    res.status(400).json({ error: 'Cita invalida' });
    return null;
  }

  return appointmentId;
};

const getDayBounds = (selectedDate: Date) => {
  const dayStart = new Date(selectedDate);
  dayStart.setHours(0, 0, 0, 0);

  const dayEnd = new Date(selectedDate);
  dayEnd.setHours(23, 59, 59, 999);

  return { dayStart, dayEnd };
};

const normalizeNullableText = (value: unknown) => {
  if (typeof value !== 'string') {
    return null;
  }

  const trimmedValue = value.trim();
  return trimmedValue ? trimmedValue : null;
};

const normalizeOptionalId = (value: unknown) =>
  typeof value === 'string' && value.trim() ? value.trim() : null;

const parseAppointmentDate = (value: unknown) => {
  if (typeof value !== 'string' || !value.trim()) {
    return null;
  }

  const parsedDate = new Date(value);
  return Number.isNaN(parsedDate.getTime()) ? null : parsedDate;
};

const formatAuditDateTime = (value: Date | string) =>
  new Intl.DateTimeFormat('es-PE', {
    timeZone: 'America/Lima',
    day: '2-digit',
    month: '2-digit',
    year: 'numeric',
    hour: '2-digit',
    minute: '2-digit',
    hour12: false,
  }).format(value instanceof Date ? value : new Date(value));

const buildAppointmentAuditTrail = (input: {
  currentDate: Date | string;
  nextDate: Date;
  currentStatus: string;
  nextStatus: string;
  currentDentistName: string;
  nextDentistName: string;
  reason: string | null;
}) => {
  const changes: string[] = [];
  const currentDateIso = input.currentDate instanceof Date ? input.currentDate.toISOString() : input.currentDate;

  if (currentDateIso !== input.nextDate.toISOString()) {
    changes.push(`de ${formatAuditDateTime(input.currentDate)} a ${formatAuditDateTime(input.nextDate)}`);
  }

  if (input.currentStatus !== input.nextStatus) {
    changes.push(`estado ${input.currentStatus.toLowerCase()} a ${input.nextStatus.toLowerCase()}`);
  }

  if (input.currentDentistName !== input.nextDentistName) {
    changes.push(`odontologo ${input.currentDentistName} a ${input.nextDentistName}`);
  }

  if (input.reason) {
    changes.push(`motivo: ${input.reason}`);
  }

  if (changes.length === 0) {
    return null;
  }

  return `[Reprogramacion ${formatAuditDateTime(new Date())}] ${changes.join(' | ')}`;
};

const mergeAppointmentNotes = (
  currentNotes: string | null,
  nextNotes: string | null | undefined,
  auditTrail: string | null,
) => {
  const baseNotes = nextNotes !== undefined ? nextNotes : currentNotes;
  return [baseNotes, auditTrail].filter((value): value is string => Boolean(value)).join('\n\n') || null;
};

const resolveAssignableDentist = async (dentistaId: string | null) => {
  if (dentistaId) {
    return prisma.user.findFirst({
      where: {
        id: dentistaId,
        active: true,
        role: {
          in: [...assignableRoles],
        },
      },
    });
  }

  return resolveSchedulingUser();
};

const resolveFallbackAssignableDentist = async (dentistaId: string | null) => {
  if (dentistaId) {
    const fallbackUser = await findFallbackUserById(dentistaId);

    if (fallbackUser && fallbackUser.active && isAssignableRole(fallbackUser.role)) {
      return fallbackUser;
    }

    return null;
  }

  const users = await listFallbackUsers();
  return users.find((user) => user.active && isAssignableRole(user.role)) ?? null;
};

const handleAppointmentAvailabilityError = (error: unknown, res: Response) => {
  if (error instanceof DoctorAvailabilityConflictError) {
    return res.status(409).json({ error: error.message });
  }

  if (error instanceof InvalidDoctorScheduleError) {
    return res.status(400).json({ error: error.message });
  }

  if (error instanceof DoctorScheduleNotFoundError) {
    return res.status(404).json({ error: error.message });
  }

  return null;
};

const serializeDatabaseAppointment = (appointment: DatabaseAppointment) => ({
  id: appointment.id,
  fecha: appointment.fecha.toISOString(),
  motivo: appointment.motivo,
  estado: appointment.estado,
  notas: appointment.notas,
  pacienteId: appointment.pacienteId,
  paciente: {
    id: appointment.paciente.id,
    nombres: appointment.paciente.nombres,
    apellidos: appointment.paciente.apellidos,
    email: appointment.paciente.email,
    telefono: appointment.paciente.telefono,
  },
  dentista: {
    id: appointment.dentista.id,
    nombre: appointment.dentista.name,
  },
});

const serializeFallbackAppointment = (appointment: FallbackAppointment) => ({
  id: appointment.id,
  fecha: appointment.fecha,
  motivo: appointment.motivo,
  estado: appointment.estado,
  notas: appointment.notas,
  pacienteId: appointment.pacienteId,
  paciente: {
    id: appointment.pacienteId,
    nombres: appointment.pacienteNombres,
    apellidos: appointment.pacienteApellidos,
    email: appointment.pacienteEmail,
    telefono: appointment.pacienteTelefono,
  },
  dentista: {
    id: appointment.dentistaId,
    nombre: appointment.dentistaNombre,
  },
});

const resolveSchedulingUser = async () => {
  const existingScheduler = await prisma.user.findFirst({
    where: {
      active: true,
      role: {
        in: ['ADMIN', 'DENTIST'],
      },
    },
    orderBy: {
      createdAt: 'asc',
    },
  });

  if (existingScheduler) {
    return existingScheduler;
  }

  return prisma.user.upsert({
    where: { email: systemSchedulerEmail },
    update: {
      active: true,
      name: 'Agenda del Sistema',
      role: 'ADMIN',
    },
    create: {
      email: systemSchedulerEmail,
      password: 'system-placeholder',
      name: 'Agenda del Sistema',
      role: 'ADMIN',
      active: true,
    },
  });
};

const emitAppointmentCommunications = async (input: {
  eventType: 'APPOINTMENT_CREATED' | 'APPOINTMENT_UPDATED' | 'APPOINTMENT_RESCHEDULED' | 'APPOINTMENT_STATUS_CHANGED';
  sourceMode: 'database' | 'local-fallback';
  appointment: {
    id: string;
    fecha: Date | string;
    motivo: string;
    estado: string;
    paciente: {
      id: string;
      nombres: string;
      apellidos: string;
      email: string | null;
      telefono: string | null;
    };
    dentista: {
      id: string;
      nombre: string;
      email: string | null;
    };
  };
  reason?: string | null;
}) => {
  try {
    await recordAppointmentCommunications({
      eventType: input.eventType,
      appointmentId: input.appointment.id,
      startsAt:
        input.appointment.fecha instanceof Date
          ? input.appointment.fecha.toISOString()
          : input.appointment.fecha,
      patient: {
        id: input.appointment.paciente.id,
        name: `${input.appointment.paciente.nombres} ${input.appointment.paciente.apellidos}`.trim(),
        email: input.appointment.paciente.email,
        phone: input.appointment.paciente.telefono,
      },
      dentist: {
        id: input.appointment.dentista.id,
        name: input.appointment.dentista.nombre,
        email: input.appointment.dentista.email,
      },
      motivo: input.appointment.motivo,
      status: input.appointment.estado,
      sourceMode: input.sourceMode,
      reason: input.reason ?? null,
    });
  } catch (communicationError) {
    console.error('Error emitting appointment communications:', communicationError);
  }
};

export const getAppointments = async (req: Request, res: Response) => {
  const requestedDate = resolveRequestedDate(req.query.date);
  const { dayStart, dayEnd } = getDayBounds(requestedDate);

  try {
    const appointments = await prisma.cita.findMany({
      where: {
        fecha: {
          gte: dayStart,
          lte: dayEnd,
        },
      },
      include: {
        paciente: true,
        dentista: true,
      },
      orderBy: { fecha: 'asc' },
    });

    res.setHeader('X-Data-Source', 'database');
    return res.json(appointments.map((appointment) => serializeDatabaseAppointment(appointment)));
  } catch (error) {
    console.warn('Database unavailable, using local appointment fallback store.', error);

    try {
      const appointments = await listFallbackAppointments(requestedDate);
      res.setHeader('X-Data-Source', 'local-fallback');
      return res.json(appointments.map((appointment) => serializeFallbackAppointment(appointment)));
    } catch (fallbackError) {
      console.error('Error fetching appointments:', fallbackError);
      return res.status(500).json({ error: 'Error interno del servidor' });
    }
  }
};

export const createAppointment = async (req: Request, res: Response) => {
  const { pacienteId, fecha, motivo, estado, notas } = req.body;
  const dentistaId = normalizeOptionalId(req.body.dentistaId);

  if (typeof pacienteId !== 'string' || typeof fecha !== 'string' || typeof motivo !== 'string') {
    return res.status(400).json({ error: 'Paciente, fecha y motivo son obligatorios' });
  }

  const normalizedMotivo = motivo.trim();
  const normalizedNotes = typeof notas === 'string' ? notas.trim() : null;

  if (!normalizedMotivo) {
    return res.status(400).json({ error: 'El motivo de la cita es obligatorio' });
  }

  const parsedDate = new Date(fecha);

  if (Number.isNaN(parsedDate.getTime())) {
    return res.status(400).json({ error: 'La fecha de la cita no es valida' });
  }

  const normalizedStatus =
    typeof estado === 'string' && appointmentStatuses.includes(estado as AppointmentStatus)
      ? (estado as AppointmentStatus)
      : EstadoCita.PROGRAMADA;

  try {
    const patient = await prisma.paciente.findUnique({
      where: { id: pacienteId },
    });

    if (!patient) {
      return res.status(404).json({ error: 'Paciente no encontrado' });
    }

    const schedulingUser = await resolveAssignableDentist(dentistaId);

    if (!schedulingUser) {
      return res.status(404).json({ error: 'Odontologo no encontrado' });
    }

    if (appointmentConsumesAvailability(normalizedStatus)) {
      await assertDoctorSlotAvailability(schedulingUser.id, parsedDate);
    }

    const appointment = await prisma.cita.create({
      data: {
        fecha: parsedDate,
        motivo: normalizedMotivo,
        estado: normalizedStatus,
        notas: normalizedNotes || null,
        pacienteId,
        dentistaId: schedulingUser.id,
      },
      include: {
        paciente: true,
        dentista: true,
      },
    });

    await emitAppointmentCommunications({
      eventType: 'APPOINTMENT_CREATED',
      sourceMode: 'database',
      appointment: {
        id: appointment.id,
        fecha: appointment.fecha,
        motivo: appointment.motivo,
        estado: appointment.estado,
        paciente: appointment.paciente,
        dentista: {
          id: appointment.dentista.id,
          nombre: appointment.dentista.name,
          email: appointment.dentista.email,
        },
      },
    });

    res.setHeader('X-Data-Source', 'database');
    return res.status(201).json(serializeDatabaseAppointment(appointment));
  } catch (error) {
    const availabilityErrorResponse = handleAppointmentAvailabilityError(error, res);

    if (availabilityErrorResponse) {
      return availabilityErrorResponse;
    }

    console.warn('Database unavailable while creating appointment, using local fallback store.', error);

    try {
      const fallbackDentist = await resolveFallbackAssignableDentist(dentistaId);

      if (!fallbackDentist) {
        return res.status(404).json({ error: 'Odontologo no encontrado' });
      }

      if (appointmentConsumesAvailability(normalizedStatus)) {
        await assertDoctorSlotAvailability(fallbackDentist.id, parsedDate);
      }

      const appointment = await createFallbackAppointment({
        pacienteId,
        dentistaId: fallbackDentist.id,
        fecha: parsedDate.toISOString(),
        motivo: normalizedMotivo,
        estado: normalizedStatus,
        notas: normalizedNotes || null,
      });

      await emitAppointmentCommunications({
        eventType: 'APPOINTMENT_CREATED',
        sourceMode: 'local-fallback',
        appointment: {
          id: appointment.id,
          fecha: appointment.fecha,
          motivo: appointment.motivo,
          estado: appointment.estado,
          paciente: {
            id: appointment.pacienteId,
            nombres: appointment.pacienteNombres,
            apellidos: appointment.pacienteApellidos,
            email: appointment.pacienteEmail,
            telefono: appointment.pacienteTelefono,
          },
          dentista: {
            id: appointment.dentistaId,
            nombre: appointment.dentistaNombre,
            email: fallbackDentist?.email ?? null,
          },
        },
      });

      res.setHeader('X-Data-Source', 'local-fallback');
      return res.status(201).json(serializeFallbackAppointment(appointment));
    } catch (fallbackError) {
      const availabilityErrorResponse = handleAppointmentAvailabilityError(fallbackError, res);

      if (availabilityErrorResponse) {
        return availabilityErrorResponse;
      }

      if (fallbackError instanceof MissingFallbackPatientError) {
        return res.status(404).json({ error: fallbackError.message });
      }

      if (fallbackError instanceof MissingFallbackDentistError) {
        return res.status(404).json({ error: fallbackError.message });
      }

      console.error('Error creating appointment:', fallbackError);
      return res.status(500).json({ error: 'Error interno del servidor' });
    }
  }
};

export const updateAppointment = async (req: Request, res: Response) => {
  const appointmentId = getAppointmentIdFromRequest(req, res);

  if (!appointmentId) {
    return;
  }

  const normalizedMotivo = typeof req.body.motivo === 'string' ? req.body.motivo.trim() : undefined;
  const hasDateField = Object.prototype.hasOwnProperty.call(req.body, 'fecha');
  const hasNotesField = Object.prototype.hasOwnProperty.call(req.body, 'notas');
  const hasDentistField = Object.prototype.hasOwnProperty.call(req.body, 'dentistaId');
  const parsedDate = hasDateField ? parseAppointmentDate(req.body.fecha) : null;
  const normalizedNotes = hasNotesField ? normalizeNullableText(req.body.notas) : undefined;
  const normalizedDentistId = hasDentistField ? normalizeOptionalId(req.body.dentistaId) : null;
  const rescheduleReason = normalizeNullableText(req.body.rescheduleReason);
  const normalizedStatus =
    typeof req.body.estado === 'string' && appointmentStatuses.includes(req.body.estado as AppointmentStatus)
      ? (req.body.estado as AppointmentStatus)
      : undefined;

  if (typeof req.body.motivo === 'string' && !normalizedMotivo) {
    return res.status(400).json({ error: 'El motivo de la cita es obligatorio' });
  }

  if (hasDateField && !parsedDate) {
    return res.status(400).json({ error: 'La nueva fecha de la cita no es valida' });
  }

  if (
    !hasDateField &&
    normalizedMotivo === undefined &&
    normalizedStatus === undefined &&
    normalizedNotes === undefined &&
    !hasDentistField &&
    !rescheduleReason
  ) {
    return res.status(400).json({ error: 'No se enviaron cambios para la cita' });
  }

  try {
    const currentAppointment = await prisma.cita.findUnique({
      where: { id: appointmentId },
      include: {
        paciente: true,
        dentista: true,
      },
    });

    if (!currentAppointment) {
      return res.status(404).json({ error: 'Cita no encontrada' });
    }

    const nextDate = parsedDate ?? currentAppointment.fecha;
    const nextStatus = normalizedStatus ?? currentAppointment.estado;
    const nextDentist = hasDentistField
      ? await resolveAssignableDentist(normalizedDentistId)
      : currentAppointment.dentista;

    if (!nextDentist) {
      return res.status(404).json({ error: 'Odontologo no encontrado' });
    }

    if (
      shouldValidateNextSlot({
        currentStatus: currentAppointment.estado,
        nextStatus,
        hasDateField,
        hasDentistField,
      })
    ) {
      await assertDoctorSlotAvailability(nextDentist.id, nextDate, {
        excludeAppointmentId: appointmentId,
      });
    }

    const auditTrail = buildAppointmentAuditTrail({
      currentDate: currentAppointment.fecha,
      nextDate,
      currentStatus: currentAppointment.estado,
      nextStatus,
      currentDentistName: currentAppointment.dentista.name,
      nextDentistName: nextDentist.name,
      reason: rescheduleReason,
    });
    const nextNotes = mergeAppointmentNotes(currentAppointment.notas, normalizedNotes, auditTrail);

    const appointment = await prisma.cita.update({
      where: { id: appointmentId },
      data: {
        ...(parsedDate ? { fecha: parsedDate } : {}),
        ...(normalizedMotivo !== undefined ? { motivo: normalizedMotivo } : {}),
        ...(normalizedStatus ? { estado: normalizedStatus } : {}),
        ...(hasDentistField ? { dentistaId: nextDentist.id } : {}),
        ...(normalizedNotes !== undefined || auditTrail ? { notas: nextNotes } : {}),
      },
      include: {
        paciente: true,
        dentista: true,
      },
    });

    const communicationEventType =
      parsedDate && parsedDate.getTime() !== currentAppointment.fecha.getTime()
        ? 'APPOINTMENT_RESCHEDULED'
        : normalizedStatus && normalizedStatus !== currentAppointment.estado
          ? 'APPOINTMENT_STATUS_CHANGED'
          : 'APPOINTMENT_UPDATED';

    await emitAppointmentCommunications({
      eventType: communicationEventType,
      sourceMode: 'database',
      appointment: {
        id: appointment.id,
        fecha: appointment.fecha,
        motivo: appointment.motivo,
        estado: appointment.estado,
        paciente: appointment.paciente,
        dentista: {
          id: appointment.dentista.id,
          nombre: appointment.dentista.name,
          email: appointment.dentista.email,
        },
      },
      reason: rescheduleReason,
    });

    res.setHeader('X-Data-Source', 'database');
    return res.json(serializeDatabaseAppointment(appointment));
  } catch (error) {
    const availabilityErrorResponse = handleAppointmentAvailabilityError(error, res);

    if (availabilityErrorResponse) {
      return availabilityErrorResponse;
    }

    console.warn('Database unavailable while updating appointment, using local fallback store.', error);

    try {
      const fallbackAppointments = await listFallbackAppointments();
      const currentAppointment = fallbackAppointments.find((appointment) => appointment.id === appointmentId);

      if (!currentAppointment) {
        return res.status(404).json({ error: 'Cita no encontrada' });
      }

      const nextDate = parsedDate ?? new Date(currentAppointment.fecha);
      const nextStatus = normalizedStatus ?? currentAppointment.estado;
      const nextDentist = hasDentistField
        ? await resolveFallbackAssignableDentist(normalizedDentistId)
        : await findFallbackUserById(currentAppointment.dentistaId);

      if (!nextDentist) {
        return res.status(404).json({ error: 'Odontologo no encontrado' });
      }

      if (
        shouldValidateNextSlot({
          currentStatus: currentAppointment.estado,
          nextStatus,
          hasDateField,
          hasDentistField,
        })
      ) {
        await assertDoctorSlotAvailability(nextDentist.id, nextDate, {
          excludeAppointmentId: appointmentId,
        });
      }

      const auditTrail = buildAppointmentAuditTrail({
        currentDate: currentAppointment.fecha,
        nextDate,
        currentStatus: currentAppointment.estado,
        nextStatus,
        currentDentistName: currentAppointment.dentistaNombre,
        nextDentistName: nextDentist.name,
        reason: rescheduleReason,
      });
      const nextNotes = mergeAppointmentNotes(currentAppointment.notas, normalizedNotes, auditTrail);

      const updatedAppointment = await updateFallbackAppointment(appointmentId, {
        ...(parsedDate ? { fecha: parsedDate.toISOString() } : {}),
        ...(normalizedMotivo !== undefined ? { motivo: normalizedMotivo } : {}),
        ...(normalizedStatus ? { estado: normalizedStatus } : {}),
        ...(hasDentistField ? { dentistaId: nextDentist.id } : {}),
        ...(normalizedNotes !== undefined || auditTrail ? { notas: nextNotes } : {}),
      });

      const fallbackDentist = await findFallbackUserById(updatedAppointment.dentistaId);
      const communicationEventType =
        parsedDate && parsedDate.getTime() !== new Date(currentAppointment.fecha).getTime()
          ? 'APPOINTMENT_RESCHEDULED'
          : normalizedStatus && normalizedStatus !== currentAppointment.estado
            ? 'APPOINTMENT_STATUS_CHANGED'
            : 'APPOINTMENT_UPDATED';

      await emitAppointmentCommunications({
        eventType: communicationEventType,
        sourceMode: 'local-fallback',
        appointment: {
          id: updatedAppointment.id,
          fecha: updatedAppointment.fecha,
          motivo: updatedAppointment.motivo,
          estado: updatedAppointment.estado,
          paciente: {
            id: updatedAppointment.pacienteId,
            nombres: updatedAppointment.pacienteNombres,
            apellidos: updatedAppointment.pacienteApellidos,
            email: updatedAppointment.pacienteEmail,
            telefono: updatedAppointment.pacienteTelefono,
          },
          dentista: {
            id: updatedAppointment.dentistaId,
            nombre: updatedAppointment.dentistaNombre,
            email: fallbackDentist?.email ?? null,
          },
        },
        reason: rescheduleReason,
      });

      res.setHeader('X-Data-Source', 'local-fallback');
      return res.json(serializeFallbackAppointment(updatedAppointment));
    } catch (fallbackError) {
      const availabilityErrorResponse = handleAppointmentAvailabilityError(fallbackError, res);

      if (availabilityErrorResponse) {
        return availabilityErrorResponse;
      }

      if (fallbackError instanceof FallbackAppointmentNotFoundError) {
        return res.status(404).json({ error: fallbackError.message });
      }

      if (fallbackError instanceof MissingFallbackDentistError) {
        return res.status(404).json({ error: fallbackError.message });
      }

      console.error('Error updating appointment:', fallbackError);
      return res.status(500).json({ error: 'Error interno del servidor' });
    }
  }
};
