<?php
/**
 * Invoke Provider Ability
 *
 * Executes a direct AI prompt via the configured provider and returns the
 * generated text. Exposes the plugin's AI infrastructure to Abilities API
 * consumers (AI agents, MCP clients, tool-calling integrations).
 *
 * @package AIPT\Core\Abilities
 * @since   2.6.0
 */

declare(strict_types=1);

namespace AIPT\Core\Abilities;

defined( 'ABSPATH' ) || exit;

use AIPT\Api\Services\RestAPIClientFactory;
use AIPT\Core\Services\AiSupportGate;
use AIPT\Utils\ProviderNameMapper;

/**
 * Handles direct AI prompt invocation through the configured provider.
 *
 * Validates input, resolves the provider and model, instantiates the AI
 * client via RestAPIClientFactory (which sources keys through the
 * CredentialResolver chain), and returns the generated text.
 *
 * The factory can be replaced for testing via the aipt_invoke_provider_factory
 * filter, which receives the default RestAPIClientFactory instance.
 *
 * @since 2.6.0
 */
final class InvokeProviderAbility {

	/**
	 * Execute the invoke-provider ability.
	 *
	 * Validates input, applies the WP 7.0 AI support gate when available,
	 * resolves the AI client, and returns generated text with provider metadata.
	 *
	 * @since 2.6.0
	 *
	 * @param array $input {
	 *     Ability input array. All keys are optional except 'prompt'.
	 *
	 *     @type string $prompt   Required. Non-empty user prompt to send to the provider.
	 *     @type string $provider Optional. One of 'openai', 'gemini', 'claude', 'openrouter'.
	 *                            Defaults to the current aipt_api_provider option.
	 *     @type string $model    Optional. Model identifier. Defaults to the provider default.
	 * }
	 * @return array|\WP_Error {
	 *     Result array on success, WP_Error on failure.
	 *
	 *     @type string      $text             Generated response text.
	 *     @type string      $provider         Resolved provider ID.
	 *     @type string      $model            Resolved model identifier.
	 *     @type array|null  $tokenUsage       Token usage data (null if not surfaced by provider).
	 *     @type array|null  $providerMetadata Additional provider metadata (null if not surfaced).
	 * }
	 */
	public static function execute( array $input ) {

		if ( empty( $input['prompt'] ) || ! is_string( $input['prompt'] ) ) {
			return new \WP_Error(
				'aipt_missing_prompt',
				__( 'The prompt parameter is required and must be a non-empty string.', 'ai-product-tools' ),
				array( 'status' => 400 )
			);
		}

		$provider = ( isset( $input['provider'] ) && is_string( $input['provider'] ) )
			? $input['provider']
			: (string) get_option( 'aipt_api_provider', 'openai' );

		if ( ! in_array( $provider, ProviderNameMapper::aiptProviders(), true ) ) {
			return new \WP_Error(
				'aipt_invalid_provider',
				sprintf(
					/* translators: %s: provider ID supplied by caller */
					__( 'Invalid provider "%s". Must be one of: openai, gemini, claude, openrouter.', 'ai-product-tools' ),
					sanitize_text_field( $provider )
				),
				array( 'status' => 400 )
			);
		}

		$gate = AiSupportGate::guard();
		if ( $gate instanceof \WP_Error ) {
			return $gate;
		}

		$model = ( isset( $input['model'] ) && is_string( $input['model'] ) && '' !== $input['model'] )
			? $input['model']
			: '';

		/**
		 * Filter the AI client factory used by the invoke-provider ability.
		 *
		 * Third-party code and unit tests may replace the factory to inject
		 * a mock or alternative implementation.
		 *
		 * @since 2.6.0
		 *
		 * @param \AIPT\Api\Services\AIClientFactoryInterface $factory Default RestAPIClientFactory.
		 */
		$factory = apply_filters( 'aipt_invoke_provider_factory', new RestAPIClientFactory() );

		try {
			$client         = $factory->create( $provider );
			$resolved_model = '' !== $model ? $model : $factory->getDefaultModel( $provider );

			$text = $client->generate_description(
				'',
				wp_check_invalid_utf8( $input['prompt'] ),
				$resolved_model,
				null
			);
		} catch ( \Throwable $e ) {
			return new \WP_Error(
				'aipt_provider_error',
				esc_html( $e->getMessage() ),
				array( 'status' => 502 )
			);
		}

		return array(
			'text'             => $text,
			'provider'         => $provider,
			'model'            => $resolved_model,
			'tokenUsage'       => null,
			'providerMetadata' => null,
		);
	}

	/**
	 * Permission check for the invoke-provider ability.
	 *
	 * Restricts access to users with the manage_woocommerce capability
	 * because invoking AI providers can consume credits.
	 *
	 * @since 2.6.0
	 *
	 * @return bool True if the current user can manage WooCommerce, false otherwise.
	 */
	public static function permission_check(): bool {
		return current_user_can( 'manage_woocommerce' );
	}
}
