kylekatarnls/business-time

View on GitHub
src/BusinessTime/Schedule.php

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
<?php

namespace BusinessTime;

use BusinessTime\Exceptions\InvalidArgumentException;
use Carbon\CarbonImmutable;
use Carbon\CarbonInterface;
use Closure;
use ReflectionMethod;
use Spatie\OpeningHours\OpeningHours;

/**
 * Create a schedule that won't apply globally nor enabling macro on Carbon and can later be called
 * with any object.
 *
 * <autodoc generated by `composer types`>
 *
 * @method $this|null                                                          setOpeningHours(CarbonInterface $date, $openingHours)                                                                      Set the opening hours for the class/instance.
 * @method $this|null                                                          resetOpeningHours(CarbonInterface $date)                                                                                   Reset the opening hours for the class/instance.
 * @method \Spatie\OpeningHours\OpeningHours                                   getOpeningHours(CarbonInterface $date, $mode = null)                                                                       Get the opening hours of the class/instance.
 * @method mixed                                                               safeCallOnOpeningHours(CarbonInterface $date, $method, ...$arguments)                                                      Call a method on the OpeningHours of the current instance.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface|bool getCalleeAsMethod(CarbonInterface $date, $method = null)                                                                   Get a closure to be executed on OpeningHours on the current instance (or now if called globally) that should
 *                                                                                                                                                                                                        return a date, then convert it into a Carbon/sub-class instance.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      getMethodLoopOnHoliday(CarbonInterface $date)                                                                              Loop on the current instance (or now if called statically) with a given method until it's not a holiday.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      getTernaryMethod(CarbonInterface $date)                                                                                    Get a method that return current date-time if $testMethod applied on it return true,
 *                                                                                                                                                                                                        else return the result of $method called on it.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      addBusinessDays(CarbonInterface $date, $days = 1, $date = null)                                                            Add a given number of business days to the current date.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      addBusinessDay(CarbonInterface $date, $days = 1, $date = null)                                                             Add one business day to the current date.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      subBusinessDays(CarbonInterface $date, $days = 1, $date = null)                                                            Subtract a given number of business days to the current date.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      subtractBusinessDays(CarbonInterface $date, $days = 1, $date = null)                                                       Subtract a given number of business days to the current date.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      subBusinessDay(CarbonInterface $date, $days = 1, $date = null)                                                             Subtract one business day to the current date.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      subtractBusinessDay(CarbonInterface $date, $days = 1, $date = null)                                                        Subtract one business day to the current date.
 * @method int                                                                 diffInBusinessDays(CarbonInterface $date, $other = null)                                                                   Returns the difference between 2 dates in business days.
 * @method int                                                                 getBusinessDaysInMonth(CarbonInterface $date, $date = null)                                                                Get the number of business days in the current month.
 * @method array                                                               getMonthBusinessDays(CarbonInterface $date, $date = null)                                                                  Get list of date objects for each business day in the current month.
 * @method $this|null                                                          setBusinessDayChecker(CarbonInterface $date, callable $checkCallback = null)                                               Checks the date to see if it is a business day (neither a weekend day nor a holiday).
 * @method bool                                                                isBusinessDay(CarbonInterface $date)                                                                                       Checks the date to see if it is a business day (extra workday or neither a weekend day nor a holiday).
 * @method \Carbon\CarbonInterface|\Carbon\Carbon|\Carbon\CarbonImmutable      nextBusinessDay(CarbonInterface $date)                                                                                     Sets the date to the next business day (neither a weekend day nor a holiday).
 * @method \Carbon\CarbonInterface|\Carbon\Carbon|\Carbon\CarbonImmutable      currentOrNextBusinessDay(CarbonInterface $date)                                                                            Sets the date to the current or next business day (neither a weekend day nor a holiday).
 * @method \Carbon\CarbonInterface|\Carbon\Carbon|\Carbon\CarbonImmutable      previousBusinessDay(CarbonInterface $date)                                                                                 Sets the date to the previous business day (neither a weekend day nor a holiday).
 * @method \Carbon\CarbonInterface|\Carbon\Carbon|\Carbon\CarbonImmutable      currentOrPreviousBusinessDay(CarbonInterface $date)                                                                        Sets the date to the current or previous business day.
 * @method $this|null                                                          setObservedHolidaysZone(CarbonInterface $date, $zone, $self = null)                                                        Set the selected zone for observed holidays. So next observe methods will be saved and considered in this
 *                                                                                                                                                                                                        given custom zone.
 * @method string|null                                                         getObservedHolidaysZone(CarbonInterface $date)                                                                             Get the selected zone for observed holidays.
 * @method $this|null                                                          setHolidayObserveStatus(CarbonInterface $date, $holidayId, $observed, $self = null)                                        Set a holiday as observed/unobserved in the selected zone.
 * @method $this|null                                                          getObserveHolidayMethod(CarbonInterface $date, $holidayId = null, $observed = null, $self = null)                          Set a holiday as observed/unobserved in the selected zone (can take array of holidays).
 * @method $this|null                                                          observeHoliday(CarbonInterface $date, $holidayId = null, $observed = null, $self = null)                                   Set a holiday as observed in the selected zone.
 * @method $this|null                                                          unobserveHoliday(CarbonInterface $date, $holidayId = null, $observed = null, $self = null)                                 Set a holiday as not observed in the selected zone.
 * @method $this|null                                                          observeHolidays(CarbonInterface $date, $holidayId = null, $observed = null, $self = null)                                  Set a holiday as observed in the selected zone.
 * @method $this|null                                                          unobserveHolidays(CarbonInterface $date, $holidayId = null, $observed = null, $self = null)                                Set a holiday as not observed in the selected zone.
 * @method $this|null                                                          observeAllHolidays(CarbonInterface $date, $holidayId = null, $observed = null, $self = null)                               Set all holidays as observed in the selected zone.
 * @method $this|null                                                          unobserveAllHolidays(CarbonInterface $date, $holidayId = null, $observed = null, $self = null)                             Set all holidays as observed in the selected zone.
 * @method bool                                                                checkObservedHoliday(CarbonInterface $date, $holidayId = null)                                                             Check if a given holiday ID is observed in the selected zone.
 * @method bool                                                                isObservedHoliday(CarbonInterface $date, $holidayId = null, $date = null)                                                  Checks the date to see if it is a holiday observed in the selected zone.
 * @method $this|null                                                          setHolidayGetter(CarbonInterface $date, callable $holidayGetter)                                                           Set the strategy to get the holiday ID from a date object.
 * @method $this|null                                                          setExtraWorkdayGetter(CarbonInterface $date, callable $workdayGetter)                                                      Set the strategy to get the extra workday ID from a date object.
 * @method string|false                                                        getDBDayId(CarbonInterface $date, string $getDays = 'getHolidays')                                                         Get the identifier of the current holiday or false if it's not a holiday.
 * @method string|false                                                        getHolidayId(CarbonInterface $date)                                                                                        Get the identifier of the current holiday or false if it's not a holiday.
 * @method bool                                                                isHoliday(CarbonInterface $date)                                                                                           Checks the date to see if it is a holiday.
 * @method string|false                                                        getExtraWorkdayId(CarbonInterface $date)                                                                                   Get the identifier of the current special workday or false if it's not a special workday.
 * @method bool                                                                isExtraWorkday(CarbonInterface $date)                                                                                      Checks the date to see if it is a holiday.
 * @method array                                                               getHolidayNamesDictionary(CarbonInterface $date, $locale)                                                                  Get the holidays in the given language.
 * @method string|false                                                        getHolidayName(CarbonInterface $date, $date = null, $locale = null)                                                        Get the name of the current holiday (using the locale given in parameter or the current date locale)
 *                                                                                                                                                                                                        or false if it's not a holiday.
 * @method array                                                               getYearHolidays(CarbonInterface $date, $year = null, $type = null, string $getDays = 'getHolidays')                        Get the holidays dates for a given year (current year if no parameter given).
 * @method callable                                                            getYearHolidaysNextFunction(CarbonInterface $date, $year = null, $type = null, string $getDays = 'getHolidays')            Get a next() callback to call to iterate over holidays of a year.
 * @method string                                                              standardizeHolidaysRegion(CarbonInterface $date, $region)                                                                  Return a standardized region name.
 * @method array                                                               getHolidaysAvailableRegions(CarbonInterface $date)                                                                         Get the current holidays region.
 * @method mixed                                                               setHolidaysRegion(CarbonInterface $date, $region)                                                                          Set the holidays region (see src/Cmixin/Holidays for examples).
 * @method null|string                                                         getHolidaysRegion(CarbonInterface $date)                                                                                   Get the current holidays region.
 * @method array                                                               getBDDaysList(CarbonInterface $date, string $list, $region = null)                                                         Get the holidays for the current region selected.
 * @method array                                                               getHolidays(CarbonInterface $date, $region = null)                                                                         Get the holidays for the current region selected.
 * @method mixed                                                               setHolidays(CarbonInterface $date, $region, $holidays)                                                                     Set the holidays list.
 * @method array                                                               getExtraWorkdays(CarbonInterface $date, $region = null)                                                                    Get the holidays for the current region selected.
 * @method mixed                                                               setExtraWorkdays(CarbonInterface $date, $region, $holidays)                                                                Set the holidays list.
 * @method mixed                                                               resetHolidays(CarbonInterface $date)                                                                                       Reset the holidays list.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface|null pushToBDList(CarbonInterface $date, string $list, $region, $day, $dayId = null)                                            Push a day into a given list list of a region.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface|null pushHoliday(CarbonInterface $date, $region, $holiday, $holidayId = null)                                                   Push a holiday to the holidays list of a region.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface|null pushWorkday(CarbonInterface $date, $region, $workday, $workdayId = null)                                                   Push a workday to the workdays list of a region.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface|null setHolidayName(CarbonInterface $date, $holidayKey = null, $language = null, $name = null)                                  Set/change the name of holiday by ID for a given language (or a list of languages).
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface|null addHoliday(CarbonInterface $date, $region, $holiday, $holidayId = null, $name = null, $observed = null)                    Add a holiday to the holidays list of a region and optionally init its ID, name and observed state.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface|null addExtraWorkday(CarbonInterface $date, $region, $workday, $workdayId = null, $name = null)                                 Add a workday to the workdays list of a region and optionally init its ID and name.
 * @method array                                                               unpackHoliday(CarbonInterface $date, $holiday, $name = null, $observed = null)                                             Unpack a holiday array definition.
 * @method array                                                               checkHoliday(CarbonInterface $date, $holiday, $holidayId, $name = null, $observed = null)                                  Check a holiday definition and unpack it if it's an array.
 * @method mixed                                                               addHolidays(CarbonInterface $date, string $region, iterable $holidays = null, iterable $workingDays = null)                Add a holiday to the holidays list of a region and optionally init their IDs, names and observed states (if provided as array-definitions).
 * @method bool                                                                isDateTimeInstance(CarbonInterface $date, $value)                                                                          Return true if the given value is a DateTime or DateTimeInterface.
 * @method array                                                               swapDateTimeParam(CarbonInterface $date, $target, $date, $defaultValue)                                                    Store a first variable as Carbon instance into the second variable if the first one is a date.
 * @method array|null                                                          getHolidayDataById(CarbonInterface $date, string $id)                                                                      Get stored data set for the a given holiday ID.
 * @method $this|null                                                          setHolidayDataById(CarbonInterface $date, string $id, array $data)                                                         Set stored data set for the a given holiday ID.
 * @method array|null                                                          getHolidayData(CarbonInterface $date)                                                                                      Get stored data set for the current holiday or null if it's not a holiday.
 * @method $this|null                                                          setHolidayData(CarbonInterface $date, array $data)                                                                         Set stored data set for the current holiday, does nothing if it's not a holiday.
 * @method mixed                                                               setMaxIteration(CarbonInterface $date, int $maximum)                                                                       Set the maximum of loop turns to run before throwing an exception where trying to add
 *                                                                                                                                                                                                        or subtract open/closed time.
 * @method mixed                                                               getMaxIteration(CarbonInterface $date)                                                                                     Get the maximum of loop turns to run before throwing an exception where trying to add
 *                                                                                                                                                                                                        or subtract open/closed time.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      applyBusinessInterval(CarbonInterface $date, bool $inverted, bool $open, $interval = null, $unit = null, int $options = 0) Shift current time with a given interval taking into account only open time
 *                                                                                                                                                                                                        (if $open is true) or only closed time (if $open is false).
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      addBusinessInterval(CarbonInterface $date, bool $open, $interval = null, $unit = null, int $options = 0)                   Add the given interval taking into account only open time
 *                                                                                                                                                                                                        (if $open is true) or only closed time (if $open is false).
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      subBusinessInterval(CarbonInterface $date, bool $open, $interval = null, $unit = null, int $options = 0)                   Add the given interval taking into account only open time
 *                                                                                                                                                                                                        (if $open is true) or only closed time (if $open is false).
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      addOpenTime(CarbonInterface $date, $interval = null, $unit = null, int $options = 0)                                       Add the given interval taking into account only open time.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      subOpenTime(CarbonInterface $date, $interval = null, $unit = null, int $options = 0)                                       Subtract the given interval taking into account only open time.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      addClosedTime(CarbonInterface $date, $interval = null, $unit = null, int $options = 0)                                     Add the given interval taking into account only closed time.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      subClosedTime(CarbonInterface $date, $interval = null, $unit = null, int $options = 0)                                     Subtract the given interval taking into account only closed time.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      addOpenMinutes(CarbonInterface $date, int $numberOfMinutes, int $options = 0)                                              Add the given number of minutes taking into account only open time.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      subOpenMinutes(CarbonInterface $date, int $numberOfMinutes, int $options = 0)                                              Subtract the given number of minutes taking into account only open time.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      addClosedMinutes(CarbonInterface $date, int $numberOfMinutes, int $options = 0)                                            Add the given number of minutes taking into account only closed time.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      subClosedMinutes(CarbonInterface $date, int $numberOfMinutes, int $options = 0)                                            Subtract the given number of minutes taking into account only closed time.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      addOpenHours(CarbonInterface $date, int $numberOfHours, int $options = 0)                                                  Add the given number of hours taking into account only open time.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      subOpenHours(CarbonInterface $date, int $numberOfHours, int $options = 0)                                                  Subtract the given number of hours taking into account only open time.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      addClosedHours(CarbonInterface $date, int $numberOfHours, int $options = 0)                                                Add the given number of hours taking into account only closed time.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      subClosedHours(CarbonInterface $date, int $numberOfHours, int $options = 0)                                                Subtract the given number of hours taking into account only closed time.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      closedOrNextOpenExcludingHolidays(CarbonInterface $date)                                                                   Return current date-time if it's closed, else go to the next open date
 *                                                                                                                                                                                                        and time that is also not a holiday.
 *                                                                                                                                                                                                        Note than you can use the 'holidaysAreClosed' option and closedOrNextOpen().
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      closedOrNextBusinessOpen(CarbonInterface $date)                                                                            Return current date-time if it's closed, else go to the next open date
 *                                                                                                                                                                                                        and time that is also not a holiday.
 *                                                                                                                                                                                                        Note than you can use the 'holidaysAreClosed' option and closedOrNextOpen().
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      closedOrPreviousBusinessOpen(CarbonInterface $date)                                                                        Return current date-time if it's closed, else go to the previous open date
 *                                                                                                                                                                                                        and time that is also not a holiday.
 *                                                                                                                                                                                                        Note than you can use the 'holidaysAreClosed' option and closedOrPreviousOpen().
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      closedOrPreviousOpenExcludingHolidays(CarbonInterface $date)                                                               Return current date-time if it's closed, else go to the previous open date
 *                                                                                                                                                                                                        and time that is also not a holiday.
 *                                                                                                                                                                                                        Note than you can use the 'holidaysAreClosed' option and closedOrPreviousOpen().
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      closedOrNextOpen(CarbonInterface $date)                                                                                    Return current date-time if it's closed, else go to the next open date and time
 *                                                                                                                                                                                                        (holidays ignored if not set as exception and holidaysAreClosed set to false).
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      closedOrPreviousOpen(CarbonInterface $date)                                                                                Return current date-time if it's closed, else go to the previous open date and time
 *                                                                                                                                                                                                        (holidays ignored if not set as exception and holidaysAreClosed set to false).
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      currentOrNextOpenExcludingHolidays(CarbonInterface $date)                                                                  Return current date-time if it's open, else go to the next open date
 *                                                                                                                                                                                                        and time that is also not a holiday.
 *                                                                                                                                                                                                        Note than you can use the 'holidaysAreClosed' option and currentOrNextOpen().
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      currentOrNextBusinessOpen(CarbonInterface $date)                                                                           Return current date-time if it's open, else go to the next open date
 *                                                                                                                                                                                                        and time that is also not a holiday.
 *                                                                                                                                                                                                        Note than you can use the 'holidaysAreClosed' option and currentOrNextOpen().
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      currentOrPreviousBusinessOpen(CarbonInterface $date)                                                                       Return current date-time if it's open, else go to the previous open date
 *                                                                                                                                                                                                        and time that is also not a holiday.
 *                                                                                                                                                                                                        Note than you can use the 'holidaysAreClosed' option and currentOrPreviousOpen().
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      currentOrPreviousOpenExcludingHolidays(CarbonInterface $date)                                                              Return current date-time if it's open, else go to the previous open
 *                                                                                                                                                                                                        date and time that is also not a holiday.
 *                                                                                                                                                                                                        Note than you can use the 'holidaysAreClosed' option and currentOrPreviousOpen().
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      currentOrNextCloseIncludingHolidays(CarbonInterface $date)                                                                 Return current date-time if it's closed, else go to the next close date
 *                                                                                                                                                                                                        and time or next holiday if sooner.
 *                                                                                                                                                                                                        Note than you can use the 'holidaysAreClosed' option and currentOrNextClose().
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      currentOrNextBusinessClose(CarbonInterface $date)                                                                          Return current date-time if it's closed, else go to the next close date
 *                                                                                                                                                                                                        and time or next holiday if sooner.
 *                                                                                                                                                                                                        Note than you can use the 'holidaysAreClosed' option and currentOrNextClose().
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      currentOrPreviousCloseIncludingHolidays(CarbonInterface $date)                                                             Return current date-time if it's closed, else go to the previous close date
 *                                                                                                                                                                                                        and time or previous holiday if sooner.
 *                                                                                                                                                                                                        Note than you can use the 'holidaysAreClosed' option and currentOrPreviousClose().
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      currentOrPreviousBusinessClose(CarbonInterface $date)                                                                      Return current date-time if it's closed, else go to the previous close date
 *                                                                                                                                                                                                        and time or previous holiday if sooner.
 *                                                                                                                                                                                                        Note than you can use the 'holidaysAreClosed' option and currentOrPreviousClose().
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      currentOrNextOpen(CarbonInterface $date)                                                                                   Return current date-time if it's open, else go to the next open date and time
 *                                                                                                                                                                                                        (holidays ignored if not set as exception and holidaysAreClosed set to false).
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      currentOrPreviousOpen(CarbonInterface $date)                                                                               Return current date-time if it's open, else go to the previous open date and time
 *                                                                                                                                                                                                        (holidays ignored if not set as exception and holidaysAreClosed set to false).
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      currentOrNextClose(CarbonInterface $date)                                                                                  Return current date-time if it's closed, else go to the next close date and time
 *                                                                                                                                                                                                        (holidays ignored if not set as exception and holidaysAreClosed set to false).
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      currentOrPreviousClose(CarbonInterface $date)                                                                              Return current date-time if it's closed, else go to the previous close date and time
 *                                                                                                                                                                                                        (holidays ignored if not set as exception and holidaysAreClosed set to false).
 * @method \Carbon\CarbonInterval|float                                        diffInBusinessUnit(CarbonInterface $date, string $unit, $date = null, int $options = 0)                                    Return an interval/count of given unit with open/closed business time between the current date and an other
 *                                                                                                                                                                                                        given date.
 * @method \Carbon\CarbonInterval                                              diffAsBusinessInterval(CarbonInterface $date, $date = null, int $options = 0)                                              Return an interval with open/closed business time between the current date and an other
 *                                                                                                                                                                                                        given date.
 * @method float                                                               diffInBusinessSeconds(CarbonInterface $date, $date = null, int $options = 0)                                               Return a number of seconds with open/closed business time between the current date and an other
 *                                                                                                                                                                                                        given date.
 * @method float                                                               diffInBusinessMinutes(CarbonInterface $date, $date = null, int $options = 0)                                               Return a number of minutes with open/closed business time between the current date and an other
 *                                                                                                                                                                                                        given date.
 * @method float                                                               diffInBusinessHours(CarbonInterface $date, $date = null, int $options = 0)                                                 Return a number of hours with open/closed business time between the current date and an other
 *                                                                                                                                                                                                        given date.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      nextOpenExcludingHolidays(CarbonInterface $date)                                                                           Go to the next open date and time that is also not a holiday.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      nextBusinessOpen(CarbonInterface $date)                                                                                    Go to the next open date and time that is also not a holiday.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      previousOpenExcludingHolidays(CarbonInterface $date)                                                                       Go to the previous open date and time that is also not a holiday.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      previousBusinessOpen(CarbonInterface $date)                                                                                Go to the previous open date and time that is also not a holiday.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      nextCloseIncludingHolidays(CarbonInterface $date)                                                                          Go to the next close date and time or next holiday if sooner.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      nextBusinessClose(CarbonInterface $date)                                                                                   Go to the next close date and time or next holiday if sooner.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      previousCloseIncludingHolidays(CarbonInterface $date)                                                                      Go to the previous close date and time or previous holiday if sooner.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      previousBusinessClose(CarbonInterface $date)                                                                               Go to the previous close date and time or previous holiday if sooner.
 * @method bool                                                                isOpenOn($day)                                                                                                             Returns true if the business is open on a given day according to current opening hours.
 * @method bool                                                                isClosedOn($day)                                                                                                           Returns true if the business is closed on a given day according to current opening hours.
 * @method bool                                                                isOpen(CarbonInterface $date)                                                                                              Returns true if the business is open now (or current date and time) according to current opening hours.
 *                                                                                                                                                                                                        /!\ Important: it returns true if the current day is a holiday unless you set a closure handler for it in
 *                                                                                                                                                                                                        the exceptions setting.
 * @method bool                                                                isClosed(CarbonInterface $date)                                                                                            Returns true if the business is closed now (or current date and time) according to current opening hours.
 *                                                                                                                                                                                                        /!\ Important: it returns false if the current day is a holiday unless you set a closure handler for it in
 *                                                                                                                                                                                                        the exceptions setting.
 * @method bool                                                                isBusinessOpen(CarbonInterface $date)                                                                                      Returns true if the business is open and not a holiday now (or current date and time) according to current
 *                                                                                                                                                                                                        opening hours.
 * @method bool                                                                isOpenExcludingHolidays(CarbonInterface $date)                                                                             Returns true if the business is open and not a holiday now (or current date and time) according to current
 *                                                                                                                                                                                                        opening hours.
 * @method bool                                                                isBusinessClosed(CarbonInterface $date)                                                                                    Returns true if the business is closed or a holiday now (or current date and time) according to current
 *                                                                                                                                                                                                        opening hours.
 * @method bool                                                                isClosedIncludingHolidays(CarbonInterface $date)                                                                           Returns true if the business is closed or a holiday now (or current date and time) according to current
 *                                                                                                                                                                                                        opening hours.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      nextOpen(CarbonInterface $date, $method = null)                                                                            Go to the next open date and time.
 *                                                                                                                                                                                                        /!\ Important: holidays are assumed open unless you set a closure handler for it in the
 *                                                                                                                                                                                                        exceptions setting.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      nextClose(CarbonInterface $date, $method = null)                                                                           Go to the next close date and time.
 *                                                                                                                                                                                                        /!\ Important: holidays are assumed open unless you set a closure handler for it in the
 *                                                                                                                                                                                                        exceptions setting.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      previousOpen(CarbonInterface $date, $method = null)                                                                        Go to the previous open date and time.
 *                                                                                                                                                                                                        /!\ Important: holidays are assumed open unless you set a closure handler for it in the
 *                                                                                                                                                                                                        exceptions setting.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      previousClose(CarbonInterface $date, $method = null)                                                                       Go to the previous close date and time.
 *                                                                                                                                                                                                        /!\ Important: holidays are assumed open unless you set a closure handler for it in the
 *                                                                                                                                                                                                        exceptions setting.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      openOrNextCloseIncludingHolidays(CarbonInterface $date)                                                                    Return current date-time if it's open, else go to the next close date
 *                                                                                                                                                                                                        and time or next holiday if sooner.
 *                                                                                                                                                                                                        Note than you can use the 'holidaysAreClosed' option and openOrNextClose().
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      openOrNextBusinessClose(CarbonInterface $date)                                                                             Return current date-time if it's open, else go to the next close date
 *                                                                                                                                                                                                        and time or next holiday if sooner.
 *                                                                                                                                                                                                        Note than you can use the 'holidaysAreClosed' option and openOrNextClose().
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      openOrPreviousCloseIncludingHolidays(CarbonInterface $date)                                                                Return current date-time if it's open, else go to the previous close date
 *                                                                                                                                                                                                        and time or previous holiday if sooner.
 *                                                                                                                                                                                                        Note than you can use the 'holidaysAreClosed' option and openOrPreviousClose().
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      openOrPreviousBusinessClose(CarbonInterface $date)                                                                         Return current date-time if it's open, else go to the previous close date
 *                                                                                                                                                                                                        and time or previous holiday if sooner.
 *                                                                                                                                                                                                        Note than you can use the 'holidaysAreClosed' option and openOrPreviousClose().
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      openOrNextClose(CarbonInterface $date)                                                                                     Return current date-time if it's open, else go to the next close date and time
 *                                                                                                                                                                                                        (holidays ignored if not set as exception and holidaysAreClosed set to false).
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface      openOrPreviousClose(CarbonInterface $date)                                                                                 Return current date-time if it's open, else go to the previous close date and time
 *                                                                                                                                                                                                        (holidays ignored if not set as exception and holidaysAreClosed set to false).
 * @method \Spatie\OpeningHours\OpeningHoursForDay                             getCurrentDayOpeningHours(CarbonInterface $date)                                                                           Get OpeningHoursForDay instance of the current instance or class.
 * @method \Spatie\OpeningHours\TimeRange[]                                    getCurrentOpenTimeRanges(CarbonInterface $date)                                                                            Get open time ranges as array of TimeRange instances that matches the current date and time.
 * @method \Spatie\OpeningHours\TimeRange|bool                                 getCurrentOpenTimeRange(CarbonInterface $date)                                                                             Get current open time range as TimeRange instance or false if closed.
 * @method \Carbon\CarbonPeriod|bool                                           getCurrentOpenTimePeriod(CarbonInterface $date, $interval = null)                                                          Get current open time range as TimeRange instance or false if closed.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface|bool getCurrentOpenTimeRangeStart(CarbonInterface $date, $method = null)                                                        Get current open time range start as Carbon instance or false if closed.
 *                                                                                                                                                                                                        /!\ Important: it returns true if the current day is a holiday unless you set a closure handler for it in
 *                                                                                                                                                                                                        the exceptions setting.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface|bool getCurrentOpenTimeRangeEnd(CarbonInterface $date, $method = null)                                                          Get current open time range end as Carbon instance or false if closed.
 *                                                                                                                                                                                                        /!\ Important: it returns true if the current day is a holiday unless you set a closure handler for it in
 *                                                                                                                                                                                                        the exceptions setting.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface|bool getCurrentBusinessTimeRangeStart(CarbonInterface $date, $method = null)                                                    Get current open time range start as Carbon instance or false if closed or holiday.
 * @method \Carbon\Carbon|\Carbon\CarbonImmutable|\Carbon\CarbonInterface|bool getCurrentBusinessOpenTimeRangeEnd(CarbonInterface $date, $method = null)                                                  Get current open time range end as Carbon instance or false if closed.
 *</autodoc>
 */
