<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\RecurringAppointment;
use App\Models\Appointments;
use App\Models\Patients;
use App\Helper\Helper;
use App\Models\User;
use App\Models\Especiality;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Carbon;
use App\Exceptions\GlobalException;
use DateTime;
use DateTimeZone;
use App\Models\Settings;
use Illuminate\Support\Facades\Mail;
use App\Mail\AppointmentReminderMail;

class RecurringAppointmentController extends Controller
{
    /**
     * Mostrar la lista de citas recurrentes
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function index(Request $request)
    {
        try {
            $query = RecurringAppointment::where('is_active', true);
            
            // Filtrar por paciente si se proporciona el ID
            if ($request->has('patient_id')) {
                $query->where('patients_id', $request->input('patient_id'));
            }
            
            $recurringAppointments = $query->with(['patient', 'doctor', 'especialityRelation'])
                ->orderBy('next_appointment_date', 'asc')
                ->get();
                
            return view('recurring-appointments.index', [
                'recurringAppointments' => $recurringAppointments,
                'patient_id' => $request->input('patient_id')
            ]);
        } catch (\Throwable $th) {
            throw new GlobalException($th->getMessage());
        }
    }

    /**
     * Mostrar formulario para crear una nueva cita recurrente desde una cita existente
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function create(Request $request)
    {
        try {
            $appointmentId = $request->input('appointment_id');
            
            // Verificar si se proporcionó un ID de cita
            if (!$appointmentId) {
                return redirect()->route('/admin/view-appointments')
                    ->with('error', __('recurring-appointments.errorNoAppointment'));
            }
            
            // Intentar buscar la cita
            try {
                $appointment = Appointments::findOrFail($appointmentId);
            } catch (\Exception $e) {
                return redirect()->route('/admin/view-appointments')
                    ->with('error', __('recurring-appointments.errorAppointmentNotFound'));
            }
            
            $doctor = User::find($appointment->users_id);
            $especiality = Especiality::find($appointment->especiality);
            $patient = Patients::find($appointment->patients_id);
            
            return view('recurring-appointments.create', [
                'appointment' => $appointment,
                'doctor' => $doctor,
                'especiality' => $especiality,
                'patient' => $patient
            ]);
        } catch (\Throwable $th) {
            throw new GlobalException($th->getMessage());
        }
    }

    /**
     * Almacenar una nueva cita recurrente
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        try {

            $validated = $request->validate([
                'original_appointment_id' => 'required|exists:appointments,id',
                'days_interval' => 'required|integer|min:1',
                'description' => 'nullable|string',
                'auto_schedule' => 'nullable',
                'reminder_days' => 'required|integer|min:1|max:30',
            ]);

            
            $appointment = Appointments::findOrFail($validated['original_appointment_id']);
            
            $recurringAppointment = new RecurringAppointment();
            $recurringAppointment->id = md5(uniqid()); // Generar un ID único
            $recurringAppointment->original_appointment_id = $appointment->id;
            $recurringAppointment->patients_id = $appointment->patients_id;
            $recurringAppointment->users_id = $appointment->users_id;
            $recurringAppointment->especiality = $appointment->especiality;
            $recurringAppointment->days_interval = $validated['days_interval'];
            $recurringAppointment->description = $validated['description'] ?? null;
            $recurringAppointment->is_active = true;
            $recurringAppointment->auto_schedule = $request->has('auto_schedule');
            $recurringAppointment->reminder_days = $validated['reminder_days'];
            $recurringAppointment->notification_sent = false;
            
            // Calcular la fecha de la próxima cita basada en la cita original
            $originalDate = Carbon::parse($appointment->date);
            $recurringAppointment->next_appointment_date = $originalDate->copy()->addDays($validated['days_interval']);
            
            $recurringAppointment->save();
            
            return redirect()->route('recurring-appointments.index')
                ->with('success', __('recurring-appointments.successCreate'));
        } catch (\Throwable $th) {
            throw new GlobalException($th->getMessage());
        }
    }

    /**
     * Mostrar una cita recurrente específica
     *
     * @param  string  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        try {
            $recurringAppointment = RecurringAppointment::with([
                'patient', 
                'doctor', 
                'especialityRelation', 
                'originalAppointment'
            ])->findOrFail($id);
            
            return view('recurring-appointments.show', [
                'recurringAppointment' => $recurringAppointment
            ]);
        } catch (\Throwable $th) {
            throw new GlobalException($th->getMessage());
        }
    }

    /**
     * Mostrar formulario para editar una cita recurrente
     *
     * @param  string  $id
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
        try {
            $recurringAppointment = RecurringAppointment::with([
                'patient', 
                'doctor', 
                'especialityRelation'
            ])->findOrFail($id);
            
            return view('recurring-appointments.edit', [
                'recurringAppointment' => $recurringAppointment
            ]);
        } catch (\Throwable $th) {
            throw new GlobalException($th->getMessage());
        }
    }

    /**
     * Actualizar una cita recurrente
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  string  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        try {
            $validated = $request->validate([
                'days_interval' => 'required|integer|min:1',
                'description' => 'nullable|string',
                'auto_schedule' => 'nullable',
                'reminder_days' => 'required|integer|min:1|max:30',
                'is_active' => 'boolean'
            ]);
            
            $recurringAppointment = RecurringAppointment::findOrFail($id);
            
            // Actualizar datos
            $recurringAppointment->days_interval = $validated['days_interval'];
            $recurringAppointment->description = $validated['description'] ?? $recurringAppointment->description;
            $recurringAppointment->auto_schedule = $request->has('auto_schedule');
            $recurringAppointment->reminder_days = $validated['reminder_days'];
            $recurringAppointment->is_active = $request->has('is_active');
            
            // Si cambió el intervalo de días, recalcular la fecha de la próxima cita
            if ($recurringAppointment->isDirty('days_interval')) {
                $recurringAppointment->calculateNextAppointmentDate();
            }
            
            $recurringAppointment->save();
            
            return redirect()->route('recurring-appointments.index')
                ->with('success', __('recurring-appointments.successUpdate'));
        } catch (\Throwable $th) {
            throw new GlobalException($th->getMessage());
        }
    }

    /**
     * Cancelar (desactivar) una cita recurrente
     *
     * @param  string  $id
     * @return \Illuminate\Http\Response
     */
    public function cancel($id)
    {
        try {
            $recurringAppointment = RecurringAppointment::findOrFail($id);
            $recurringAppointment->is_active = false;
            $recurringAppointment->save();
            
            return redirect()->route('recurring-appointments.index')
                ->with('success', __('recurring-appointments.successCancel'));
        } catch (\Throwable $th) {
            throw new GlobalException($th->getMessage());
        }
    }

