<?php

namespace Limb_Chatbot\Includes\Services\Actions;

use Limb_Chatbot\Includes\Data_Objects\Action_Plan_Step;
use Limb_Chatbot\Includes\Data_Objects\Parameter;
use Limb_Chatbot\Includes\Interfaces\Parameter_Type_Interface;

/**
 * Class Parameter_Value_Processor
 *
 * Coordinates the validation, sanitization, and storage of parameter values
 * using the Strategy Pattern with parameter type handlers.
 *
 * @package Limb_Chatbot\Includes\Services\Actions
 * @since 1.0.0
 */
class Parameter_Value_Processor {

	/**
	 * Parameter type registry
	 *
	 * @var Parameter_Type_Registry
	 */
	private Parameter_Type_Registry $registry;

	/**
	 * Constructor
	 *
	 * @param  Parameter_Type_Registry|null  $registry  Type registry (for dependency injection)
	 *
	 * @since 1.0.0
	 */
	public function __construct( ?Parameter_Type_Registry $registry = null ) {
		$this->registry = $registry ?? Parameter_Type_Registry::instance();
	}

	/**
	 * Process and store a parameter value
	 *
	 * @param  Action_Plan_Step  $step  The step to process
	 * @param  string  $raw_value  The raw user input
	 *
	 * @return Parameter_Value_Result The processing result
	 * @since 1.0.0
	 */
	public function process( Action_Plan_Step $step, string $raw_value ): Parameter_Value_Result {
		$data      = $step->get_data();
		$parameter = $data['parameter'] ?? null;

		if ( ! $parameter instanceof Parameter ) {
			return Parameter_Value_Result::failed( __( 'Invalid parameter configuration.', 'limb-chatbot' ) );
		}

		// Get the appropriate type handler
		$type_handler = $this->get_type_handler( $parameter );

		if ( ! $type_handler ) {
			return Parameter_Value_Result::failed( sprintf( __( 'Unsupported parameter type: %s', 'limb-chatbot' ), $parameter->get_type() ?? 'unknown' ) );
		}

		// Step 1: Check if raw input is empty
		if ( empty( trim( $raw_value ) ) ) {
			// If empty and required, return required error
			if ( $this->is_required( $parameter ) ) {
				return Parameter_Value_Result::failed( sprintf( __( '%s is required.', 'limb-chatbot' ), $parameter->get_label() ?? $parameter->get_name() ) );
			}

			// If empty and not required, it's valid
			$data['value'] = null;
			$step->set_data( $data );
			$step->set_state( Action_Plan_Step::STATE_COMPLETE );

			return Parameter_Value_Result::success( '' );
		}

		// Step 2: Validate the raw input (non-empty)
		$is_valid = $type_handler->validate( $raw_value, $parameter );

		if ( ! $is_valid ) {
			return Parameter_Value_Result::failed( $type_handler->get_first_error() ?? __( 'Validation failed.', 'limb-chatbot' ), $type_handler->get_errors() );
		}

		// Step 3: Sanitize the validated input
		$sanitized_value = $type_handler->sanitize( $raw_value, $parameter );

		// Step 4: Store the validated and sanitized value
		$data['value'] = $sanitized_value;
		$step->set_data( $data );
		$step->set_state( Action_Plan_Step::STATE_COMPLETE );

		return Parameter_Value_Result::success( $sanitized_value );
	}

	/**
	 * Get the appropriate type handler for a parameter
	 *
	 * @param  Parameter  $parameter  Parameter configuration
	 *
	 * @return Parameter_Type_Interface|null Type handler or null if not found
	 * @since 1.0.0
	 */
	private function get_type_handler( Parameter $parameter ): ?Parameter_Type_Interface {
		$type = $parameter->get_type() ?? Parameter::TYPE_TEXT; // Default to text

		return $this->registry->get( $type );
	}

	/**
	 * Check if parameter is required
	 *
	 * @param  Parameter  $parameter  Parameter to check
	 *
	 * @return bool
	 * @since 1.0.0
	 */
	private function is_required( Parameter $parameter ): bool {
		$required = $parameter->get_required();

		return in_array( $required, [ 1, '1', true ] );
	}
}

