| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273 | <?php
namespace App\Support;
use App\Enums\Accounting\DayOfMonth;
use App\Enums\Accounting\DayOfWeek;
use App\Enums\Accounting\Frequency;
use App\Enums\Accounting\IntervalType;
use App\Enums\Accounting\Month;
use Carbon\CarbonImmutable;
use Filament\Forms\Get;
use Filament\Forms\Set;
use Illuminate\Support\Carbon;
class ScheduleHandler
{
    protected CarbonImmutable $today;
    protected Set $set;
    protected ?Get $get;
    public function __construct(Set $set, ?Get $get = null)
    {
        $this->today = today()->toImmutable();
        $this->set = $set;
        $this->get = $get;
    }
    protected function setMany(Set $set, array $values): void
    {
        foreach ($values as $key => $value) {
            $set($key, $value);
        }
    }
    public function handleFrequencyChange(mixed $state): void
    {
        $frequency = Frequency::parse($state);
        match (true) {
            $frequency->isDaily() => $this->handleDaily(),
            $frequency->isWeekly() => $this->handleWeekly(),
            $frequency->isMonthly() => $this->handleMonthly(),
            $frequency->isYearly() => $this->handleYearly(),
            $frequency->isCustom() => $this->handleCustom(),
            default => null,
        };
    }
    public function handleIntervalTypeChange(mixed $state): void
    {
        $intervalType = IntervalType::parse($state);
        match (true) {
            $intervalType->isWeek() => $this->handleWeeklyInterval(),
            $intervalType->isMonth() => $this->handleMonthlyInterval(),
            $intervalType->isYear() => $this->handleYearlyInterval(),
            default => null,
        };
    }
    public function handleDateChange(?string $component, mixed $state): void
    {
        match ($component) {
            'start_date' => $this->syncComponentsToStartDate(Carbon::parse($state)),
            'month' => $this->handleMonthChange($state),
            'day_of_month' => $this->handleDayOfMonthChange($state),
            'day_of_week' => $this->handleDayOfWeekChange($state),
            default => null,
        };
    }
    protected function handleDaily(): void
    {
        $this->setMany($this->set, [
            'interval_value' => null,
            'interval_type' => null,
            'day_of_month' => null,
            'start_date' => $this->today->toDateString(),
        ]);
    }
    protected function handleWeekly(): void
    {
        $currentDayOfWeek = DayOfWeek::parse($this->today->dayOfWeek);
        $this->setMany($this->set, [
            'day_of_week' => $currentDayOfWeek,
            'start_date' => $this->today->toDateString(),
            'interval_value' => null,
            'interval_type' => null,
            'day_of_month' => null,
        ]);
    }
    protected function handleMonthly(): void
    {
        $dayOfMonth = DayOfMonth::First;
        $date = $dayOfMonth->resolveDate($this->today);
        $adjustedStartDate = $date->lt($this->today)
            ? $dayOfMonth->resolveDate($date->addMonth())
            : $dayOfMonth->resolveDate($date);
        $this->setMany($this->set, [
            'month' => null,
            'day_of_month' => $dayOfMonth,
            'start_date' => $adjustedStartDate->toDateString(),
            'interval_value' => null,
            'interval_type' => null,
        ]);
    }
    protected function handleYearly(): void
    {
        $currentMonth = Month::parse($this->today->month);
        $currentDayOfMonth = DayOfMonth::parse($this->today->day);
        $this->setMany($this->set, [
            'month' => $currentMonth,
            'day_of_month' => $currentDayOfMonth,
            'start_date' => $this->today->toDateString(),
            'interval_value' => null,
            'interval_type' => null,
        ]);
    }
    protected function handleCustom(): void
    {
        $dayOfMonth = DayOfMonth::First;
        $date = $dayOfMonth->resolveDate($this->today);
        $adjustedStartDate = $date->lt($this->today)
            ? $dayOfMonth->resolveDate($date->addMonth())
            : $dayOfMonth->resolveDate($date);
        $this->setMany($this->set, [
            'interval_value' => 1,
            'interval_type' => IntervalType::Month,
            'month' => null,
            'day_of_month' => $dayOfMonth,
            'start_date' => $adjustedStartDate->toDateString(),
        ]);
    }
    protected function handleWeeklyInterval(): void
    {
        $currentDayOfWeek = DayOfWeek::parse($this->today->dayOfWeek);
        $this->setMany($this->set, [
            'day_of_week' => $currentDayOfWeek,
            'start_date' => $this->today->toDateString(),
        ]);
    }
    protected function handleMonthlyInterval(): void
    {
        $dayOfMonth = DayOfMonth::First;
        $date = $dayOfMonth->resolveDate($this->today);
        $adjustedStartDate = $date->lt($this->today)
            ? $dayOfMonth->resolveDate($date->addMonth())
            : $dayOfMonth->resolveDate($date);
        $this->setMany($this->set, [
            'month' => null,
            'day_of_month' => $dayOfMonth,
            'start_date' => $adjustedStartDate->toDateString(),
        ]);
    }
    protected function handleYearlyInterval(): void
    {
        $currentMonth = Month::parse($this->today->month);
        $currentDayOfMonth = DayOfMonth::parse($this->today->day);
        $this->setMany($this->set, [
            'month' => $currentMonth,
            'day_of_month' => $currentDayOfMonth,
            'start_date' => $this->today->toDateString(),
        ]);
    }
    protected function syncComponentsToStartDate(Carbon $startDate): void
    {
        $frequency = Frequency::parse(($this->get)('frequency'));
        $intervalType = IntervalType::parse(($this->get)('interval_type'));
        if ($frequency->isWeekly() || $intervalType?->isWeek()) {
            ($this->set)('day_of_week', DayOfWeek::parse($startDate->dayOfWeek));
        }
        if ($frequency->isMonthly() || $intervalType?->isMonth() ||
            $frequency->isYearly() || $intervalType?->isYear()) {
            ($this->set)('day_of_month', $startDate->day);
        }
        if ($frequency->isYearly() || $intervalType?->isYear()) {
            ($this->set)('month', Month::parse($startDate->month));
        }
    }
    protected function handleMonthChange(mixed $state): void
    {
        if (! $this->get) {
            return;
        }
        $dayOfMonth = DayOfMonth::parse(($this->get)('day_of_month'));
        $frequency = Frequency::parse(($this->get)('frequency'));
        $intervalType = IntervalType::parse(($this->get)('interval_type'));
        $month = Month::parse($state);
        if (($frequency->isYearly() || $intervalType?->isYear()) && $month && $dayOfMonth) {
            $date = $dayOfMonth->resolveDate($this->today->month($month->value));
            $adjustedStartDate = $date->lt($this->today)
                ? $dayOfMonth->resolveDate($date->addYear()->month($month->value))
                : $dayOfMonth->resolveDate($date->month($month->value));
            $adjustedDay = min($dayOfMonth->value, $adjustedStartDate->daysInMonth);
            $this->setMany($this->set, [
                'day_of_month' => $adjustedDay,
                'start_date' => $adjustedStartDate->toDateString(),
            ]);
        }
    }
    protected function handleDayOfMonthChange(mixed $state): void
    {
        if (! $this->get) {
            return;
        }
        $dayOfMonth = DayOfMonth::parse($state);
        $frequency = Frequency::parse(($this->get)('frequency'));
        $intervalType = IntervalType::parse(($this->get)('interval_type'));
        $month = Month::parse(($this->get)('month'));
        if (($frequency->isMonthly() || $intervalType?->isMonth()) && $dayOfMonth) {
            $date = $dayOfMonth->resolveDate($this->today);
            $adjustedStartDate = $date->lt($this->today)
                ? $dayOfMonth->resolveDate($date->addMonth())
                : $dayOfMonth->resolveDate($date);
            ($this->set)('start_date', $adjustedStartDate->toDateString());
        }
        if (($frequency->isYearly() || $intervalType?->isYear()) && $month && $dayOfMonth) {
            $date = $dayOfMonth->resolveDate($this->today->month($month->value));
            $adjustedStartDate = $date->lt($this->today)
                ? $dayOfMonth->resolveDate($date->addYear()->month($month->value))
                : $dayOfMonth->resolveDate($date->month($month->value));
            ($this->set)('start_date', $adjustedStartDate->toDateString());
        }
    }
    protected function handleDayOfWeekChange(mixed $state): void
    {
        $dayOfWeek = DayOfWeek::parse($state);
        $adjustedStartDate = $this->today->is($dayOfWeek->name)
            ? $this->today
            : $this->today->next($dayOfWeek->name);
        ($this->set)('start_date', $adjustedStartDate->toDateString());
    }
}
 |