<?php
/**
 * Action Scheduler Schedule Tool.
 *
 * Schedule new actions using Action Scheduler with support for single,
 * recurring, and cron-based schedules via as_schedule_* functions.
 *
 * @package   Agenistrator
 * @since     1.0.0
 * @author    Alex Ruban <alex@maximum.guru>
 * @author    Maximum Software Inc <contact@maximum.software>
 * @license   GPL-3.0-only
 * @link      https://agenistrator.com/
 *
 * SPDX-FileCopyrightText: 2025-2026 Alex Ruban <alex@maximum.guru>
 * SPDX-FileCopyrightText: 2025-2026 Maximum Software Inc <contact@maximum.software>
 * SPDX-License-Identifier: GPL-3.0-only
 */

namespace Agenistrator;

// Direct access prevention
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Action_Scheduler_Schedule_Tool class.
 */
class Action_Scheduler_Schedule_Tool extends Tool {

	/**
	 * Get the tool ID.
	 *
	 * @return string The tool ID.
	 * @since     1.0.0
	 */
	public function get_id() {
		return 'schedule_action';
	}

	/**
	 * Get the tool name.
	 *
	 * @return string The tool name.
	 * @since     1.0.0
	 */
	public function get_name() {
		return __( 'Schedule Action', 'agenistrator' );
	}

	/**
	 * Get the tool description.
	 *
	 * @return string The tool description.
	 * @since     1.0.0
	 */
	public function get_description() {
		return 'Schedules a new action using Action Scheduler. Supports single, recurring, cron-based, and async actions.';
	}

	/**
	 * Get the parameters schema for this tool.
	 *
	 * @return array Parameters schema in JSON Schema format.
	 * @since     1.0.0
	 */
	public function get_parameters() {
		return array(
			'type'       => 'object',
			'properties' => array(
				'hook'     => array(
					'type'        => 'string',
					'description' => 'The hook name for the action.',
				),
				'type'     => array(
					'type'        => 'string',
					'description' => 'Type of action: "single" (one-time), "recurring" (interval-based), "cron" (cron expression), or "async" (run ASAP).',
					'enum'        => array( 'single', 'recurring', 'cron', 'async' ),
				),
				'when'     => array(
					'type'        => 'string',
					'description' => 'When to run. Unix timestamp, strtotime() string (e.g., "+1 hour"), or "now". Required for single/recurring/cron types.',
				),
				'interval' => array(
					'type'        => 'integer',
					'description' => 'Interval in seconds between runs. Required for "recurring" type.',
				),
				'cron'     => array(
					'type'        => 'string',
					'description' => 'Cron expression (e.g., "0 0 * * *" for daily at midnight). Required for "cron" type.',
				),
				'args'     => array(
					'type'        => 'array',
					'description' => 'Arguments to pass to the hook callback.',
					'items'       => array(),
				),
				'group'    => array(
					'type'        => 'string',
					'description' => 'Group to assign the action to.',
				),
				'unique'   => array(
					'type'        => 'boolean',
					'description' => 'If true, action will not be scheduled if a matching pending action exists.',
				),
				'priority' => array(
					'type'        => 'integer',
					'description' => 'Priority (0-255). Lower values run first. Default: 10.',
				),
			),
			'required'   => array( 'hook', 'type' ),
		);
	}

	/**
	 * Check if the current user can execute this tool.
	 *
	 * @return bool True if the current user has permission.
	 * @since     1.0.0
	 */
	public function current_user_can_execute() {
		return is_super_admin();
	}

	/**
	 * Check if this tool is read-only.
	 *
	 * @return bool True if the tool only reads data.
	 * @since     1.0.0
	 */
	public function is_read_only() {
		return false;
	}

	/**
	 * Execute the tool with the given arguments.
	 *
	 * @param array $args The tool arguments.
	 *
	 * @return array The tool execution result.
	 * @since     1.0.0
	 */
	protected function execute( $args ) {
		if ( ! WordPress_Utils::is_action_scheduler_available() ) {
			return $this->error_response( 'Action Scheduler is not available or not initialized.' );
		}

		$hook = trim( $args['hook'] );
		if ( ! preg_match( '/^[a-zA-Z_][a-zA-Z0-9_]*$/', $hook ) ) {
			return $this->error_response( 'Invalid hook name format. Must start with a letter or underscore, followed by letters, numbers, or underscores.' );
		}

		$type     = $args['type'];
		$cb_args  = $args['args'] ?? array();
		$group    = $args['group'] ?? '';
		$unique   = isset( $args['unique'] ) ? (bool) $args['unique'] : false;
		$priority = isset( $args['priority'] ) ? (int) $args['priority'] : 10;

		$action_id = 0;

		try {
			switch ( $type ) {
				case 'async':
					$action_id = as_enqueue_async_action( $hook, $cb_args, $group, $unique, $priority );
					break;

				case 'single':
					$timestamp = $this->parse_timestamp( $args['when'] ?? null );
					$action_id = as_schedule_single_action( $timestamp, $hook, $cb_args, $group, $unique, $priority );
					break;

				case 'recurring':
					$timestamp = $this->parse_timestamp( $args['when'] ?? null );
					if ( empty( $args['interval'] ) ) {
						return $this->error_response( 'Interval is required for recurring actions.' );
					}
					$action_id = as_schedule_recurring_action( $timestamp, (int) $args['interval'], $hook, $cb_args, $group, $unique, $priority );
					break;

				case 'cron':
					$timestamp = $this->parse_timestamp( $args['when'] ?? null );
					if ( empty( $args['cron'] ) ) {
						return $this->error_response( 'Cron expression is required for cron actions.' );
					}
					$action_id = as_schedule_cron_action( $timestamp, $args['cron'], $hook, $cb_args, $group, $unique, $priority );
					break;

				default:
					return $this->error_response( "Invalid action type: {$type}" );
			}
		} catch ( \Throwable $e ) {
			return $this->error_response( $e->getMessage() );
		}

		if ( 0 === $action_id ) {
			return $this->error_response( 'Failed to schedule action. It may already exist if unique was specified.' );
		}

		return $this->success_response(
			'Action scheduled successfully',
			array(
				'action_id' => $action_id,
				'hook'      => $hook,
				'type'      => $type,
				'group'     => $group,
			)
		);
	}

	/**
	 * Parse a timestamp from various formats.
	 *
	 * @param mixed $when The time value to parse.
	 * @return int The parsed timestamp.
	 * @throws \InvalidArgumentException If the time format is invalid.
	 */
	private function parse_timestamp( $when ) {
		if ( null === $when || '' === $when ) {
			throw new \InvalidArgumentException( 'The "when" parameter is required for this action type.' );
		}

		if ( is_numeric( $when ) ) {
			return (int) $when;
		}

		if ( 'now' === strtolower( $when ) ) {
			return time();
		}

		$timestamp = strtotime( $when );
		if ( false === $timestamp ) {
			// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Exception messages are not HTML output
			throw new \InvalidArgumentException( "Invalid time format: {$when}" );
		}

		return $timestamp;
	}
}