final class Schedule
{
    /**
     * @var BusinessTimeWrapper
     */
    private $businessTime;

    /**
     * @var array
     */
    private $bindMacroContext = [];

    private function __construct(BusinessTimeWrapper $businessTime)
    {
        $this->businessTime = $businessTime;
    }

    public static function create(array $openingHours): self
    {
        return new self(BusinessTimeWrapper::create($openingHours));
    }

    /**
     * @param string|int $day
     *
     * @return string
     */
    public function normalizeDay($day): string
    {
        return $this->businessTime->normalizeDay()($day);
    }

    public function convertOpeningHours($defaultOpeningHours, $data = null): OpeningHours
    {
        return $this->businessTime->convertOpeningHours()($defaultOpeningHours, $data);
    }

    public function __call(string $name, array $arguments)
    {
        $closure = $this->businessTime->$name();

        if (!($closure instanceof Closure)) {
            throw new InvalidArgumentException(
                $name.' cannot be called on a '.self::class.'.'
            );
        }

        $arguments = array_map(function ($value) {
            return ($value instanceof CarbonInterface)
                ? $this->passBusinessTimeMethods($value)
                : $value;
        }, $arguments);
        $initialArguments = $arguments;
        $date = array_shift($arguments);

        if (!($date instanceof CarbonInterface)) {
            $arguments = $initialArguments;
            $date = $this->passBusinessTimeMethods(CarbonImmutable::now());
        }

        return $this->callInMacroContext($date, $closure, $arguments);
    }

