<?php

namespace App\Services;

use App\Enums\ConsultationStatus;
use App\Models\BlockedTime;
use App\Models\Consultation;
use App\Models\WorkingHour;
use Carbon\Carbon;

class AvailabilityService
{
    /**
     * Get availability status for all days in a month.
     *
     * @return array<string, string>
     */
    public function getMonthAvailability(int $year, int $month): array
    {
        $startOfMonth = Carbon::create($year, $month, 1)->startOfMonth();
        $endOfMonth = $startOfMonth->copy()->endOfMonth();

        $availability = [];
        $current = $startOfMonth->copy();

        while ($current->lte($endOfMonth)) {
            $availability[$current->format('Y-m-d')] = $this->getDateStatus($current);
            $current->addDay();
        }

        return $availability;
    }

    /**
     * Get the availability status for a specific date.
     */
    public function getDateStatus(Carbon $date): string
    {
        // Past date
        if ($date->lt(today())) {
            return 'past';
        }

        // Check if fully blocked
        if ($this->isDateFullyBlocked($date)) {
            return 'blocked';
        }

        // Check working hours
        $workingHour = WorkingHour::query()
            ->where('day_of_week', $date->dayOfWeek)
            ->where('is_active', true)
            ->first();

        if (! $workingHour) {
            return 'closed';
        }

        // Get available slots
        $availableSlots = $this->getAvailableSlots($date);

        if (empty($availableSlots)) {
            return 'full';
        }

        $totalSlots = count($workingHour->getSlots(60));
        if (count($availableSlots) < $totalSlots) {
            return 'partial';
        }

        return 'available';
    }

    /**
     * Get available time slots for a specific date.
     *
     * @return array<string>
     */
    public function getAvailableSlots(Carbon $date): array
    {
        $workingHour = WorkingHour::query()
            ->where('day_of_week', $date->dayOfWeek)
            ->where('is_active', true)
            ->first();

        if (! $workingHour) {
            return [];
        }

        // All possible slots
        $allSlots = $workingHour->getSlots(60);

        // Booked slots (pending and approved consultations block the slot)
        $bookedSlots = Consultation::query()
            ->whereDate('booking_date', $date)
            ->whereIn('status', [ConsultationStatus::Pending, ConsultationStatus::Approved])
            ->pluck('booking_time')
            ->map(fn ($t) => Carbon::parse($t)->format('H:i'))
            ->toArray();

        // Blocked slots
        $blockedSlots = $this->getBlockedSlots($date);

        return array_values(array_diff($allSlots, $bookedSlots, $blockedSlots));
    }

    /**
     * Get blocked time slots for a specific date.
     *
     * @return array<string>
     */
    private function getBlockedSlots(Carbon $date): array
    {
        $blockedTimes = BlockedTime::query()
            ->whereDate('block_date', $date)
            ->get();

        $blockedSlots = [];

        foreach ($blockedTimes as $blocked) {
            if ($blocked->isAllDay()) {
                // Block all slots for the day
                $workingHour = WorkingHour::query()
                    ->where('day_of_week', $date->dayOfWeek)
                    ->first();

                return $workingHour ? $workingHour->getSlots(60) : [];
            }

            // Calculate blocked slots from time range
            $start = Carbon::parse($blocked->start_time);
            $end = Carbon::parse($blocked->end_time);
            $current = $start->copy();

            while ($current->lt($end)) {
                $blockedSlots[] = $current->format('H:i');
                $current->addMinutes(60);
            }
        }

        return array_unique($blockedSlots);
    }

    /**
     * Check if a date is fully blocked (all-day block).
     */
    private function isDateFullyBlocked(Carbon $date): bool
    {
        return BlockedTime::query()
            ->whereDate('block_date', $date)
            ->whereNull('start_time')
            ->exists();
    }
}
