<?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\OpenAI\OpenAIClient;
use AIPT\Api\Gemini\GeminiClient;
use AIPT\Api\OpenRouter\OpenRouterClient;
use AIPT\Utils\ApiKeyManager;

/**
 * Interface for AI Client Factory
 *
 * Defines contract for creating AI client instances based on provider.
 *
 * @internal Used by RestEndpoints
 */
interface AIClientFactoryInterface {
	/**
	 * 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 ): AIClientInterface;

	/**
	 * Get default model for provider
	 *
	 * @param string $provider Provider name
	 * @return string Default model identifier
	 */
	public function getDefaultModel( string $provider ): string;

	/**
	 * 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;
}

/**
 * REST API Client Factory Implementation
 *
 * Creates AI client instances with proper API key validation and model normalization.
 *
 * @internal Used by RestEndpoints
 */
class RestAPIClientFactory implements AIClientFactoryInterface {

	/**
	 * 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 ): AIClientInterface {
		$api_provider = $provider ?? get_option( 'aipt_api_provider', 'openai' );

		if ( $api_provider === 'gemini' ) {
			$api_key = ApiKeyManager::get_gemini_key();
			if ( empty( $api_key ) ) {
				throw new \Exception( esc_html__( 'Gemini API key is not set. Please set your API key in the settings.', 'ai-product-tools' ) );
			}
			return new GeminiClient( $api_key );
		} elseif ( $api_provider === 'openrouter' ) {
			$api_key = ApiKeyManager::get_openrouter_key();
			if ( empty( $api_key ) ) {
				throw new \Exception( esc_html__( 'OpenRouter API key is not set. Please set your API key in the settings.', 'ai-product-tools' ) );
			}
			return new OpenRouterClient( $api_key );
		} elseif ( $api_provider === 'claude' ) {
			$api_key = ApiKeyManager::get_claude_key();
			if ( empty( $api_key ) ) {
				throw new \Exception( esc_html__( 'Claude API key is not set. Please set your API key in the settings.', 'ai-product-tools' ) );
			}
			return new \AIPT\Api\Claude\ClaudeClient( $api_key );
		} else {
			$api_key = ApiKeyManager::get_openai_key();
			if ( empty( $api_key ) ) {
				throw new \Exception( esc_html__( 'OpenAI API key is not set. Please set your API key in the settings.', 'ai-product-tools' ) );
			}
			return new OpenAIClient( $api_key );
		}
	}

	/**
	 * 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.1-pro-preview',
				'gemini-3-flash-preview',
				'gemini-3.1-flash-lite-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-6',
				'claude-sonnet-4-6',
				'claude-opus-4-5-20251101',
				'claude-sonnet-4-5-20250929',
				'claude-haiku-4-5-20251001',
				'claude-sonnet-4-20250514',
			];
			$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.4',
			'gpt-5.4-mini',
			'gpt-5.4-nano',
			'gpt-5.2',
			'gpt-5.2-pro',
			'gpt-5-mini',
			'gpt-5-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;
	}
}