    /**
     * Procesar citas recurrentes pendientes (para uso con cronjob)
     * Este método verifica y envía notificaciones para citas próximas
     * y también crea citas automáticas si está configurado
     *
     * @return void
     */
    public function processPendingRecurringAppointments()
    {
        try {
            // 1. Procesar citas recurrentes para programación automática y notificaciones
            $pendingRecurringAppointments = RecurringAppointment::where('is_active', true)
                ->where('next_appointment_date', '>=', Carbon::now()->startOfDay())
                ->where('next_appointment_date', '<=', Carbon::now()->copy()->addDays(5)->endOfDay())
                // Evitar procesamiento múltiple: verificar que la cita no se haya procesado recientemente
                ->where(function($query) {
                    $query->where('notification_sent', false)
                          ->orWhere('updated_at', '<=', Carbon::now()->subHours(12));
                })
                ->get();
            
            \Illuminate\Support\Facades\Log::info("Procesando " . $pendingRecurringAppointments->count() . " citas recurrentes pendientes");
            
            // Para evitar duplicados, mantener registro de citas creadas en esta ejecución
            $processedAppointments = [];
            
            foreach ($pendingRecurringAppointments as $recurring) {
                $appointmentKey = $recurring->id . '-' . $recurring->next_appointment_date->format('Y-m-d');
                
                // Verificar si ya procesamos esta cita en esta ejecución
                if (in_array($appointmentKey, $processedAppointments)) {
                    \Illuminate\Support\Facades\Log::warning(
                        "Omitiendo cita recurrente ya procesada en esta ejecución: " . $appointmentKey
                    );
                    continue;
                }
                
                $daysUntilAppointment = Carbon::now()->startOfDay()->diffInDays(Carbon::parse($recurring->next_appointment_date)->startOfDay(), false);
                
                \Illuminate\Support\Facades\Log::info(
                    "Procesando cita recurrente para paciente ID: " . $recurring->patients_id . 
                    ", días hasta la cita: " . $daysUntilAppointment .
                    ", fecha programada: " . Carbon::parse($recurring->next_appointment_date)->format('Y-m-d H:i:s')
                );
                
                // Solo procesar si está dentro del período de recordatorio
                if ($daysUntilAppointment <= $recurring->reminder_days && $daysUntilAppointment >= 0) {
                    
                    // Si requiere auto-programación, verificar disponibilidad y crear cita
                    if ($recurring->auto_schedule) {
                        // Verificar que no exista ya una cita creada para esta fecha y paciente
                        $existingAppointment = Appointments::where('patients_id', $recurring->patients_id)
                            ->where('users_id', $recurring->users_id)
                            ->where('date', '>=', Carbon::parse($recurring->next_appointment_date)->startOfDay())
                            ->where('date', '<=', Carbon::parse($recurring->next_appointment_date)->endOfDay())
                            ->where('active', 1)
                            ->where('deleted', 0)
                            ->first();
                            
                        if ($existingAppointment) {
                            \Illuminate\Support\Facades\Log::info(
                                "Ya existe una cita para este paciente en esta fecha. Omitiendo creación."
                            );
                            
                            // Marcar como procesada para la siguiente iteración
                            $recurring->notification_sent = true;
                            $recurring->calculateNextAppointmentDate();
                            $recurring->save();
                            continue;
                        }
                        
                        // Verificar disponibilidad del doctor y obtener fecha real
                        $realAppointmentDateTime = $this->determineRealAppointmentDate($recurring);
                        
                        \Illuminate\Support\Facades\Log::info(
                            "Fecha real determinada para la cita: " . 
                            ($realAppointmentDateTime ? Carbon::parse($realAppointmentDateTime)->format('Y-m-d H:i:s') : "No disponible")
                        );
                        
                        if ($realAppointmentDateTime) {
                            // Enviar notificación con la fecha REAL de la cita
                            $this->sendReminderNotification($recurring, $realAppointmentDateTime);
                            
                            // Marcar como notificado
                            $recurring->notification_sent = true;
                            $recurring->touch(); // Actualizar timestamp para evitar reprocesamiento inmediato
                            $recurring->save();
                            
                            // Crear la cita con la fecha real
                            $appointmentCreated = $this->createAppointmentWithDate($recurring, $realAppointmentDateTime);
                            
                            // Solo actualizar la fecha de la próxima cita si se creó correctamente la actual
                            if ($appointmentCreated) {
                                // Agregar a la lista de procesados para evitar duplicados
                                $processedAppointments[] = $appointmentKey;
                                
                                // Calcular la fecha para la siguiente cita recurrente
                                $recurring->calculateNextAppointmentDate();
                                $recurring->notification_sent = false; // Reiniciar para la siguiente cita
                                $recurring->save();
                                
                                \Illuminate\Support\Facades\Log::info(
                                    "Próxima cita recurrente programada para: " . 
                                    Carbon::parse($recurring->next_appointment_date)->format('Y-m-d H:i:s')
                                );
                            }
                        } else {
                            // No se pudo encontrar horario disponible
                            $this->sendFailureNotification($recurring);
                        }
                    } else {
                        // Si no es auto-programación, solo enviar recordatorio con la fecha planificada
                        $this->sendReminderNotification($recurring);
                        
                        // Marcar como notificado
                        $recurring->notification_sent = true;
                        $recurring->touch(); // Actualizar timestamp
                        $recurring->save();
                    }
                }
            }
            
            return response()->json(['success' => true, 'message' => 'Citas recurrentes procesadas correctamente']);
        } catch (\Throwable $th) {
            \Illuminate\Support\Facades\Log::error("Error al procesar citas recurrentes: " . $th->getMessage());
            return response()->json(['success' => false, 'message' => $th->getMessage()], 500);
        }
    }

