<?php

/**
 * LolaCore � ActionExecutor
 *
 * @package LolaCore
 * @license GPL-2.0-or-later
 */

/**
 * Action Executor
 *
 * @package LolaCore\Actions
 */

declare(strict_types=1);

namespace LolaCore\Actions;

// phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery.SchemaChange, WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Required for core functionality, dynamic queries, and performance. Validated inputs.

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Executes actions requested by the AI with confirmation pattern.
 */
class ActionExecutor {

	/**
	 * Domain handler router. Resolves action IDs to their Domain Handler
	 * during the Phase-Out refactoring. Only active when
	 * LOLACORE_USE_DOMAIN_HANDLERS is true.
	 *
	 * @var HandlerRouter|null
	 */
	private ?HandlerRouter $router = null;

	/**
	 * Constructor. Initializes the HandlerRouter when the feature flag is active.
	 */
	public function __construct() {
		$this->router = new HandlerRouter();
	}

	/**
	 * Available actions in MVP.
	 *
	 * @var array
	 */
	private array $registry = array(
		'get_site_status'          => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'audit_plugins'            => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'get_content'              => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'get_options'              => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'get_database_status'      => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'get_users'                => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'get_discovered_abilities' => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'get_taxonomies'           => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'get_comments'             => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'get_media'                => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'update_plugin'            => array( 'requires_confirmation' => true, 'type' => 'write' ),
		'create_content'           => array( 'requires_confirmation' => true, 'type' => 'write' ),
		'optimize_database'        => array( 'requires_confirmation' => true, 'type' => 'write' ),
		'update_option'            => array( 'requires_confirmation' => true, 'type' => 'write' ),
		'purge_cache'              => array( 'requires_confirmation' => true, 'type' => 'write' ),
		'diagnose_seo'             => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'audit_security'           => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'toggle_plugin'            => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'bulk_update_plugins'      => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'edit_content'             => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'delete_content'           => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'manage_users'             => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'get_roles'                => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'manage_roles'             => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'cleanup_cron_jobs'        => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'search_plugins'           => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'get_plugin_versions'      => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'install_plugin'           => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'rollback_plugin'          => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'get_post_meta'            => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'update_post_meta'         => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'manage_taxonomies'        => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'manage_comments'          => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'upload_media'             => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'delete_media'             => array( 'requires_confirmation' => true,  'type' => 'write' ),
		// Sprint 08: Advanced Management
		'get_menus'                => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'get_site_settings'        => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'get_theme_info'           => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'export_content'           => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'manage_menus'             => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'switch_theme'             => array( 'requires_confirmation' => true,  'type' => 'write' ),
		// Theme lifecycle (v1.5.0 — parity with plugin lifecycle)
		'search_themes'            => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'install_theme'            => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'update_theme'             => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'bulk_update_themes'       => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'delete_theme'             => array( 'requires_confirmation' => true,  'type' => 'write' ),
		// WooCommerce Integration
		'wc_get_products'          => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'wc_get_product'           => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'wc_update_product'        => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'wc_get_orders'            => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'wc_get_order'             => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'wc_update_order_status'   => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'wc_get_customers'         => array( 'requires_confirmation' => false, 'type' => 'read' ),
		// Sprint 09: WooCommerce Complete
		'wc_get_categories'        => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'wc_get_coupons'           => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'wc_get_reports'           => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'wc_create_product'        => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'wc_manage_categories'     => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'wc_manage_coupons'        => array( 'requires_confirmation' => true,  'type' => 'write' ),
		// Memory
		'add_memory_fact'          => array( 'requires_confirmation' => false, 'type' => 'read' ),
		// Skills
		'add_skill'                => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'activate_skill'           => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'update_skill'             => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'list_skills'              => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'delete_skill'             => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'deactivate_skill'         => array( 'requires_confirmation' => true,  'type' => 'write' ),
		// Playbooks
		'create_playbook'          => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'list_playbooks'           => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'read_playbook'            => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'update_playbook'          => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'delete_playbook'          => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'activate_playbook'        => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'deactivate_playbook'      => array( 'requires_confirmation' => true,  'type' => 'write' ),
		// Plugin Builder
		'create_plugin'            => array(
			'requires_confirmation' => false,
			'type'                  => 'write',
			'presentation'          => 'artifact_after_write',
			'artifact'              => 'plugin_created',
		),
		'update_plugin_code'       => array(
			'requires_confirmation' => false,
			'type'                  => 'write',
			'presentation'          => 'artifact_after_write',
			'artifact'              => 'plugin_updated',
		),
		'list_built_plugins'       => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'get_built_plugin_details' => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'get_installed_plugin_state' => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'test_plugin_playground'   => array(
			'requires_confirmation' => false,
			'type'                  => 'read',
			'presentation'          => 'narrative',
			'artifact'              => 'plugin_playground',
		),
		'install_plugin_local'     => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'delete_built_plugin'      => array( 'requires_confirmation' => true,  'type' => 'write' ),
		// Block Builder
		'create_block'             => array(
			'requires_confirmation' => false,
			'type'                  => 'write',
			'presentation'          => 'artifact_after_write',
			'artifact'              => 'block_created',
		),
		'update_block_code'        => array(
			'requires_confirmation' => false,
			'type'                  => 'write',
			'presentation'          => 'artifact_after_write',
			'artifact'              => 'block_updated',
		),
		'list_built_blocks'        => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'get_built_block'          => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'get_installed_block'      => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'test_block'               => array(
			'requires_confirmation' => false,
			'type'                  => 'read',
			'presentation'          => 'narrative',
			'artifact'              => 'block_playground',
		),
		'install_block'            => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'uninstall_block'          => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'delete_built_block'       => array( 'requires_confirmation' => true,  'type' => 'write' ),
		'get_site_render_context'  => array( 'requires_confirmation' => false, 'type' => 'read' ),
		// Meta-tool: intercepted by Controller before reaching executor
		'request_full_toolset'          => array( 'requires_confirmation' => false, 'type' => 'read' ),
		'request_additional_categories' => array( 'requires_confirmation' => false, 'type' => 'read' ),
	);

