<?php

namespace App\Actions\Tenancy\Visit;

use App\Models\Tenancy\Procedure;
use App\Models\Tenancy\Visit;
use App\Models\Tenancy\WorkingYear;
use App\Models\WorkingDay;
use Illuminate\Support\Carbon;

class GetBlockedDaysAction
{
    public function execute(int $specialistId, int $procedureId, Carbon $startDate, Carbon $endDate): array
    {
        $clinicWorkingDays = WorkingDay::ofTenant(tenant('unique_name'))
            ->get();

        $nonWorkingWeekday = [];
        $blockedDates = [];

        foreach ($clinicWorkingDays as $workingDay) {
            if (date('w') == $workingDay->day) {
                if ($workingDay->dayoff) {
                    $nonWorkingWeekday[] = $workingDay->day;
                }
                if ($startDate->lte(Carbon::now()) && strtotime($workingDay->close_time) < strtotime(date('H:i'))) {
                    $blockedDates[] = Carbon::now()->toDateString();
                }
            } else {
                if ($workingDay->dayoff || ! $workingDay->open_time || ! $workingDay->close_time) {
                    $nonWorkingWeekday[] = $workingDay->day;
                }
            }
        }

        $workingYear = WorkingYear::betweenDates($startDate, $endDate)
            ->ofSpecialist($specialistId)
            ->where('color', 'green')
            ->pluck('date')
            ->all();

        $startDateCopy = $startDate->copy();
        while ($endDate->gt($startDateCopy)) {
            if (in_array($startDateCopy->dayOfWeek, $nonWorkingWeekday)) {
                $blockedDates[] = $startDateCopy->toDateString();
            }
            if (! in_array($startDateCopy->toDateString(), $workingYear)) {
                $blockedDates[] = $startDateCopy->toDateString();
            }
            $startDateCopy->addDay();
        }

        $specialistVisitsByDate = Visit::ofSpecialist($specialistId)
            ->betweenDates($startDate, $endDate)
            ->orderBy('date')
            ->get()
            ->groupBy(function ($visit) {
                return Carbon::parse($visit->date)->format('Y-m-d');
            });

        $workingYearGroupedByDate = WorkingYear::whereIn('date', $specialistVisitsByDate->keys())
            ->ofSpecialist($specialistId)
            ->get()
            ->keyBy('date');
        $availableHoursByDate = [];
        foreach ($specialistVisitsByDate as $date => $specialistVisits) {
            if (isset($workingYearGroupedByDate[$date]) && $workingYearGroupedByDate[$date]) {
                $prevDate = Carbon::parse($date)->setTimeFromTimeString($workingYearGroupedByDate[$date]->time_start);
                foreach ($specialistVisits as $specialistVisit) {
                    $currDate = Carbon::parse($specialistVisit->date);
                    $availableHoursByDate[$date][] = $currDate->diffInMinutes($prevDate);
                    $prevDate = Carbon::parse($specialistVisit->visit_ends);
                }
                $prevDateEndOfDay = Carbon::parse($date)->setTimeFromTimeString($workingYearGroupedByDate[$date]->time_end);
                $availableHoursByDate[$date][] = $prevDateEndOfDay->diffInMinutes($prevDate);
            }
        }

        $procedure = Procedure::findOrFail($procedureId);

        foreach ($availableHoursByDate as $date => $availableHours) {
            if (max($availableHours) < $procedure->duration) {
                $blockedDates[] = $date;
            }
        }

        $blockedDates = array_unique($blockedDates);
        sort($blockedDates);

        return $blockedDates;
    }
}
