<?php
/**
 * Closure Action Handler Adapter
 *
 * Wraps a closure and definition array into an ActionHandler implementation.
 * This is the bridge between array-based package definitions and the existing
 * execution pipeline (WorkflowExecutor, QueueManager, ActionQueueProcessor).
 *
 * @package Sequensy
 * @since 2.0.0
 */

declare(strict_types=1);

namespace Sequensy\Actions;

use Sequensy\Actions\Contracts\ActionHandler;

// If this file is called directly, abort.
if ( ! defined( 'WPINC' ) ) {
	die;
}

/**
 * Class ClosureActionHandler
 *
 * Adapts a closure-based action definition into the ActionHandler interface.
 * Used by PackageLoader to wrap closure handlers from package.php files into
 * objects consumable by the existing execution pipeline.
 */
final class ClosureActionHandler implements ActionHandler {

	/**
	 * The closure that executes the action logic.
	 *
	 * @var \Closure
	 */
	private \Closure $handler;

	/**
	 * The action definition array containing metadata and schema.
	 *
	 * @var array
	 */
	private array $definition;

	/**
	 * Constructor.
	 *
	 * @param array    $definition Action definition array with keys:
	 *                             type, label, description, schema, required_fields, output_fields.
	 * @param \Closure $handler    Closure that receives ($config, $context) and returns data.
	 */
	public function __construct( array $definition, \Closure $handler ) {
		$this->definition = $definition;
		$this->handler    = $handler;
	}

	/**
	 * Get action type identifier.
	 *
	 * @return string Action type (e.g., 'wordpress/send_email').
	 */
	public function get_type(): string {
		return $this->definition['type'];
	}

	/**
	 * Get action display label.
	 *
	 * @return string Human-readable label (e.g., 'Send Email').
	 */
	public function get_label(): string {
		return $this->definition['label'];
	}

	/**
	 * Get action description.
	 *
	 * @return string Action description.
	 */
	public function get_description(): string {
		return $this->definition['description'] ?? '';
	}

	/**
	 * Get action configuration schema.
	 *
	 * Applies the 'sequensy_action_schema' filter to allow extensions
	 * to modify the schema.
	 *
	 * @return array Schema defining expected config fields.
	 */
	public function get_schema(): array {
		$schema = $this->definition['schema'] ?? array();

		return apply_filters( 'sequensy_action_schema', $schema, $this->get_type() );
	}

	/**
	 * Get the integration slug this action belongs to.
	 *
	 * Not part of the ActionHandler interface, but used by MetadataController
	 * and ActionValidator via method_exists() duck-typing.
	 *
	 * @return string Integration slug (e.g., 'campaignmonitor', 'WordPress').
	 */
	public function get_integration(): string {
		return $this->definition['integration'] ?? '';
	}

	/**
	 * Get output fields produced by this action.
	 *
	 * @return array Array of output field definitions.
	 */
	public function get_output_fields(): array {
		return $this->definition['output_fields'] ?? array();
	}

	/**
	 * Execute the action with the given configuration and context.
	 *
	 * Validates required fields, then delegates to the closure handler.
	 * Wraps the closure call in a try/catch for safe error handling.
	 *
	 * @param array<string, mixed> $config  Action configuration values from workflow.
	 * @param array<string, mixed> $context Execution context from trigger and previous actions.
	 * @return array<string, mixed> Result array with 'success' boolean and 'data' or 'error'.
	 */
	public function execute( array $config, array $context ): array {
		// Validate required fields.
		$required_fields = $this->definition['required_fields'] ?? array();
		foreach ( $required_fields as $field ) {
			if ( ! isset( $config[ $field ] ) || '' === $config[ $field ] ) {
				return array(
					'success' => false,
					'error'   => 'Missing required field: ' . esc_attr( $field ),
				);
			}
		}

		// Execute the closure handler.
		try {
			$data = ( $this->handler )( $config, $context );

			return array(
				'success' => true,
				'data'    => $data,
			);
		} catch ( \Throwable $e ) {
			return array(
				'success' => false,
				'error'   => $e->getMessage(),
			);
		}
	}

	/**
	 * Get the raw definition array.
	 *
	 * Useful internally for introspection and serialization.
	 *
	 * @return array The original definition array.
	 */
	public function get_definition_array(): array {
		return $this->definition;
	}
}
