<?php
/**
 * Tools registry and dispatcher for function calling.
 *
 * @package AgenticWP
 */

namespace Agentic_WP;

use Agentic_WP\Error_Handler;

defined( 'ABSPATH' ) || exit;

/**
 * Tools registry and dispatcher.
 *
 * Central registry for function calling tools and their execution.
 *
 * @since 1.0.0
 */
class Tools {
	/**
	 * Registry of available tool instances.
	 *
	 * Central tool registry: name => [ 'class' => FQCN, 'group' => 'post_creator'|'delegation' ]
	 *
	 * @since 1.0.0
	 *
	 * @var array|null
	 */
	private static ?array $registry = null;

	/**
	 * Returns all registered tools.
	 *
	 * @since 1.0.0
	 *
	 * @return array Tool registry with class names and groups.
	 */
	private static function get_registry(): array {
		if ( null !== self::$registry ) {
			return self::$registry;
		}

		if ( ! class_exists( __NAMESPACE__ . '\\Block_Catalog' ) ) {
			require_once plugin_dir_path( __DIR__ ) . 'includes/class-block-catalog.php';
		}
		if ( ! class_exists( __NAMESPACE__ . '\\Tool_List_Blocks' ) ) {
			require_once plugin_dir_path( __DIR__ ) . 'includes/tools/class-tool-list-blocks.php';
		}
		if ( ! class_exists( __NAMESPACE__ . '\\Tool_Get_Block_Info' ) ) {
			require_once plugin_dir_path( __DIR__ ) . 'includes/tools/class-tool-get-block-info.php';
		}
		if ( ! class_exists( __NAMESPACE__ . '\\Tool_Start_Post' ) ) {
			require_once plugin_dir_path( __DIR__ ) . 'includes/tools/class-tool-start-post.php';
		}
		if ( ! class_exists( __NAMESPACE__ . '\\Tool_Append_Blocks_Structured' ) ) {
			require_once plugin_dir_path( __DIR__ ) . 'includes/tools/class-tool-append-blocks-structured.php';
		}
		if ( ! class_exists( __NAMESPACE__ . '\\Tool_Append_Content' ) ) {
			require_once plugin_dir_path( __DIR__ ) . 'includes/tools/class-tool-append-content.php';
		}
		if ( ! class_exists( __NAMESPACE__ . '\\Tool_Finalize_Post' ) ) {
			require_once plugin_dir_path( __DIR__ ) . 'includes/tools/class-tool-finalize-post.php';
		}
		if ( ! class_exists( __NAMESPACE__ . '\\Tool_Delegate_Post_Agent' ) ) {
			require_once plugin_dir_path( __DIR__ ) . 'includes/tools/class-tool-delegate-post-agent.php';
		}
		if ( ! class_exists( __NAMESPACE__ . '\\Tool_Read_Post' ) ) {
			require_once plugin_dir_path( __DIR__ ) . 'includes/tools/class-tool-read-post.php';
		}
		if ( ! class_exists( __NAMESPACE__ . '\\Tool_Get_Post_Blocks_Segment' ) ) {
			require_once plugin_dir_path( __DIR__ ) . 'includes/tools/class-tool-get-post-blocks-segment.php';
		}
		if ( ! class_exists( __NAMESPACE__ . '\\Tool_Replace_Blocks_Segment' ) ) {
			require_once plugin_dir_path( __DIR__ ) . 'includes/tools/class-tool-replace-blocks-segment.php';
		}
		if ( ! class_exists( __NAMESPACE__ . '\\Tool_Search_Content' ) ) {
			require_once plugin_dir_path( __DIR__ ) . 'includes/tools/class-tool-search-content.php';
		}
		if ( ! class_exists( __NAMESPACE__ . '\\Tool_Save_Meta_Description' ) ) {
			require_once plugin_dir_path( __DIR__ ) . 'includes/tools/class-tool-save-meta-description.php';
		}

		self::$registry = array(
			'list_blocks'              => array(
				'class' => __NAMESPACE__ . '\\Tool_List_Blocks',
				'group' => 'post_creator',
			),
			'get_block_info'           => array(
				'class' => __NAMESPACE__ . '\\Tool_Get_Block_Info',
				'group' => 'post_creator',
			),
			'start_post'               => array(
				'class' => __NAMESPACE__ . '\\Tool_Start_Post',
				'group' => 'post_creator',
			),
			'append_blocks_structured' => array(
				'class' => __NAMESPACE__ . '\\Tool_Append_Blocks_Structured',
				'group' => 'post_creator',
			),
			'append_content'           => array(
				'class' => __NAMESPACE__ . '\\Tool_Append_Content',
				'group' => 'post_creator',
			),
			'finalize_post'            => array(
				'class' => __NAMESPACE__ . '\\Tool_Finalize_Post',
				'group' => array( 'post_creator', 'editor', 'interlinking' ),
			),
			'delegate_post_agent'      => array(
				'class' => __NAMESPACE__ . '\\Tool_Delegate_Post_Agent',
				'group' => 'delegation',
			),
			'read_post'                => array(
				'class' => __NAMESPACE__ . '\\Tool_Read_Post',
				'group' => array( 'delegation', 'editor', 'interlinking', 'meta_description' ),
			),
			'get_post_blocks_segment'  => array(
				'class' => __NAMESPACE__ . '\\Tool_Get_Post_Blocks_Segment',
				'group' => array( 'editor', 'interlinking' ),
			),
			'replace_blocks_segment'   => array(
				'class' => __NAMESPACE__ . '\\Tool_Replace_Blocks_Segment',
				'group' => array( 'editor', 'interlinking' ),
			),
			'search_content'           => array(
				'class' => __NAMESPACE__ . '\\Tool_Search_Content',
				'group' => array( 'delegation', 'interlinking' ),
			),
			'save_meta_description'    => array(
				'class' => __NAMESPACE__ . '\\Tool_Save_Meta_Description',
				'group' => 'meta_description',
			),
		);

		return self::$registry;
	}

