<?php

namespace Limb_Chatbot\Includes\Services\Actions\Executors;

use Exception;
use Limb_Chatbot\Includes\Data_Objects\Action_Callback;
use Limb_Chatbot\Includes\Exceptions\Error_Codes;
use Limb_Chatbot\Includes\Interfaces\Action_Callback_Executor_Interface;
use Limb_Chatbot\Includes\Services\Actions\Action_Callback_Execution_Context;

/**
 * Class Abstract_Callback_Executor
 *
 * Base class for all callback executors.
 * Provides standardized response handling and template processing.
 *
 * @package Limb_Chatbot\Includes\Services\Actions\Executors
 * @since 1.0.0
 */
abstract class Abstract_Callback_Executor implements Action_Callback_Executor_Interface {

	/**
	 * Execute the callback and return a structured response
	 *
	 * @param  Action_Callback  $callback  Callback to execute
	 * @param  Action_Callback_Execution_Context  $context  Execution context
	 * @return Callback_Executor_Response  Structured response
	 * @throws Exception  On execution failure
	 */
	public function execute( Action_Callback $callback, Action_Callback_Execution_Context $context ): Callback_Executor_Response {
		// Get config (base + overrides)
		$config = $callback->get_config();

		// Process templates in config (replace variables)
		$config = $this->process_config_templates( $config, $context );

		// Validate configuration
		if ( ! $this->validate_config( $config ) ) {
			throw new Exception( sprintf( 'Invalid configuration for callback: %s', $callback->get_title() ) );
		}

		// Execute the actual callback logic
		return $this->execute_callback( $config, $context );
	}

	/**
	 * Execute the actual callback logic
	 * Must be implemented by child classes
	 *
	 * @param  array  $config  Processed configuration
	 * @param  Action_Callback_Execution_Context  $context  Execution context
	 * @return Callback_Executor_Response  Structured response
	 * @throws Exception  On execution failure
	 */
	abstract public function execute_callback( array $config, Action_Callback_Execution_Context $context ): Callback_Executor_Response;

	/**
	 * Get array value using dot notation
	 *
	 * @param  mixed  $array  Array or object to search
	 * @param  string  $path  Path in dot notation
	 * @return mixed|null
	 */
	protected function array_get( $array, string $path ) {
		$segments = explode( '.', $path );

		foreach ( $segments as $segment ) {
			if ( is_array( $array ) ) {
				if ( ! array_key_exists( $segment, $array ) ) {
					return null;
				}
				$array = $array[ $segment ];
			} elseif ( is_object( $array ) ) {
				if ( ! property_exists( $array, $segment ) ) {
					return null;
				}
				$array = $array->$segment;
			} else {
				return null;
			}
		}

		return $array;
	}

	/**
	 * Process template variables in configuration
	 *
	 * @param  array  $config  Configuration array
	 * @param  Action_Callback_Execution_Context  $context  Execution context
	 * @return array  Processed configuration
	 */
	protected function process_config_templates( array $config, Action_Callback_Execution_Context $context ): array {
		return $this->process_array_templates( $config, $context );
	}

	/**
	 * Recursively process template variables in an array
	 *
	 * @param  mixed  $value  Value to process
	 * @param  Action_Callback_Execution_Context  $context  Execution context
	 * @return mixed  Processed value
	 */
	protected function process_array_templates( $value, Action_Callback_Execution_Context $context ) {
		if ( is_array( $value ) ) {
			$result = [];
			foreach ( $value as $key => $item ) {
				$result[ $key ] = $this->process_array_templates( $item, $context );
			}

			return $result;
		}

		if ( is_string( $value ) ) {
			return $context->process_template( $value );
		}

		return $value;
	}

	/**
	 * Validate that required fields are present in config
	 *
	 * @param  array  $config  Configuration to validate
	 * @return bool
	 */
	public function validate_config( array $config ): bool {
		$required_fields = $this->get_required_fields();

		foreach ( $required_fields as $field ) {
			if ( empty( $config[ $field ] ) ) {
				throw new \Limb_Chatbot\Includes\Exceptions\Exception( Error_Codes::VALIDATION_REQUIRED,
					sprintf( __( 'Field %s is required.', 'limb-chatbot' ), $field ) );
			}
		}

		return true;
	}
}