	/**
	 * Returns the action registry (IDs → metadata).
	 *
	 * @return array<string, array{requires_confirmation: bool, type: string, presentation?: string, artifact?: string}>
	 */
	public function getActionRegistry(): array {
		/** This filter is also applied inside execute(). */
		return apply_filters( 'lolacore_available_actions', $this->registry );
	}

	/**
	 * Execute an action by ID.
	 *
	 * @param string $actionId One of the registered action IDs.
	 * @param array  $params   Action-specific parameters.
	 * @param bool   $dryRun   If true, return preview without executing.
	 * @param string $source   'chat' or 'rest' — for logging.
	 * @return array Result array.
	 */
	public function execute( string $actionId, array $params = array(), bool $dryRun = true, string $source = 'chat' ): array {
		// Normalize actionId: camelCase → snake_case, kebab-case → snake_case.
		// This ensures addon actions are found regardless of how the ID arrives
		// (LLM tool name, REST API input, Abilities API dispatcher).
		$actionId = self::normalizeActionId( $actionId );

		/**
		 * Filters the available actions registry.
		 */
		$registry = apply_filters( 'lolacore_available_actions', $this->registry );

		if ( ! isset( $registry[ $actionId ] ) ) {
			return $this->formatResponse( $actionId, 'denied', array(), __( 'Action not recognized or not registered.', 'lolacore' ), false );
		}

		do_action( 'lolacore_action_before', $actionId, $params, $source );

		$actionMeta = $registry[ $actionId ];
		$isWrite    = 'write' === $actionMeta['type'];

		try {
			// --- PHASE-OUT: Domain Handler routing (active when LOLACORE_USE_DOMAIN_HANDLERS = true) ---
			// When the router is active, check if a Domain Handler has claimed this action ID.
			// The external-addon filter fires FIRST to preserve addon priority (WooPro, WPCode, etc.).
			
			/**
			 * Allows external addon plugins to handle action execution.
			 * Fired before the internal Domain Handler check so addon priority is preserved.
			 *
			 * @since 1.0.1
			 * @param array|null $result   Return null to skip, or an array result to handle.
			 * @param string     $actionId The action being executed.
			 * @param array      $params   Action parameters.
			 * @param bool       $dryRun   Whether this is a preview (true) or real execution (false).
			 * @param string     $source   Execution source: 'chat' or 'rest'.
			 */
			$externalResult = apply_filters( 'lolacore_execute_external_action', null, $actionId, $params, $dryRun, $source );
			if ( null !== $externalResult ) {
				if ( is_wp_error( $externalResult ) ) {
					$response = $this->formatResponse( $actionId, 'denied', array(), $externalResult->get_error_message(), $isWrite, array() );
				} else {
					$mode = $externalResult['mode'] ?? ( $isWrite ? ( $dryRun ? 'preview' : 'executed' ) : 'executed' );
					$msg  = $externalResult['message'] ?? ( $dryRun ? __( 'Action preview generated.', 'lolacore' ) : __( 'Action executed successfully.', 'lolacore' ) );
					
					$resultData = $externalResult;
					if ( is_array( $externalResult ) && ( isset( $externalResult['mode'] ) || isset( $externalResult['message'] ) ) ) {
						$resultData = $externalResult['data'] ?? $externalResult['result'] ?? array();
					}
					
					$reqConf  = $externalResult['requires_confirmation'] ?? ( $isWrite && ! $dryRun );
					$warnings = $externalResult['warnings'] ?? array();

					$response = $this->formatResponse( $actionId, $mode, (array) $resultData, $msg, (bool) $reqConf, (array) $warnings );
				}

				if ( ! $dryRun && $isWrite && 'denied' !== $response['mode'] ) {
					$logger = new ActionLogger();
					$logger->log( $actionId, "Executed $actionId (external)", 'success', $source, $params );
				}
				do_action( 'lolacore_action_after', $actionId, $response, 'denied' === $response['mode'] ? 'failed' : 'success', $source );
				return $response;
			}

			// Check if a Domain Handler owns this action.
			$handler = $this->router->resolve( $actionId );
			if ( null !== $handler ) {
				// Expose the execution surface ('chat' | 'rest') to source-aware
				// handlers (Plugin Builder gates its literal-code path to MCP/REST)
				// without polluting the params used for logging below.
				$handlerParams                = $params;
				$handlerParams['__lc_source'] = $source;
				$resultData = $handler->handle( $actionId, $handlerParams, $dryRun );
				$mode       = $isWrite ? ( $dryRun ? 'preview' : 'executed' ) : 'executed';
				$msg        = $dryRun ? __( 'Action preview generated.', 'lolacore' ) : __( 'Action executed successfully.', 'lolacore' );
				$response   = $this->formatResponse( $actionId, $mode, $resultData, $msg, $isWrite && ! $dryRun );
				if ( ! $dryRun && $isWrite ) {
					$logger = new ActionLogger();
					$logger->log( $actionId, "Executed $actionId (handler)", 'success', $source, $params );
				}
				do_action( 'lolacore_action_after', $actionId, $response, 'success', $source );
				return $response;
			}

			throw new \Exception( __( 'Action implementation not found.', 'lolacore' ) );

		} catch ( \Exception $e ) {
			$response = $this->formatResponse( $actionId, 'denied', array(), $e->getMessage(), $isWrite, array( __( 'Action blocked due to internal exception.', 'lolacore' ) ) );
			do_action( 'lolacore_action_after', $actionId, $response, 'failed', $source );
			return $response;
		}
	}

