<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use App\Traits\Auditable;
use Carbon\Carbon;

class DoctorSchedule extends Model
{
    use HasFactory, Auditable;

    /**
     * Los días de la semana representados como constantes
     */
    const SUNDAY = 0;
    const MONDAY = 1;
    const TUESDAY = 2;
    const WEDNESDAY = 3;
    const THURSDAY = 4;
    const FRIDAY = 5;
    const SATURDAY = 6;

    /**
     * Los atributos que se pueden asignar masivamente
     *
     * @var array
     */
    protected $fillable = [
        'doctor_id',
        'day_of_week',
        'start_time',
        'end_time',
        'timezone',
        'active'
    ];

    /**
     * Atributos que deben ser convertidos
     *
     * @var array
     */
    protected $casts = [
        'day_of_week' => 'integer',
        'active' => 'boolean',
    ];

    /**
     * Relación con el doctor (usuario)
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
     */
    public function doctor()
    {
        return $this->belongsTo(User::class, 'doctor_id');
    }

    /**
     * Obtener el nombre del día en español
     *
     * @return string
     */
    public function getDayNameAttribute()
    {
        $days = [
            self::SUNDAY => 'Domingo',
            self::MONDAY => 'Lunes',
            self::TUESDAY => 'Martes',
            self::WEDNESDAY => 'Miércoles',
            self::THURSDAY => 'Jueves',
            self::FRIDAY => 'Viernes',
            self::SATURDAY => 'Sábado'
        ];

        return $days[$this->day_of_week] ?? 'Desconocido';
    }

    /**
     * Obtener el nombre del día en inglés
     *
     * @return string
     */
    public function getDayNameEnAttribute()
    {
        $days = [
            self::SUNDAY => 'Sunday',
            self::MONDAY => 'Monday',
            self::TUESDAY => 'Tuesday',
            self::WEDNESDAY => 'Wednesday',
            self::THURSDAY => 'Thursday',
            self::FRIDAY => 'Friday',
            self::SATURDAY => 'Saturday'
        ];

        return $days[$this->day_of_week] ?? 'Unknown';
    }

    /**
     * Verificar si una fecha y hora específica está dentro del horario
     *
     * @param \DateTime|string $dateTime
     * @return boolean
     */
    public function isWithinSchedule($dateTime)
    {
        // Convertir a objeto Carbon si es string
        if (is_string($dateTime)) {
            $dateTime = Carbon::parse($dateTime);
        }

        // Agregar logs para depuración
        \Log::info('Verificando disponibilidad de horario');
        \Log::info('Día de la semana a verificar: ' . $dateTime->dayOfWeek . ' - Día programado: ' . $this->day_of_week);
        \Log::info('Hora a verificar: ' . $dateTime->format('H:i:s'));
        \Log::info('Horario del doctor - Inicio: ' . $this->start_time . ' - Fin: ' . $this->end_time);

        // Verificar que sea el mismo día de la semana
        if ($dateTime->dayOfWeek != $this->day_of_week) {
            \Log::info('Día de la semana incorrecto, no disponible');
            return false;
        }

        // Obtener solo la hora:minutos:segundos para comparar
        $timeToCheck = Carbon::parse($dateTime->format('H:i:s'));
        $startTime = Carbon::parse($this->start_time);
        $endTime = Carbon::parse($this->end_time);

        // Verificar si está dentro del rango de horas
        $isAvailable = $timeToCheck->between($startTime, $endTime);
        \Log::info('¿Está disponible? ' . ($isAvailable ? 'Sí' : 'No'));
        
        return $isAvailable;
    }

    /**
     * Verificar si el horario se solapa con otro horario del mismo doctor
     *
     * @param integer $dayOfWeek
     * @param string $startTime
     * @param string $endTime
     * @param integer|null $exceptId ID del horario a excluir de la verificación
     * @return boolean
     */
    public static function overlapsWithExisting($doctorId, $dayOfWeek, $startTime, $endTime, $exceptId = null)
    {
        $query = self::where('doctor_id', $doctorId)
            ->where('day_of_week', $dayOfWeek)
            ->where(function ($query) use ($startTime, $endTime) {
                // Caso 1: El nuevo horario comienza dentro de un horario existente
                $query->where(function ($q) use ($startTime) {
                    $q->where('start_time', '<=', $startTime)
                      ->where('end_time', '>', $startTime);
                })
                // Caso 2: El nuevo horario termina dentro de un horario existente
                ->orWhere(function ($q) use ($endTime) {
                    $q->where('start_time', '<', $endTime)
                      ->where('end_time', '>=', $endTime);
                })
                // Caso 3: El nuevo horario engloba completamente un horario existente
                ->orWhere(function ($q) use ($startTime, $endTime) {
                    $q->where('start_time', '>=', $startTime)
                      ->where('end_time', '<=', $endTime);
                });
            });

        // Excluir el horario actual en caso de actualización
        if ($exceptId) {
            $query->where('id', '!=', $exceptId);
        }

        return $query->exists();
    }

    /**
     * Validar que la hora de fin sea posterior a la hora de inicio
     *
     * @param string $startTime
     * @param string $endTime
     * @return boolean
     */
    public static function validateTimeRange($startTime, $endTime)
    {
        return Carbon::parse($startTime)->lt(Carbon::parse($endTime));
    }
}
