<?php

declare(strict_types=1);

namespace AIPT\Api\AiAvailability;

defined( 'ABSPATH' ) || exit;

use AIPT\Core\Services\WordPressRuntimeDetector;
use AIPT\Utils\Credentials\CredentialResolver;
use AIPT\Utils\ProviderNameMapper;

/**
 * REST controller for the AI availability endpoint.
 *
 * Surfaces wp_supports_ai() and per-provider text/image-generation flags so
 * that generator components can feature-detect AI availability at mount time.
 * Results are cached for 60 seconds via transient to avoid repeated lookups on
 * every page load.
 *
 * Rate limiting is intentionally omitted: this is a read-only, GET-only endpoint
 * with a 60-second server-side transient cache. A burst of concurrent requests
 * all resolve from the same cached payload, making additional rate-limit
 * transients redundant overhead.
 *
 * @since   2.6.0
 * @package AIPT\Api\AiAvailability
 */
final class AiAvailabilityController {

	/**
	 * Transient cache key for the availability payload.
	 *
	 * @since 2.6.0
	 * @var string
	 */
	private const CACHE_KEY = 'aipt_ai_availability_v1';

	/**
	 * Transient cache TTL in seconds.
	 *
	 * @since 2.6.0
	 * @var int
	 */
	private const CACHE_TTL = 60;

	/**
	 * Registers the REST route for GET aipt/v1/ai-availability.
	 *
	 * The route is registered directly via register_rest_route() because this
	 * method is invoked from within RestEndpoints::register_routes(), which is
	 * itself already bound to the rest_api_init hook in Init.php.
	 *
	 * Permission: edit_posts — broad enough for all generator UI users while
	 * keeping the endpoint non-public.
	 *
	 * 60-second transient staleness is acceptable because the only way
	 * `wp_supports_ai()` flips is via the WP_AI_SUPPORT constant (which
	 * requires a wp-config edit + new request lifecycle anyway) or via a
	 * filter callback that lives in a plugin file (whose activation /
	 * deactivation also starts a new request). A previous attempt to
	 * invalidate this cache on the `wp_supports_ai` filter itself was
	 * removed because the filter fires every time the function is read —
	 * which is multiple times per request, including inside this very
	 * controller — turning the 60s cache into a 0s one.
	 *
	 * @since 2.6.0
	 *
	 * @return void
	 */
	public function register_routes(): void {
		register_rest_route(
			'aipt/v1',
			'/ai-availability',
			array(
				'methods'             => \WP_REST_Server::READABLE,
				'callback'            => array( $this, 'get_availability' ),
				'permission_callback' => function () {
					return current_user_can( 'edit_posts' );
				},
			)
		);
	}

	/**
	 * Clears the cached AI availability payload.
	 *
	 * Call this after credential changes so a just-saved provider key is not
	 * hidden behind the short-lived availability transient.
	 *
	 * @since 2.6.0
	 *
	 * @return void
	 */
	public static function clearCache(): void {
		delete_transient( self::CACHE_KEY );
	}

	/**
	 * Handles GET aipt/v1/ai-availability.
	 *
	 * Returns a site-wide supportsAi flag and per-provider text/image-generation
	 * capability flags. On WP 7.0+ with wp_ai_client_prompt() present, delegates
	 * capability detection to the official WP AI Client Prompt Builder. On older
	 * WP (or when the function is unavailable), falls back to checking whether a
	 * credential is configured for each provider via CredentialResolver.
	 *
	 * Results are cached for 60 seconds in the aipt_ai_availability_v1 transient.
	 *
	 * @since 2.6.0
	 *
	 * @param \WP_REST_Request $request The current REST request (unused; accepted
	 *                                  for WP callback signature compatibility).
	 * @return \WP_REST_Response REST response containing availability flags.
	 */
	public function get_availability( \WP_REST_Request $request ): \WP_REST_Response { 
		$cached = get_transient( self::CACHE_KEY );

		if ( false !== $cached && is_array( $cached ) ) {
			return \rest_ensure_response( $cached );
		}

		$supports_ai = WordPressRuntimeDetector::supportsAi();

		$per_provider = array();

		foreach ( ProviderNameMapper::aiptProviders() as $provider ) {
			$text_generation  = false;
			$image_generation = false;
			$reason           = null;

			if ( WordPressRuntimeDetector::supportsAiClient() ) {
				if ( ! $supports_ai ) {
					$per_provider[ $provider ] = array(
						'textGeneration'  => false,
						'imageGeneration' => false,
					);
					continue;
				}

				try {
					$connector_id     = ProviderNameMapper::toConnectorId( $provider );
					$builder          = WordPressRuntimeDetector::getAiClientPrompt()->using_provider( $connector_id );
					$text_generation  = $supports_ai && ( (bool) $builder->is_supported_for_text_generation() || CredentialResolver::instance()->isConfigured( $provider ) );
					$image_generation = method_exists( $builder, 'is_supported_for_image_generation' )
						? (bool) $builder->is_supported_for_image_generation()
						: false;
				} catch ( \Throwable $e ) {
					$text_generation  = CredentialResolver::instance()->isConfigured( $provider );
					$image_generation = false;
					$reason           = $e->getMessage();
				}
			} else {

				$text_generation  = CredentialResolver::instance()->isConfigured( $provider );
				$image_generation = false; 
				$reason           = function_exists( 'wp_ai_client_prompt' )
					? null
					: __( 'wp_ai_client_prompt unavailable', 'ai-product-tools' );
			}

			$entry = array(
				'textGeneration'  => $text_generation,
				'imageGeneration' => $image_generation,
			);

			if ( null !== $reason ) {
				$entry['reason'] = $reason;
			}

			$per_provider[ $provider ] = $entry;
		}

		$payload = array(
			'supportsAi'  => $supports_ai,
			'perProvider' => $per_provider,
		);

		set_transient( self::CACHE_KEY, $payload, self::CACHE_TTL );

		return \rest_ensure_response( $payload );
	}
}