	/**
	 * Gets schemas for specific tools.
	 *
	 * @since 1.0.0
	 *
	 * @param array $names Tool names to retrieve schemas for.
	 *
	 * @return array Array of tool schemas.
	 */
	private static function schemas_for( array $names ): array {
		$reg     = self::get_registry();
		$schemas = array();
		foreach ( $names as $name ) {
			if ( isset( $reg[ $name ]['class'] ) ) {
				$class = (string) $reg[ $name ]['class'];
				if ( is_string( $class ) && class_exists( $class ) && method_exists( $class, 'schema' ) ) {
					$schemas[] = $class::schema();
				}
			}
		}
		return $schemas;
	}

	/**
	 * Returns delegation-specific tools.
	 *
	 * @since 1.0.0
	 *
	 * @return array Delegation tool schemas.
	 */
	public static function get_delegation_tools(): array {
		return self::schemas_for( array( 'delegate_post_agent', 'read_post', 'search_content' ) );
	}

	/**
	 * Returns post creation tools.
	 *
	 * @since 1.0.0
	 *
	 * @return array Post creator tool schemas.
	 */
	public static function get_post_creator_tools(): array {
		$names = array(
			'list_blocks',
			'get_block_info',
			'start_post',
			'append_blocks_structured',
			'append_content',
			'finalize_post',
		);
		return self::schemas_for( $names );
	}

	/**
	 * Get editor tools for post/page editing workflows.
	 *
	 * @return array
	 */
	public static function get_editor_tools(): array {
		$tool_names = array(
			'read_post',
			'list_blocks',
			'get_block_info',
			'get_post_blocks_segment',
			'replace_blocks_segment',
		);

		return self::schemas_for( $tool_names );
	}

	/**
	 * Get interlinking tools for content search and editing.
	 *
	 * @return array
	 */
	public static function get_interlinking_tools(): array {
		return self::schemas_for( array( 'read_post', 'search_content', 'get_post_blocks_segment', 'replace_blocks_segment' ) );
	}