    /**
     * Determinar la fecha real para la cita verificando disponibilidad
     *
     * @param  \App\Models\RecurringAppointment  $recurring
     * @return string|null  Fecha y hora real para la cita o null si no hay disponibilidad
     */
    protected function determineRealAppointmentDate(RecurringAppointment $recurring)
    {
        // Obtener la hora original de la cita
        $originalTime = Carbon::parse($recurring->originalAppointment->date)->format('H:i:s');
        
        // Crear la fecha para la nueva cita
        $nextAppointmentDate = Carbon::parse($recurring->next_appointment_date)->format('Y-m-d') . ' ' . $originalTime;
        
        // Verificar si el doctor está disponible en esa fecha/hora
        $doctorId = $recurring->users_id;
        
        if (Appointments::isDoctorAvailable($doctorId, $nextAppointmentDate)) {
            return $nextAppointmentDate;
        } else {
            // El doctor no está disponible, buscar alternativas
            $alternativeSlot = $this->findNextAvailableSlot($doctorId, $nextAppointmentDate);
            
            // Registrar la fecha alternativa seleccionada
            if ($alternativeSlot) {
                \Illuminate\Support\Facades\Log::info(
                    "Cita recurrente reasignada: Fecha original " . 
                    Carbon::parse($nextAppointmentDate)->format('d/m/Y H:i') . 
                    ". Nueva fecha: " . Carbon::parse($alternativeSlot)->format('d/m/Y H:i')
                );
                
                // Enviar notificación al paciente sobre el cambio de horario si es necesario
                // $this->sendReschedulingNotification($recurring, $alternativeSlot);
            }
            
            return $alternativeSlot;
        }
    }

