<?php

namespace Limb_Chatbot\Includes\Services\Actions;

use Limb_Chatbot\Includes\Data_Objects\Action;
use Limb_Chatbot\Includes\Data_Objects\Action_Plan;
use Limb_Chatbot\Includes\Data_Objects\Action_Plan_Step;
use Limb_Chatbot\Includes\Data_Objects\Parameter;
use Limb_Chatbot\Includes\Data_Objects\Chat;
use Limb_Chatbot\Includes\Factories\Parameter_Type_Factory;
use Limb_Chatbot\Includes\Interfaces\Parameter_Type_Interface;

/**
 * Class Action_Plan_Builder
 *
 * Builds action plans with steps for parameter collection and any
 * post-collection steps (e.g., email verification).
 *
 * Uses parameter type handlers to dynamically determine which steps
 * are needed for each parameter. This allows new parameter types with
 * custom post-collection logic to be added without modifying this class.
 *
 * @package Limb_Chatbot\Includes\Services\Actions
 * @since 1.0.0
 */
class Action_Plan_Builder {

	/**
	 * Action plan being built
	 *
	 * @var Action_Plan
	 */
	private Action_Plan $action_plan;

	/**
	 * Constructor
	 *
	 * @since 1.0.0
	 */
	public function __construct() {
		$this->action_plan = new Action_Plan();
	}

	/**
	 * Build an action plan from an action
	 *
	 * Creates data collection steps and any post-collection steps
	 * needed for the action's parameters.
	 *
	 * @param  Action  $action  The action to build a plan for
	 *
	 * @return Action_Plan|null The built action plan
	 * @since 1.0.0
	 */
	public function build( Action $action, ?Chat $chat = null ): ?Action_Plan {
		$parameters = $action->get_parameters();

		if ( ! $parameters->is_empty() ) {
			$parameters->each( function ( Parameter $parameter ) {
				// Add parameter collection step
				$this->action_plan->add_step( $this->generate_parameter_step( $parameter ) );

				// Check if parameter type has post-collection steps
				// (e.g., email verification, phone validation, etc.)
				try {
					$parameter_type = ( new Parameter_Type_Factory() )->make( $parameter->get_type() );

					if ( $parameter_type->has_post_collection_steps( $parameter ) ) {
						$this->action_plan->add_step(
							$this->generate_post_collection_step( $parameter, $parameter_type )
						);
					}
				} catch ( \Exception $e ) {
					// Parameter type not supported, skip post-collection handling
					// The parameter will be collected normally
				}
			} );
		}

		$this->action_plan->set_action_name( $action->get_name() );
		$this->action_plan->set_chat_uuid($chat->get_uuid());

		return $this->action_plan;
	}

	/**
	 * Generate a data collection step for a parameter
	 *
	 * @param  Parameter  $parameter  The parameter to create a step for
	 *
	 * @return Action_Plan_Step The parameter collection step
	 * @since 1.0.0
	 */
	private function generate_parameter_step( Parameter $parameter ): Action_Plan_Step {
		$step = new Action_Plan_Step();
		$step->set_type( Action_Plan_Step::TYPE_DATA_COLLECTION );
		$step->add_data( 'parameter', $parameter );
		$step->set_state( Action_Plan_Step::STATE_INCOMPLETE );

		return $step;
	}

	/**
	 * Generate a post-collection step for a parameter
	 *
	 * Post-collection steps are used for processing that happens after
	 * collecting a parameter value. Examples include email verification,
	 * phone number validation, data enrichment, etc.
	 *
	 * @param  Parameter  $parameter  The parameter
	 * @param  mixed  $parameter_type  The parameter type handler
	 *
	 * @return Action_Plan_Step The post-collection step
	 * @since 1.0.0
	 */
	private function generate_post_collection_step(
		Parameter $parameter,
		Parameter_Type_Interface $parameter_type
	): Action_Plan_Step {
		$step = new Action_Plan_Step();
		$step_type = $parameter_type->get_post_collection_step_type( $parameter );

		$step->set_type( $step_type );
		$step->add_data( 'parameter', $parameter );
		$step->add_data( 'parameter_type', get_class( $parameter_type ) );
		$step->set_state( Action_Plan_Step::STATE_INCOMPLETE );

		return $step;
	}
}