<?php
/**
 * Runs the short background wake loop used by loopback runners.
 *
 * @package Niwa_AI
 */

defined( 'ABSPATH' ) || exit;

class Niwa_AI_Admin_Autonomy_Background_Wake_Loop {
	const DEFAULT_WAIT_WINDOW_SECONDS = 360;
	const LOCAL_CLI_SCHEDULED_WAIT_WINDOW_SECONDS = 900;

	/** @var int */
	private $worker_time_limit;

	/** @var array<string,callable> */
	private $callbacks;

	public function __construct( $worker_time_limit, $callbacks = array() ) {
		$this->worker_time_limit = max( 30, intval( $worker_time_limit ) );
		$this->callbacks         = is_array( $callbacks ) ? $callbacks : array();
	}

	public function run() {
		$errors      = array();
		$wait_window = $this->get_scheduled_wait_window();
		$deadline    = time() + max( 30, min( $this->worker_time_limit - 30, $wait_window ) );

		do {
			$this->refresh_runner_state();

			$result = $this->call( 'run_timed_automation_tick', array(), array() );
			if ( ! empty( $result['error'] ) ) {
				$errors[] = (string) $result['error'];
			}
			if ( ! empty( $result['errors'] ) && is_array( $result['errors'] ) ) {
				$errors = array_merge( $errors, array_values( array_filter( $result['errors'] ) ) );
			}

			if (
				! $this->call( 'has_enabled_operative_heartbeat', array( true ), false )
				&& ! $this->call( 'has_due_scheduled_jobs_soon', array( $wait_window ), false )
			) {
				break;
			}

			if ( time() + 10 >= $deadline ) {
				break;
			}

			sleep( 10 );
		} while ( true );

		return array_slice( array_values( array_unique( $errors ) ), 0, 10 );
	}

	private function get_scheduled_wait_window() {
		if ( 'local_cli' === $this->get_current_runner_transport() ) {
			return self::LOCAL_CLI_SCHEDULED_WAIT_WINDOW_SECONDS;
		}

		return self::DEFAULT_WAIT_WINDOW_SECONDS;
	}

	private function get_current_runner_transport() {
		$telegram = $this->call( 'get_telegram', array(), null );
		if ( ! $telegram || ! method_exists( $telegram, 'get_runner_state' ) ) {
			return '';
		}

		$state = (array) $telegram->get_runner_state();
		return isset( $state['active_transport'] ) ? sanitize_key( (string) $state['active_transport'] ) : '';
	}

	private function refresh_runner_state() {
		$telegram = $this->call( 'get_telegram', array(), null );
		if ( ! $telegram || ! method_exists( $telegram, 'get_runner_state' ) || ! method_exists( $telegram, 'update_runner_state' ) ) {
			return;
		}

		$state                   = $telegram->get_runner_state();
		$state['last_seen_at']   = time();
		$state['active_until']   = max( intval( isset( $state['active_until'] ) ? $state['active_until'] : 0 ), time() + 75 );
		$telegram->update_runner_state( $state );
	}

	private function call( $name, $args = array(), $default = null ) {
		return Niwa_AI_Admin_Autonomy_Callbacks::call( $this->callbacks, $name, $args, $default );
	}
}
