settings = $settings; $this->runner = $runner; $this->progress = $progress; } public function register(): void { \add_action( self::HOOK, [ $this, 'run' ] ); \add_action( 'action_scheduler_init', [ $this, 'ensureScheduled' ] ); } public function ensureScheduled(): void { if ( ! \function_exists( 'as_has_scheduled_action' ) || ! \function_exists( 'as_schedule_recurring_action' ) ) { return; } if ( ! $this->isEnabled() ) { self::unschedule(); return; } if ( \as_has_scheduled_action( self::HOOK, [], ProductHooks::ACTION_GROUP ) ) { return; } \as_schedule_recurring_action( self::nextRunAt( time(), $this->offset(), \wp_timezone() ), DAY_IN_SECONDS, self::HOOK, [], ProductHooks::ACTION_GROUP ); } public function run(): void { if ( ! $this->isEnabled() || $this->progress->isRunning() ) { return; } $this->runner->schedule(); } public static function unschedule(): void { if ( \function_exists( 'as_unschedule_all_actions' ) ) { \as_unschedule_all_actions( self::HOOK, [], ProductHooks::ACTION_GROUP ); } } public static function nextRunAt( int $now, int $offsetSeconds, DateTimeZone $timezone ): int { $runAt = ( new DateTimeImmutable( '@' . $now ) ) ->setTimezone( $timezone ) ->setTime( 0, 0 ) ->modify( '+' . $offsetSeconds . ' seconds' ); if ( $runAt->getTimestamp() <= $now ) { $runAt = $runAt->modify( '+1 day' ); } return $runAt->getTimestamp(); } private function isEnabled(): bool { return $this->settings->hasApiKey() && $this->settings->hasSelectedAgents(); } private function offset(): int { $stored = (int) \get_option( self::OFFSET_OPTION, 0 ); if ( $stored >= self::MIN_OFFSET_SECONDS && $stored < self::MAX_OFFSET_SECONDS ) { return $stored; } $offset = random_int( self::MIN_OFFSET_SECONDS, self::MAX_OFFSET_SECONDS - 1 ); \update_option( self::OFFSET_OPTION, $offset, false ); return $offset; } }