	// --- FORMATTER ---

	private function formatResponse( string $actionId, string $mode, array $result, string $message, bool $requiresConfirmation, array $warnings = array() ): array {
		$response = array(
			'action_id'             => $actionId,
			'mode'                  => $mode,
			'result'                => (object) $result,
			'message'               => $message,
			'requires_confirmation' => $requiresConfirmation,
			'warnings'              => $warnings,
		);

		/**
		 * Filters an action response before it is returned to the chat loop or REST caller.
		 *
		 * @param array  $response  Formatted action response.
		 * @param string $actionId  Normalized action identifier.
		 */
		return apply_filters( 'lolacore_action_response', $response, $actionId );
	}

	private function camelCase( string $str ): string {
		$str = str_replace( '_', ' ', $str );
		$str = ucwords( $str );
		$str = str_replace( ' ', '', $str );
		return lcfirst( $str );
	}

	/**
	 * Normalizes an action ID to snake_case.
	 *
	 * Handles all incoming formats:
	 * - snake_case (no-op):   wc_pro_catalog_audit → wc_pro_catalog_audit
	 * - camelCase:            wcProCatalogAudit    → wc_pro_catalog_audit
	 * - kebab-case:           wc-pro-catalog-audit → wc_pro_catalog_audit
	 *
	 * @param string $actionId Raw action identifier.
	 * @return string Normalized snake_case action ID.
	 */
	public static function normalizeActionId( string $actionId ): string {
		// Already snake_case — fast path
		if ( strpos( $actionId, '_' ) !== false && strtolower( $actionId ) === $actionId ) {
			return $actionId;
		}

		// kebab-case → snake_case
		if ( strpos( $actionId, '-' ) !== false ) {
			return str_replace( '-', '_', strtolower( $actionId ) );
		}

		// camelCase → snake_case (e.g., wcProCatalogAudit → wc_pro_catalog_audit)
		$snake = preg_replace( '/([a-z0-9])([A-Z])/', '$1_$2', $actionId );
		return strtolower( $snake );
	}

	/**
	 * Legacy fallback for unit tests and old integrations.
	 */
	public function __call( string $name, array $arguments ) {
		$actionId = strtolower( preg_replace( '/([a-z0-9])([A-Z])/', '$1_$2', $name ) );
		$params   = $arguments[0] ?? array();
		$dryRun   = $arguments[1] ?? true;
		$response = $this->execute( $actionId, $params, $dryRun, 'legacy_call' );
		if ( 'denied' === $response['mode'] ) {
			throw new \Exception( esc_html( $response['message'] ) );
		}
		return (array) $response['result'];
	}

}