    /**
     * Crear una cita con una fecha específica
     *
     * @param  \App\Models\RecurringAppointment  $recurring
     * @param  string  $appointmentDateTime
     * @return bool
     */
    protected function createAppointmentWithDate(RecurringAppointment $recurring, $appointmentDateTime)
    {
        // Crear la nueva cita
        $appointment = new Appointments();
        $appointment->id = md5(uniqid());
        $appointment->patients_id = $recurring->patients_id;
        $appointment->users_id = $recurring->users_id;
        $appointment->code = "MED-" . substr(md5(now()->format('Y-m-d H:i:s.u')), 0, 4);
        $appointment->date = $appointmentDateTime;
        $appointment->active = 1;
        $appointment->deleted = 0;
        $appointment->especiality = $recurring->especiality;
        $appointment->summary = $recurring->description ?? 'Cita recurrente programada automáticamente';
        
        $appointment->save();
        
        return true;
    }

    /**
     * Enviar notificación de recordatorio para una cita recurrente
     *
     * @param  \App\Models\RecurringAppointment  $recurring
     * @param  string|null  $realAppointmentDateTime  Fecha y hora real de la cita
     * @return void
     */
    protected function sendReminderNotification(RecurringAppointment $recurring, $realAppointmentDateTime = null)
    {
        $patient = $recurring->patient;
        
        // Usar la fecha real si se proporciona, de lo contrario usar next_appointment_date
        if ($realAppointmentDateTime) {
            $appointmentDate = Carbon::parse($realAppointmentDateTime);
        } else {
            // Verificar si la fecha original tiene hora específica o es medianoche
            $nextDate = Carbon::parse($recurring->next_appointment_date);
            
            // Si la hora es 00:00, intentar obtener la hora de la cita original
            if ($nextDate->hour == 0 && $nextDate->minute == 0 && $recurring->originalAppointment) {
                $originalHour = Carbon::parse($recurring->originalAppointment->date)->format('H');
                $originalMinute = Carbon::parse($recurring->originalAppointment->date)->format('i');
                
                // Usar la hora de la cita original
                $appointmentDate = $nextDate->copy()->setHour($originalHour)->setMinute($originalMinute);
                
                \Illuminate\Support\Facades\Log::info(
                    "Ajustando hora de 00:00 a la hora original de la cita: " . $appointmentDate->format('H:i')
                );
            } else {
                $appointmentDate = $nextDate;
            }
        }
        
        // Asegurar que tenemos la hora correcta (para debug)
        \Illuminate\Support\Facades\Log::info(
            "Enviando recordatorio con fecha/hora: " . $appointmentDate->format('d/m/Y H:i:s')
        );
        
        $daysUntil = Carbon::now()->startOfDay()->diffInDays(
            $appointmentDate->copy()->startOfDay(), // Usamos copy() para no modificar el original
            false
        );
        
        // Datos para la notificación
        $data = [
            'patient_name' => $patient->firstname . ' ' . $patient->lastname,
            'days_until' => $daysUntil,
            'appointment_date' => $appointmentDate->format('d/m/Y'),
            'appointment_time' => $appointmentDate->format('h:i A'),   
            'appointment_datetime' => $appointmentDate->format('d/m/Y h:i A'),
            'doctor_name' => $recurring->doctor->name,
            'especiality' => $recurring->especialityRelation->name,
            'description' => $recurring->description,
            'contactNumber' => Settings::findConfigValue("CONTACT_PHONE_NUMBER"),
            'contactEmail' => Settings::findConfigValue("CONTACT_EMAIL"),
        ];
        
        // Determinar destinatarios
        $emails = [];
        if ($patient->age > 17) {
            if (isset($patient->email)) {
                $emails[] = $patient->email;
            }
        } else {
            $emails = array_merge($emails, Patients::getEmailForRepresentatives($patient));
        }
        
        // Enviar correo si hay destinatarios
        if (!empty($emails)) {
            Mail::to($emails)->send(new AppointmentReminderMail($data));
        }
    }

