<?php
/**
 * REST API Client Factory
 *
 * Handles creation of AI client instances based on provider configuration.
 *
 * @package AIPT\Api\Services
 * @since 2.4.1
 */

namespace AIPT\Api\Services;

use AIPT\Api\AIClientInterface;
use AIPT\Api\Chatbot\Jobs\IdempotencyKeyContext;
use AIPT\Api\Claude\ClaudeClient;
use AIPT\Api\OpenAI\OpenAIClient;
use AIPT\Api\Gemini\GeminiClient;
use AIPT\Api\OpenRouter\OpenRouterClient;
use AIPT\Utils\Credentials\CredentialResolver;

/**
 * REST API Client Factory Implementation
 *
 * Creates AI client instances with proper API key validation and model normalization.
 *
 * Since 2.6.0: API key source is the credential chain (Connectors → Local).
 * Public method signatures unchanged.
 *
 * @internal Used by RestEndpoints
 */
class RestAPIClientFactory implements AIClientFactoryInterface {

	private ?IdempotencyKeyContext $default_context;

	public function __construct( ?IdempotencyKeyContext $default_context = null ) {
		$this->default_context = $default_context;
	}

	/**
	 * Create AI client instance
	 *
	 * @param string|null $provider Provider name (openai|gemini|openrouter|claude). If null, uses configured provider.
	 * @return AIClientInterface AI client instance
	 * @throws \Exception If API key is not configured for provider
	 */
	public function create( ?string $provider = null, ?IdempotencyKeyContext $context = null ): AIClientInterface {

		$ai_gate = \AIPT\Core\Services\AiSupportGate::guard();
		if ( $ai_gate instanceof \WP_Error ) {

			$data        = $ai_gate->get_error_data();
			$status_code = ( is_array( $data ) && isset( $data['status'] ) ) ? (int) $data['status'] : 503;

			throw new \Exception(
				'[' . $ai_gate->get_error_code() . '] ' . $ai_gate->get_error_message(),
				$status_code
			);

		}
		$aipt_provider = $provider ?? get_option( 'aipt_api_provider', 'openai' );
		$context       = $context ?? $this->default_context;
		$resolver      = CredentialResolver::instance();
		$api_key       = $resolver->getKey( $aipt_provider );

		if ( ! is_string( $api_key ) || '' === $api_key ) {
			$source_label = $resolver->getSourceLabel( $aipt_provider );

			throw new \Exception(
				sprintf(
					/* translators: 1: provider id (openai/gemini/claude/openrouter), 2: source label (e.g. "Not configured"). */
					__( '%1$s API key is not configured (resolved source: %2$s). Configure it in Settings > Connectors or AI Product Tools > API.', 'ai-product-tools' ),
					$aipt_provider,
					$source_label
				)
			);

		}

		if ( $aipt_provider === 'gemini' ) {
			return new GeminiClient( $api_key );
		} elseif ( $aipt_provider === 'openrouter' ) {
			return new OpenRouterClient( $api_key );
		} elseif ( $aipt_provider === 'claude' ) {
			return new ClaudeClient( $api_key, $context );
		} else {
			return new OpenAIClient( $api_key, $context );
		}
	}

	/**
	 * Get default model for provider
	 *
	 * @param string $provider Provider name
	 * @return string Default model identifier
	 */
	public function getDefaultModel( string $provider ): string {
		switch ( $provider ) {
			case 'gemini':
				return 'gemini-2.5-flash';
			case 'openrouter':
				return 'openrouter/free';
			case 'claude':
				return 'claude-sonnet-4-6';
			default:
				return 'gpt-4.1-mini';
		}
	}

	/**
	 * Normalize model identifier for provider
	 *
	 * Validates model against provider's supported models and returns normalized identifier.
	 * If model is invalid, persists fallback to options and returns fallback.
	 *
	 * @param string $provider Provider name
	 * @param string|null $model Model identifier to normalize
	 * @return string Normalized model identifier
	 */
	public function normalizeModel( string $provider, ?string $model ): string {
		$model = $model ?? '';

		if ( $provider === 'gemini' ) {
			$valid_models = [
				'gemini-3.5-flash',
				'gemini-3.1-pro-preview',
				'gemini-3.1-flash-lite',
				'gemini-3-flash-preview',
				'gemini-2.5-pro',
				'gemini-2.5-flash',
				'gemini-2.5-flash-lite',
			];
			$fallback     = 'gemini-2.5-flash';

			if ( ! in_array( $model, $valid_models, true ) ) {
				update_option( 'aipt_model', $fallback );
				return $fallback;
			}

			return $model;
		}

		if ( $provider === 'openrouter' ) {
			if ( empty( $model ) ) {
				$fallback = 'openrouter/free';
				update_option( 'aipt_model', $fallback );
				return $fallback;
			}

			return $model;
		}

		if ( $provider === 'claude' ) {
			$valid_models = [
				'claude-opus-4-8',
				'claude-opus-4-7',
				'claude-opus-4-6',
				'claude-sonnet-4-6',
				'claude-opus-4-5-20251101',
				'claude-sonnet-4-5-20250929',
				'claude-haiku-4-5-20251001',
				'claude-opus-4-1-20250805',
			];
			$fallback     = 'claude-sonnet-4-6';

			if ( ! in_array( $model, $valid_models, true ) ) {
				update_option( 'aipt_model', $fallback );
				return $fallback;
			}

			return $model;
		}

		$valid_models = [
			'gpt-5.5',
			'gpt-5.5-pro',
			'gpt-5.4',
			'gpt-5.4-mini',
			'gpt-5.4-nano',
			'gpt-4.1',
			'gpt-4.1-mini',
			'gpt-4.1-nano',
			'o4-mini',
			'o3-pro',
			'o3',
		];
		$fallback     = 'gpt-4.1-mini';

		if ( ! in_array( $model, $valid_models, true ) ) {
			if ( $model !== $fallback ) {
				update_option( 'aipt_model', $fallback );
			}
			return $fallback;
		}

		return $model;
	}
}