	/**
	 * Get meta description tools for SEO metadata management.
	 *
	 * @return array
	 */
	public static function get_meta_description_tools(): array {
		return self::schemas_for( array( 'read_post', 'save_meta_description' ) );
	}

	/**
	 * Executes a tool by name.
	 *
	 * @since 1.0.0
	 *
	 * @param string $name Tool name to execute.
	 * @param array  $args Tool arguments.
	 *
	 * @return string Tool execution result or error message.
	 */
	public static function dispatch( string $name, array $args ): string {
		// Ensure all tool classes are loaded.
		self::get_registry();

		/**
		 * Fires before a tool is executed.
		 *
		 * Allows developers to intercept tool execution for logging, validation,
		 * or modification of tool arguments before execution. Hook into this action
		 * to track tool usage, enforce permissions, or modify behavior.
		 *
		 * @hook agentic_wp_before_tool_execution
		 * @since 1.0.0
		 *
		 * @param string $name Tool name being executed.
		 * @param array  $args Tool arguments array.
		 */
		do_action( 'agentic_wp_before_tool_execution', $name, $args );

		$result = '';
		try {
			switch ( $name ) {
				case 'list_blocks':
					$result = Tool_List_Blocks::run( $args );
					break;
				case 'get_block_info':
					$result = Tool_Get_Block_Info::run( $args );
					break;
				case 'start_post':
					$result = Tool_Start_Post::run( $args );
					break;
				case 'append_blocks_structured':
					$result = Tool_Append_Blocks_Structured::run( $args );
					break;
				case 'append_content':
					$result = Tool_Append_Content::run( $args );
					break;
				case 'finalize_post':
					$result = Tool_Finalize_Post::run( $args );
					break;
				case 'delegate_post_agent':
					$result = Tool_Delegate_Post_Agent::run( $args );
					break;
				case 'read_post':
					$result = Tool_Read_Post::run( $args );
					break;
				case 'get_post_blocks_segment':
					$result = Tool_Get_Post_Blocks_Segment::run( $args );
					break;
				case 'replace_blocks_segment':
					$result = Tool_Replace_Blocks_Segment::run( $args );
					break;
				case 'search_content':
					$result = Tool_Search_Content::run( $args );
					break;
				case 'save_meta_description':
					$result = Tool_Save_Meta_Description::run( $args );
					break;
				default:
					$result = 'ERROR: unsupported_or_failed_tool';
			}
		} catch ( \Throwable $e ) {
			/**
			 * Fires when a tool execution encounters an error.
			 *
			 * Allows developers to handle, log, or report tool execution errors.
			 * This hook fires before the error is returned to the caller.
			 *
			 * @hook agentic_wp_tool_execution_error
			 * @since 1.0.0
			 *
			 * @param string     $name      Tool name that failed.
			 * @param array      $args      Tool arguments that were provided.
			 * @param \Throwable $exception The exception that was thrown.
			 */
			do_action( 'agentic_wp_tool_execution_error', $name, $args, $e );

			Error_Handler::log_error(
				'tool_execution',
				'Tool execution failed: ' . $e->getMessage(),
				array(
					'tool'  => $name,
					'args'  => $args,
					'trace' => $e->getTraceAsString(),
				)
			);
			$result = 'ERROR: tool_execution_failed';
		}

		/**
		 * Fires after a tool has been executed.
		 *
		 * Allows developers to intercept tool execution results for logging,
		 * auditing, or post-processing. Hook into this action to track results,
		 * trigger follow-up actions, or modify behavior based on outcomes.
		 *
		 * @hook agentic_wp_after_tool_execution
		 * @since 1.0.0
		 *
		 * @param string $name   Tool name that was executed.
		 * @param array  $args   Tool arguments that were provided.
		 * @param string $result Tool execution result or error message.
		 */
		do_action( 'agentic_wp_after_tool_execution', $name, $args, $result );

		return $result;
	}
}