    /**
     * Buscar el próximo horario disponible para el doctor
     *
     * @param  int  $doctorId
     * @param  string  $baseDateTime
     * @return string|null
     */
    protected function findNextAvailableSlot($doctorId, $baseDateTime)
    {
        $baseDate = Carbon::parse($baseDateTime);
        $originalTime = $baseDate->format('H:i:s');
        $dayOfWeek = $baseDate->dayOfWeek; // Guardar el día de la semana (0 = domingo, 1 = lunes, etc.)
        $interval = 30; // Intervalo en minutos para las citas
        
        \Illuminate\Support\Facades\Log::info(
            "Buscando slot disponible a partir de: " . $baseDate->format('Y-m-d H:i:s') . 
            ", día de la semana: " . $dayOfWeek
        );
        
        // 1. Primero intentar en la fecha original
        if ($this->isSlotAvailable($doctorId, $baseDate, $interval)) {
            $result = $baseDate->format('Y-m-d H:i:s');
            \Illuminate\Support\Facades\Log::info("Slot disponible encontrado en fecha original: " . $result);
            return $result;
        }
        
        // 2. Enfoque diario: Verificar los próximos 14 días consecutivos
        for ($day = 1; $day <= 14; $day++) {
            $nextDayDate = $baseDate->copy()->addDays($day);
            
            \Illuminate\Support\Facades\Log::info(
                "Buscando en el día " . $day . " siguiente: " . $nextDayDate->format('Y-m-d')
            );
            
            // Verificar si el doctor trabaja este día
            $testTime = $nextDayDate->copy()->hour(9)->minute(0); // Usar 9am como referencia
            if (!Appointments::isDoctorAvailable($doctorId, $testTime)) {
                \Illuminate\Support\Facades\Log::info("El doctor no trabaja el " . $nextDayDate->format('Y-m-d'));
                continue; // Pasar al siguiente día
            }
            
            // Intentar con la misma hora que la original
            $slotDateTime = $nextDayDate->copy()->setTimeFromTimeString($originalTime);
            
            if ($this->isSlotAvailable($doctorId, $slotDateTime, $interval)) {
                $result = $slotDateTime->format('Y-m-d H:i:s');
                \Illuminate\Support\Facades\Log::info("Slot disponible encontrado en un día próximo: " . $result);
                return $result;
            }
            
            // Si no está disponible a la misma hora, probar con otras horas ese mismo día
            $dayStart = $nextDayDate->copy()->startOfDay();
            $dayEnd = $nextDayDate->copy()->endOfDay();
            $currentSlot = $dayStart->copy();
            
            while ($currentSlot->lte($dayEnd)) {
                if ($this->isSlotAvailable($doctorId, $currentSlot, $interval)) {
                    $result = $currentSlot->format('Y-m-d H:i:s');
                    \Illuminate\Support\Facades\Log::info("Slot alternativo encontrado en un día próximo: " . $result);
                    return $result;
                }
                $currentSlot->addMinutes($interval);
            }
        }
        
        // 3. Enfoque semanal: Si no hay disponibilidad en 14 días consecutivos, buscar en el mismo día de la semana durante las próximas 8 semanas
        for ($week = 2; $week <= 8; $week++) {
            // Saltar a la misma fecha pero en la siguiente semana
            $nextWeekDate = $baseDate->copy()->addWeeks($week);
            
            \Illuminate\Support\Facades\Log::info(
                "Buscando en la semana " . $week . " siguiente: " . $nextWeekDate->format('Y-m-d')
            );
            
            // Verificar si el doctor trabaja este día
            $testTime = $nextWeekDate->copy()->hour(9)->minute(0); // Usar 9am como referencia
            if (!Appointments::isDoctorAvailable($doctorId, $testTime)) {
                \Illuminate\Support\Facades\Log::info("El doctor no trabaja el " . $nextWeekDate->format('Y-m-d'));
                continue; // Pasar a la siguiente semana
            }
            
            // Intentar con la misma hora que la original
            $slotDateTime = $nextWeekDate->copy()->setTimeFromTimeString($originalTime);
            
            if ($this->isSlotAvailable($doctorId, $slotDateTime, $interval)) {
                $result = $slotDateTime->format('Y-m-d H:i:s');
                \Illuminate\Support\Facades\Log::info("Slot disponible encontrado en semana futura: " . $result);
                return $result;
            }
            
            // Si no está disponible a la misma hora, probar con otras horas ese mismo día
            $dayStart = $nextWeekDate->copy()->startOfDay();
            $dayEnd = $nextWeekDate->copy()->endOfDay();
            $currentSlot = $dayStart->copy();
            
            while ($currentSlot->lte($dayEnd)) {
                if ($this->isSlotAvailable($doctorId, $currentSlot, $interval)) {
                    $result = $currentSlot->format('Y-m-d H:i:s');
                    \Illuminate\Support\Facades\Log::info("Slot alternativo encontrado en semana futura: " . $result);
                    return $result;
                }
                $currentSlot->addMinutes($interval);
            }
        }
        
        // No se encontró disponibilidad después de búsqueda exhaustiva
        \Illuminate\Support\Facades\Log::warning("No se encontró disponibilidad para el doctor ID: " . $doctorId . " en los próximos 2 meses");
        return null;
    }
    
