<?php
/**
 * Idempotent WP-Cron maintenance helpers for Niwa background hooks.
 *
 * @package Niwa_AI
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class Niwa_AI_Cron_Maintenance {

	const OPTION_HEALTH = 'niwa_ai_cron_maintenance_health';
	const OPTION_RUNNER_LOCK = 'niwa_ai_background_runner_lock';
	const OPTION_EVENT_CLAIM_PREFIX = 'niwa_ai_background_event_claim_';
	const MAX_EVENTS_PER_HOOK = 1;
	const RUNNER_LOCK_TTL = 120;
	const EVENT_CLAIM_TTL = 120;

	public static function recurring_hooks() {
		return array(
			'niwa_ai_admin_autonomy_poll_telegram'          => 'niwa_ai_thirty_seconds',
			'niwa_ai_admin_autonomy_process_scheduled_tasks' => 'niwa_ai_thirty_seconds',
			'niwa_ai_admin_autonomy_background_clock_watchdog' => 'niwa_ai_one_minute',
			'niwa_ai_admin_autonomy_background_tick'        => 'niwa_ai_one_minute',
			'niwa_ai_admin_autonomy_whatsapp_gateway_watchdog' => 'niwa_ai_one_minute',
			'niwa_ai_process_frontend_chat_queue'           => 'niwa_ai_one_minute',
			'niwa_ai_cart_recovery_tick'                    => 'niwa_ai_one_minute',
			'niwa_ai_process_memory_embeddings'             => 'niwa_ai_five_minutes',
			'niwa_ai_admin_autonomy_imap_idle_watchdog'     => 'niwa_ai_five_minutes',
			'niwa_ai_inbound_email_poll'                    => 'niwa_ai_fifteen_minutes',
			'niwa_ai_traffic_rollup'                        => 'niwa_ai_fifteen_minutes',
			'niwa_ai_cb_cleanup_expired'                    => 'hourly',
			'niwa_ai_process_lead_notifications'            => 'hourly',
			'niwa_ai_traffic_cleanup'                       => 'daily',
		);
	}

	public static function one_shot_hooks() {
		return array(
			'niwa_ai_admin_autonomy_restart_polling_runtime',
			'niwa_ai_admin_autonomy_process_scheduled_job',
			'niwa_ai_admin_autonomy_process_post_reply_memory',
		);
	}

	private static function repairable_missing_hooks() {
		return array(
			'niwa_ai_admin_autonomy_process_scheduled_tasks' => true,
			'niwa_ai_admin_autonomy_background_clock_watchdog' => true,
			'niwa_ai_admin_autonomy_background_tick'        => true,
			'niwa_ai_process_frontend_chat_queue'           => true,
			'niwa_ai_process_memory_embeddings'             => true,
			'niwa_ai_admin_autonomy_imap_idle_watchdog'     => true,
			'niwa_ai_process_lead_notifications'            => true,
			'niwa_ai_traffic_rollup'                        => true,
			'niwa_ai_traffic_cleanup'                       => true,
			'niwa_ai_cb_cleanup_expired'                    => true,
		);
	}

	public static function schedule_offsets() {
		return array(
			'niwa_ai_admin_autonomy_poll_telegram'          => 30,
			'niwa_ai_admin_autonomy_process_scheduled_tasks' => 30,
			'niwa_ai_admin_autonomy_background_clock_watchdog' => 30,
			'niwa_ai_admin_autonomy_background_tick'        => 60,
			'niwa_ai_admin_autonomy_whatsapp_gateway_watchdog' => 60,
			'niwa_ai_process_frontend_chat_queue'           => 45,
			'niwa_ai_cart_recovery_tick'                    => 60,
			'niwa_ai_process_memory_embeddings'             => 120,
			'niwa_ai_admin_autonomy_imap_idle_watchdog'     => 90,
			'niwa_ai_inbound_email_poll'                    => 300,
			'niwa_ai_traffic_rollup'                        => 300,
			'niwa_ai_cb_cleanup_expired'                    => HOUR_IN_SECONDS,
			'niwa_ai_process_lead_notifications'            => 300,
			'niwa_ai_traffic_cleanup'                       => DAY_IN_SECONDS,
		);
	}

	public static function ensure_recurring_event( $hook, $schedule, $offset = 60, $args = array() ) {
		$hook     = sanitize_key( (string) $hook );
		$schedule = sanitize_key( (string) $schedule );
		$args     = is_array( $args ) ? $args : array();
		$offset   = max( 1, intval( $offset ) );

		if ( '' === $hook || '' === $schedule || ! function_exists( 'wp_next_scheduled' ) || ! function_exists( 'wp_schedule_event' ) ) {
			return false;
		}

		$events = self::get_events( $hook, $args );
		if ( count( $events ) > self::MAX_EVENTS_PER_HOOK ) {
			self::dedupe_events( $hook, $args, $events );
			$events = self::get_events( $hook, $args );
		}

		if ( ! empty( $events ) ) {
			return true;
		}

		$scheduled = wp_schedule_event( time() + $offset, $schedule, $hook, $args );
		if ( false === $scheduled ) {
			self::record_health(
				$hook,
				array(
					'status' => 'schedule_failed',
					'action' => 'ensure_recurring_event',
				)
			);
			return false;
		}

		return true;
	}

	public static function replace_single_recurring_event( $hook, $schedule, $offset = 5, $args = array() ) {
		$hook     = sanitize_key( (string) $hook );
		$schedule = sanitize_key( (string) $schedule );
		$args     = is_array( $args ) ? $args : array();
		$offset   = max( 1, intval( $offset ) );

		if ( '' === $hook || '' === $schedule ) {
			return false;
		}

		$events = self::get_events( $hook, $args );
		$clear  = self::clear_events( $hook, $args, $events );
		if ( ! $clear['success'] ) {
			self::record_health(
				$hook,
				array(
					'status'        => 'clear_failed_before_reschedule',
					'action'        => 'replace_single_recurring_event',
					'cleared'       => $clear['cleared'],
					'failed'        => $clear['failed'],
					'existing_count' => count( $events ),
				)
			);
			return false;
		}

		return self::ensure_recurring_event( $hook, $schedule, $offset, $args );
	}

	public static function dedupe_managed_hooks() {
		$summary = array(
			'checked' => 0,
			'fixed'   => 0,
			'failed'  => 0,
		);

		foreach ( self::recurring_hooks() as $hook => $schedule ) {
			$events = self::get_events( $hook );
			$summary['checked']++;
			if ( count( $events ) <= self::MAX_EVENTS_PER_HOOK ) {
				continue;
			}
			$result = self::dedupe_events( $hook, array(), $events );
			if ( ! empty( $result['success'] ) ) {
				$summary['fixed']++;
			} else {
				$summary['failed']++;
			}
		}

		self::remove_stale_restart_events();

		return $summary;
	}

	public static function repair_managed_hooks( $source = 'unknown' ) {
		$summary = array(
			'checked' => 0,
			'ensured' => 0,
			'fixed'   => 0,
			'failed'  => 0,
		);
		$offsets = self::schedule_offsets();
		$repairable_missing = self::repairable_missing_hooks();

		foreach ( self::recurring_hooks() as $hook => $schedule ) {
			$summary['checked']++;
			$events = self::get_events( $hook );
			if ( count( $events ) > self::MAX_EVENTS_PER_HOOK ) {
				$result = self::dedupe_events( $hook, array(), $events );
				if ( ! empty( $result['success'] ) ) {
					$summary['fixed']++;
				} else {
					$summary['failed']++;
				}
			}

			if ( empty( $repairable_missing[ $hook ] ) ) {
				continue;
			}

			$offset = isset( $offsets[ $hook ] ) ? $offsets[ $hook ] : 60;
			if ( self::ensure_recurring_event( $hook, $schedule, $offset ) ) {
				$summary['ensured']++;
			} else {
				$summary['failed']++;
			}
		}

		self::remove_stale_restart_events();
		self::record_global_health(
			array(
				'last_repair_at'     => time(),
				'last_repair_source' => sanitize_key( (string) $source ),
				'last_repair'        => $summary,
			)
		);

		return $summary;
	}

	public static function run_due_managed_hooks( $limit = 8, $source = 'unknown' ) {
		$limit     = max( 1, intval( $limit ) );
		$now       = time();
		$source    = sanitize_key( (string) $source );
		if ( '' === $source ) {
			$source = 'unknown';
		}
		$processed = 0;
		$errors    = array();
		$recurring = self::recurring_hooks();
		$hooks     = array_merge( array_keys( $recurring ), self::one_shot_hooks() );
		$repair    = array();
		$lock      = self::acquire_runner_lock();
		if ( empty( $lock['success'] ) ) {
			self::record_global_health(
				array(
					'last_background_run_at' => time(),
					'last_wake_source'       => $source,
					'last_lock_skip_at'      => time(),
					'last_lock_skip_reason'  => isset( $lock['error'] ) ? (string) $lock['error'] : 'runner_already_active',
					'last_processed_count'   => 0,
					'last_errors'            => array(),
					'last_repair'            => $repair,
				)
			);
			return array(
				'success'   => true,
				'skipped'   => 'runner_already_active',
				'processed' => 0,
				'errors'    => array(),
				'source'    => $source,
				'repair'    => $repair,
			);
		}

		if ( ! empty( $lock['stale_cleared'] ) ) {
			self::record_global_health(
				array(
					'last_stale_lock_cleared_at' => time(),
				)
			);
		}

		try {
			$repair = self::repair_managed_hooks( $source );
			foreach ( self::due_events( $hooks, $now ) as $event ) {
				if ( $processed >= $limit ) {
					break;
				}

				$hook = $event['hook'];
				$args = $event['args'];
				self::record_global_health(
					array(
						'last_attempted_hook' => $hook,
						'last_attempted_at'   => time(),
					)
				);
				self::refresh_runner_lock( $lock['token'] );

				$event_schedule = isset( $event['schedule'] ) ? sanitize_key( (string) $event['schedule'] ) : '';
				if ( isset( $recurring[ $hook ] ) && '' !== $event_schedule ) {
					$schedule = $event_schedule;
					$offsets  = self::schedule_offsets();
					$offset   = isset( $offsets[ $hook ] ) ? $offsets[ $hook ] : 60;
					if ( ! self::ensure_recurring_future_event( $hook, $schedule, $offset, $args, $now ) ) {
						$errors[] = $hook . ': future_schedule_failed';
						self::record_health( $hook, array( 'status' => 'future_schedule_failed' ) );
						continue;
					}

					if ( ! self::unschedule_event_with_retry( $event['timestamp'], $hook, $args ) ) {
						$errors[] = $hook . ': unschedule_failed';
						self::record_health( $hook, array( 'status' => 'run_due_unschedule_failed' ) );
						continue;
					}

					try {
						self::refresh_runner_lock( $lock['token'] );
						do_action_ref_array( $hook, $args );
						$processed++;
						self::record_global_health(
							array(
								'last_completed_hook' => $hook,
								'last_completed_at'   => time(),
							)
						);
					} catch ( Throwable $e ) {
						$errors[] = $hook . ': ' . $e->getMessage();
						self::record_health(
							$hook,
							array(
								'status' => 'handler_exception',
								'error'  => $e->getMessage(),
							)
						);
					}

					self::ensure_recurring_event( $hook, $schedule, $offset, $args );
					continue;
				}

				$claim = self::acquire_event_claim( $event );
				if ( empty( $claim['success'] ) ) {
					self::record_health(
						$hook,
						array(
							'status'    => 'event_claim_active',
							'timestamp' => intval( $event['timestamp'] ),
						)
					);
					continue;
				}

				$release_claim = true;
				try {
					self::refresh_runner_lock( $lock['token'] );
					do_action_ref_array( $hook, $args );
					$processed++;
					self::record_global_health(
						array(
							'last_completed_hook' => $hook,
							'last_completed_at'   => time(),
						)
					);
					if ( ! self::unschedule_event_with_retry( $event['timestamp'], $hook, $args ) ) {
						$errors[] = $hook . ': unschedule_failed_after_success';
						self::record_health( $hook, array( 'status' => 'one_shot_unschedule_failed_after_success' ) );
						// Keep the claim TTL so the next wake does not immediately duplicate side effects.
						$release_claim = false;
					}
				} catch ( Throwable $e ) {
					$errors[] = $hook . ': ' . $e->getMessage();
					self::record_health(
						$hook,
						array(
							'status' => 'handler_exception',
							'error'  => $e->getMessage(),
						)
					);
				} finally {
					if ( $release_claim ) {
						self::release_event_claim( $event, $claim['token'] );
					}
				}
			}
		} finally {
			self::release_runner_lock( $lock['token'] );
		}

		self::record_global_health(
			array(
				'last_background_run_at' => time(),
				'last_wake_source'       => $source,
				'last_processed_count'   => $processed,
				'last_errors'            => array_slice( $errors, 0, 10 ),
				'last_repair'            => $repair,
			)
		);

		return array(
			'success'   => empty( $errors ),
			'processed' => $processed,
			'errors'    => $errors,
			'source'    => $source,
			'repair'    => $repair,
		);
	}

	public static function get_events( $hook, $args = array() ) {
		if ( ! function_exists( '_get_cron_array' ) ) {
			return array();
		}

		$hook   = (string) $hook;
		$args   = is_array( $args ) ? $args : array();
		$events = array();
		$cron   = _get_cron_array();

		foreach ( is_array( $cron ) ? $cron : array() as $timestamp => $hooks ) {
			if ( ! isset( $hooks[ $hook ] ) || ! is_array( $hooks[ $hook ] ) ) {
				continue;
			}
			foreach ( $hooks[ $hook ] as $key => $event ) {
				$event_args = isset( $event['args'] ) && is_array( $event['args'] ) ? $event['args'] : array();
				if ( $event_args !== $args ) {
					continue;
				}
				$events[] = array(
					'timestamp' => intval( $timestamp ),
					'key'       => (string) $key,
					'schedule'  => isset( $event['schedule'] ) ? (string) $event['schedule'] : '',
					'args'      => $event_args,
				);
			}
		}

		usort(
			$events,
			static function ( $a, $b ) {
				return intval( $a['timestamp'] ) <=> intval( $b['timestamp'] );
			}
		);

		return $events;
	}

	private static function due_events( $hooks, $now ) {
		if ( ! function_exists( '_get_cron_array' ) ) {
			return array();
		}

		$hook_map = array_fill_keys( array_map( 'strval', (array) $hooks ), true );
		$events   = array();
		$cron     = _get_cron_array();
		foreach ( is_array( $cron ) ? $cron : array() as $timestamp => $items ) {
			if ( intval( $timestamp ) > $now || ! is_array( $items ) ) {
				continue;
			}
			foreach ( $items as $hook => $hook_events ) {
				if ( empty( $hook_map[ $hook ] ) || ! is_array( $hook_events ) ) {
					continue;
				}
				foreach ( $hook_events as $event ) {
					$events[] = array(
						'timestamp' => intval( $timestamp ),
						'hook'      => (string) $hook,
						'schedule'  => isset( $event['schedule'] ) ? (string) $event['schedule'] : '',
						'args'      => isset( $event['args'] ) && is_array( $event['args'] ) ? $event['args'] : array(),
					);
				}
			}
		}

		usort(
			$events,
			static function ( $a, $b ) {
				return intval( $a['timestamp'] ) <=> intval( $b['timestamp'] );
			}
		);

		return $events;
	}

	private static function dedupe_events( $hook, $args, $events ) {
		if ( count( $events ) <= self::MAX_EVENTS_PER_HOOK ) {
			return array( 'success' => true, 'removed' => 0 );
		}

		$keep   = array_shift( $events );
		$result = self::clear_events( $hook, $args, $events );
		self::record_health(
			$hook,
			array(
				'status'       => $result['success'] ? 'deduped' : 'dedupe_failed',
				'kept_at'      => isset( $keep['timestamp'] ) ? intval( $keep['timestamp'] ) : 0,
				'removed'      => $result['cleared'],
				'failed'       => $result['failed'],
				'checked_at'   => time(),
			)
		);

		return array(
			'success' => $result['success'],
			'removed' => $result['cleared'],
			'failed'  => $result['failed'],
		);
	}

	private static function clear_events( $hook, $args, $events ) {
		$cleared = 0;
		$failed  = 0;
		foreach ( $events as $event ) {
			$event_args = isset( $event['args'] ) ? $event['args'] : $args;
			if ( ! self::unschedule_event_with_retry( intval( $event['timestamp'] ), $hook, $event_args ) ) {
				$failed++;
			} else {
				$cleared++;
			}
		}

		return array(
			'success' => 0 === $failed,
			'cleared' => $cleared,
			'failed'  => $failed,
		);
	}

	private static function remove_stale_restart_events() {
		$stale_before = time() - 10 * MINUTE_IN_SECONDS;
		foreach ( self::get_any_arg_events( 'niwa_ai_admin_autonomy_restart_polling_runtime' ) as $event ) {
			if ( intval( $event['timestamp'] ) >= $stale_before ) {
				continue;
			}
			self::unschedule_event_with_retry( intval( $event['timestamp'] ), 'niwa_ai_admin_autonomy_restart_polling_runtime', $event['args'] );
		}
	}

	private static function unschedule_event_with_retry( $timestamp, $hook, $args ) {
		$timestamp = intval( $timestamp );
		$args      = is_array( $args ) ? $args : array();

		for ( $attempt = 0; $attempt < 3; $attempt++ ) {
			$result = wp_unschedule_event( $timestamp, $hook, $args );
			if ( false !== $result && ! is_wp_error( $result ) ) {
				return true;
			}

			if ( ! self::event_exists( $timestamp, $hook, $args ) ) {
				return true;
			}

			if ( $attempt < 2 && function_exists( 'usleep' ) ) {
				usleep( 150000 * ( $attempt + 1 ) );
			}
		}

		return ! self::event_exists( $timestamp, $hook, $args );
	}

	private static function ensure_recurring_future_event( $hook, $schedule, $offset, $args, $now ) {
		$hook     = sanitize_key( (string) $hook );
		$schedule = sanitize_key( (string) $schedule );
		$args     = is_array( $args ) ? $args : array();
		$offset   = max( 1, intval( $offset ) );
		$now      = intval( $now );

		foreach ( self::get_events( $hook, $args ) as $event ) {
			if ( intval( $event['timestamp'] ) > $now ) {
				return true;
			}
		}

		if ( '' === $hook || '' === $schedule || ! function_exists( 'wp_schedule_event' ) ) {
			return false;
		}

		$scheduled = wp_schedule_event( time() + $offset, $schedule, $hook, $args );
		if ( false === $scheduled || is_wp_error( $scheduled ) ) {
			self::record_health(
				$hook,
				array(
					'status' => 'future_schedule_failed',
					'action' => 'ensure_recurring_future_event',
				)
			);
			return false;
		}

		self::record_health(
			$hook,
			array(
				'status'      => 'future_scheduled_before_handler',
				'next_run_at' => time() + $offset,
			)
		);
		return true;
	}

	private static function event_exists( $timestamp, $hook, $args ) {
		foreach ( self::get_events( $hook, $args ) as $event ) {
			if ( intval( $event['timestamp'] ) === intval( $timestamp ) ) {
				return true;
			}
		}

		return false;
	}

	private static function get_any_arg_events( $hook ) {
		if ( ! function_exists( '_get_cron_array' ) ) {
			return array();
		}

		$events = array();
		$cron   = _get_cron_array();
		foreach ( is_array( $cron ) ? $cron : array() as $timestamp => $hooks ) {
			if ( empty( $hooks[ $hook ] ) || ! is_array( $hooks[ $hook ] ) ) {
				continue;
			}
			foreach ( $hooks[ $hook ] as $event ) {
				$events[] = array(
					'timestamp' => intval( $timestamp ),
					'args'      => isset( $event['args'] ) && is_array( $event['args'] ) ? $event['args'] : array(),
				);
			}
		}

		return $events;
	}

	private static function record_health( $hook, $entry ) {
		$health = self::health();
		if ( ! isset( $health['hooks'] ) || ! is_array( $health['hooks'] ) ) {
			$health['hooks'] = array();
		}
		$health['hooks'][ $hook ] = array_merge(
			array(
				'checked_at' => time(),
			),
			is_array( $entry ) ? $entry : array()
		);
		update_option( self::OPTION_HEALTH, $health, false );
	}

	private static function record_global_health( $entry ) {
		$health = self::health();
		update_option(
			self::OPTION_HEALTH,
			array_merge( $health, is_array( $entry ) ? $entry : array() ),
			false
		);
	}

	private static function health() {
		$health = get_option( self::OPTION_HEALTH, array() );
		return is_array( $health ) ? $health : array();
	}

	private static function acquire_runner_lock() {
		$token = function_exists( 'wp_generate_uuid4' ) ? wp_generate_uuid4() : md5( uniqid( 'niwa_bg_', true ) );
		$now   = time();
		$ttl   = self::runner_lock_ttl();
		$value = array(
			'token'      => $token,
			'started_at' => $now,
			'expires_at' => $now + $ttl,
		);

		if ( add_option( self::OPTION_RUNNER_LOCK, $value, '', false ) ) {
			return array(
				'success' => true,
				'token'   => $token,
			);
		}

		$current = get_option( self::OPTION_RUNNER_LOCK, array() );
		if ( is_array( $current ) && intval( isset( $current['expires_at'] ) ? $current['expires_at'] : 0 ) > $now ) {
			return array(
				'success' => false,
				'error'   => 'runner_already_active',
			);
		}

		update_option( self::OPTION_RUNNER_LOCK, $value, false );
		return array(
			'success'       => true,
			'token'         => $token,
			'stale_cleared' => true,
		);
	}

	private static function refresh_runner_lock( $token ) {
		$current = get_option( self::OPTION_RUNNER_LOCK, array() );
		if ( ! is_array( $current ) || ! isset( $current['token'] ) || ! hash_equals( (string) $current['token'], (string) $token ) ) {
			return false;
		}

		$current['expires_at']  = time() + self::runner_lock_ttl();
		$current['refreshed_at'] = time();
		update_option( self::OPTION_RUNNER_LOCK, $current, false );
		return true;
	}

	private static function release_runner_lock( $token ) {
		$current = get_option( self::OPTION_RUNNER_LOCK, array() );
		if ( is_array( $current ) && isset( $current['token'] ) && hash_equals( (string) $current['token'], (string) $token ) ) {
			delete_option( self::OPTION_RUNNER_LOCK );
		}
	}

	private static function runner_lock_ttl() {
		$ttl = self::RUNNER_LOCK_TTL;
		if ( class_exists( 'Niwa_AI_Admin_Autonomy' ) && method_exists( 'Niwa_AI_Admin_Autonomy', 'get_turn_worker_time_limit' ) ) {
			$ttl = max( $ttl, intval( Niwa_AI_Admin_Autonomy::get_turn_worker_time_limit() ) + 180 );
		}
		if ( function_exists( 'apply_filters' ) ) {
			$ttl = apply_filters( 'niwa_ai_cron_maintenance_runner_lock_ttl', $ttl );
		}

		return max( 120, intval( $ttl ) );
	}

	private static function event_claim_option_name( $event ) {
		$hook      = isset( $event['hook'] ) ? (string) $event['hook'] : '';
		$timestamp = isset( $event['timestamp'] ) ? intval( $event['timestamp'] ) : 0;
		$args      = isset( $event['args'] ) && is_array( $event['args'] ) ? $event['args'] : array();
		return self::OPTION_EVENT_CLAIM_PREFIX . md5( $hook . '|' . $timestamp . '|' . wp_json_encode( $args ) );
	}

	private static function acquire_event_claim( $event ) {
		$option = self::event_claim_option_name( $event );
		$token  = function_exists( 'wp_generate_uuid4' ) ? wp_generate_uuid4() : md5( uniqid( 'niwa_event_', true ) );
		$now    = time();
		$ttl    = self::event_claim_ttl();
		$value  = array(
			'token'      => $token,
			'started_at' => $now,
			'expires_at' => $now + $ttl,
		);

		if ( add_option( $option, $value, '', false ) ) {
			return array( 'success' => true, 'token' => $token );
		}

		$current = get_option( $option, array() );
		if ( is_array( $current ) && intval( isset( $current['expires_at'] ) ? $current['expires_at'] : 0 ) > $now ) {
			return array( 'success' => false, 'error' => 'event_claim_active' );
		}

		update_option( $option, $value, false );
		return array( 'success' => true, 'token' => $token, 'stale_cleared' => true );
	}

	private static function release_event_claim( $event, $token ) {
		$option  = self::event_claim_option_name( $event );
		$current = get_option( $option, array() );
		if ( is_array( $current ) && isset( $current['token'] ) && hash_equals( (string) $current['token'], (string) $token ) ) {
			delete_option( $option );
		}
	}

	private static function event_claim_ttl() {
		$ttl = self::EVENT_CLAIM_TTL;
		if ( class_exists( 'Niwa_AI_Admin_Autonomy' ) && method_exists( 'Niwa_AI_Admin_Autonomy', 'get_turn_worker_time_limit' ) ) {
			$ttl = max( $ttl, intval( Niwa_AI_Admin_Autonomy::get_turn_worker_time_limit() ) + 180 );
		}
		if ( function_exists( 'apply_filters' ) ) {
			$ttl = apply_filters( 'niwa_ai_cron_maintenance_event_claim_ttl', $ttl );
		}

		return max( 120, intval( $ttl ) );
	}
}
