<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Carbon\Carbon;

class Meja extends Model
{
    use HasFactory;

    protected $table = 'meja';
    
    protected $fillable = [
        'nomor_meja',
        'kapasitas',
        'status',
        'kategori',
        'deskripsi',
        'gambar'
    ];

    protected $attributes = [
        'status' => 'tersedia',
        'kategori' => 'outdoor'
    ];

    /**
     * Get all reservations for this table.
     */
    public function reservasi()
    {
        return $this->hasMany(Reservasi::class, 'meja_id');
    }

    /**
     * Get active reservations for this table.
     */
    public function activeReservations(): HasMany
    {
        return $this->reservasi()
            ->where('status', '!=', 'cancelled')
            ->where('date', '>=', Carbon::today())
            ->orderBy('date')
            ->orderBy('start_time');
    }

    /**
     * Check if table has any available slots for a specific date
     */
    public function hasAvailableSlotsForDate($date): bool
    {
        $reservations = $this->getReservationsForDate($date);
        $operatingHours = $this->getOperatingHours();
        
        // If no reservations, the entire day is available
        if ($reservations->isEmpty()) {
            return true;
        }

        $reservedSlots = $reservations->map(function ($reservation) {
            return [
                'start' => Carbon::parse($reservation->start_time),
                'end' => Carbon::parse($reservation->end_time)
            ];
        })->sortBy('start');

        // Check for any gaps between reservations
        $previousEnd = Carbon::parse($operatingHours['start']);
        
        foreach ($reservedSlots as $slot) {
            if ($previousEnd->lt($slot['start'])) {
                return true; // Found a gap
            }
            $previousEnd = max($previousEnd, $slot['end']);
        }

        // Check if there's time after the last reservation
        $lastSlotEnd = $reservedSlots->last()['end'];
        $closingTime = Carbon::parse($operatingHours['end']);
        
        return $lastSlotEnd->lt($closingTime);
    }

    /**
     * Get the next available date with open slots
     */
    public function getNextAvailableDate($startDate = null): ?string
    {
        $date = $startDate ? Carbon::parse($startDate) : Carbon::today();
        $maxDays = 30; // Limit search to next 30 days
        $daysChecked = 0;

        while ($daysChecked < $maxDays) {
            if ($this->hasAvailableSlotsForDate($date->format('Y-m-d'))) {
                return $date->format('Y-m-d');
            }
            $date->addDay();
            $daysChecked++;
        }

        return null;
    }

    /**
     * Get operating hours
     */
    public function getOperatingHours(): array
    {
        return [
            'start' => '10:00',
            'end' => '22:00'
        ];
    }

    /**
     * Get available time slots for a specific date
     */
    public function getAvailableTimeSlots($date): array
    {
        $reservations = $this->getReservationsForDate($date);
        $operatingHours = $this->getOperatingHours();
        $slots = [];
        
        $startTime = Carbon::parse($operatingHours['start']);
        $endTime = Carbon::parse($operatingHours['end']);
        $interval = 30; // 30 minutes interval

        // If date is today, start from current time rounded up to next 30 minutes
        if ($date === date('Y-m-d')) {
            $now = Carbon::now();
            $startTime = $now->copy()->addMinutes($interval - ($now->minute % $interval))->startOfMinute();
            if ($startTime->lt($now)) {
                $startTime->addMinutes($interval);
            }
        }

        while ($startTime < $endTime) {
            $timeSlot = $startTime->format('H:i');
            $isAvailable = true;

            // Check if this time slot conflicts with any existing reservations
            foreach ($reservations as $reservation) {
                $reservationStart = Carbon::parse($reservation->start_time);
                $reservationEnd = Carbon::parse($reservation->end_time);

                if ($startTime->between($reservationStart, $reservationEnd, true)) {
                    $isAvailable = false;
                    break;
                }
            }

            if ($isAvailable) {
                $slots[] = $timeSlot;
            }

            $startTime->addMinutes($interval);
        }

        return $slots;
    }

    /**
     * Get reservations for a specific date
     */
    public function getReservationsForDate($date)
    {
        return $this->reservasi()
            ->whereDate('date', $date)
            ->where('status', '!=', 'cancelled')
            ->get(['start_time', 'end_time']);
    }

    /**
     * Check if table is available for a specific time slot.
     */
    public function isAvailableForTimeSlot($date, $startTime, $endTime): bool
    {
        return Reservasi::isTimeSlotAvailable($this->id, $date, $startTime, $endTime);
    }

    /**
     * Get the status badge class
     */
    public function getStatusBadgeClass(): string
    {
        return match($this->status) {
            'tersedia' => 'bg-green-100 text-green-800',
            'dipesan' => 'bg-yellow-100 text-yellow-800',
            'terisi' => 'bg-red-100 text-red-800',
            default => 'bg-gray-100 text-gray-800',
        };
    }

    /**
     * Get formatted category name
     */
    public function getFormattedCategory(): string
    {
        return match($this->kategori) {
            'outdoor' => 'Outdoor',
            'vip-outdoor' => 'VIP Outdoor',
            'vip-indoor' => 'VIP Indoor',
            default => ucfirst($this->kategori),
        };
    }

    /**
     * Check if the table is available
     */
    public function getIsAvailableAttribute(): bool
    {
        return $this->status === 'tersedia';
    }

    /**
     * Format nomor meja for display
     */
    public function getNomorMejaFormattedAttribute(): string
    {
        return 'M' . str_pad($this->nomor_meja, 3, '0', STR_PAD_LEFT);
    }

    // Accessor untuk memastikan kapasitas selalu integer
    public function getKapasitasAttribute($value)
    {
        return (int) $value;
    }

    // Scope untuk meja yang tersedia
    public function scopeAvailable($query)
    {
        return $query->where('status', 'tersedia');
    }
} 