    /**
     * Verificar si un slot específico está disponible
     * 
     * @param  int  $doctorId
     * @param  \Carbon\Carbon  $dateTime
     * @param  int  $interval
     * @return bool
     */
    protected function isSlotAvailable($doctorId, $dateTime, $interval)
    {
        // 1. Verificar que el doctor tenga disponibilidad programada para ese horario
        if (!Appointments::isDoctorAvailable($doctorId, $dateTime)) {
            return false;
        }
        
        // 2. Verificar que el slot completo (incluyendo la duración) esté dentro del horario
        $slotEndTime = $dateTime->copy()->addMinutes($interval);
        if (!Appointments::isDoctorAvailable($doctorId, $slotEndTime)) {
            \Illuminate\Support\Facades\Log::info(
                "Slot descartado porque finaliza fuera del horario: " . 
                $dateTime->format('Y-m-d H:i') . " - " . $slotEndTime->format('H:i')
            );
            return false;
        }
        
        // 3. Verificar que no haya citas existentes en ese intervalo
        $startRange = $dateTime->copy()->subMinutes($interval / 2);
        $endRange = $dateTime->copy()->addMinutes($interval / 2);
        
        $existingAppointments = Appointments::whereBetween('date', [$startRange, $endRange])
            ->where('users_id', $doctorId)
            ->where('active', 1)
            ->where('deleted', 0)
            ->count();
        
        return $existingAppointments == 0;
    }