    private function passBusinessTimeMethods(CarbonInterface $date): CarbonInterface
    {
        if (!$date->isMutable()) {
            $date = $date->copy();
        }

        return $date->settings(['macros' => $this->businessTime->getMethods()]);
    }

    private function callInMacroContext(CarbonInterface $date, Closure $closure, array $arguments)
    {
        $class = get_class($date);

        return $this->callInContext(
            $this->getBindMacroContext($class),
            $date,
            $closure->bindTo(null, $class),
            $arguments
        );
    }

    private function callInContext(
        ?ReflectionMethod $context,
        CarbonInterface $date,
        Closure $closure,
        array $arguments
    ) {
        if (!$context) {
            return $closure(...$arguments);
        }

        return $context->invoke(null, $date, static function () use ($closure, $arguments) {
            return $closure(...$arguments);
        });
    }

    private function getBindMacroContext(string $class): ?ReflectionMethod
    {
        if (array_key_exists($class, $this->bindMacroContext)) {
            return $this->bindMacroContext[$class];
        }

        $bindMacroContextMethod = $this->calculateBindMacroContext($class);
        $this->bindMacroContext[$class] = $bindMacroContextMethod;

        return $bindMacroContextMethod;
    }

    private function calculateBindMacroContext(string $class): ?ReflectionMethod
    {
        if (!method_exists($class, 'bindMacroContext')) {
            return null;
        }

        $bindMacroContextMethod = new ReflectionMethod($class, 'bindMacroContext');

        if (PHP_VERSION < 8.1) {
            $bindMacroContextMethod->setAccessible(true); // @codeCoverageIgnore
        }

        return $bindMacroContextMethod;
    }
}