    /**
     * Enviar notificación de reprogramación al paciente
     *
     * @param  \App\Models\RecurringAppointment  $recurring
     * @param  string  $newDateTime
     * @return void
     */
    protected function sendReschedulingNotification(RecurringAppointment $recurring, $newDateTime)
    {
        $patient = $recurring->patient;
        
        // Datos para la notificación
        $data = [
            'patient_name' => $patient->firstname . ' ' . $patient->lastname,
            'original_date' => Carbon::parse($recurring->next_appointment_date)->format('d/m/Y'),
            'new_date' => Carbon::parse($newDateTime)->format('d/m/Y H:i'),
            'doctor_name' => $recurring->doctor->name,
            'especiality' => $recurring->especialityRelation->name,
            'description' => $recurring->description,
            'contactNumber' => Settings::findConfigValue("CONTACT_PHONE_NUMBER"),
            'contactEmail' => Settings::findConfigValue("CONTACT_EMAIL"),
        ];
        
        // Determinar destinatarios
        $emails = [];
        if ($patient->age > 17) {
            if (isset($patient->email)) {
                $emails[] = $patient->email;
            }
        } else {
            $emails = array_merge($emails, Patients::getEmailForRepresentatives($patient));
        }
        
        // Enviar correo si hay destinatarios
        if (!empty($emails)) {
            // En un entorno de producción, aquí enviarías un email usando una plantilla específica
            // para reprogramación. Por ahora solo lo registramos en el log.
            \Illuminate\Support\Facades\Log::info(
                "Notificación de reprogramación enviada a " . implode(", ", $emails) . 
                " para la cita de " . $data['patient_name']
            );
        }
    }

    /**
     * Enviar notificación de fallo al administrador
     *
     * @param  \App\Models\RecurringAppointment  $recurring
     * @return void
     */
    protected function sendFailureNotification(RecurringAppointment $recurring)
    {
        // En un entorno de producción, aquí enviarías un email al administrador.
        // Por ahora solo lo registramos en el log.
        \Illuminate\Support\Facades\Log::warning(
            "No se pudo programar automáticamente la cita recurrente para " . 
            $recurring->patient->firstname . " " . $recurring->patient->lastname . 
            " con el Dr. " . $recurring->doctor->name . 
            ". Se requiere intervención manual."
        );
    }
}
