<?php
/**
 * AI Model Map
 *
 * Normalizes model metadata and capabilities across providers.
 *
 * @package SmartyPress_AI_Engine
 * @since 1.1.0
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

/**
 * Model map class.
 */
class SmartyPress_AI_Model_Map {

	const OPENROUTER_MODELS_URL = 'https://openrouter.ai/api/v1/models';
	const OPENROUTER_TEXT_TRANSIENT = 'smartypress_ai_openrouter_models_text';
	const OPENROUTER_IMAGE_TRANSIENT = 'smartypress_ai_openrouter_models_image';
	const OPENROUTER_CACHE_TTL = 12 * HOUR_IN_SECONDS;

	/**
	 * Get text models for provider.
	 *
	 * @param string $provider Provider ID.
	 * @return array
	 */
	public static function get_text_models( $provider ) {
		switch ( $provider ) {
			case 'openrouter':
				return self::get_openrouter_models( 'text' );
			case 'openai':
				return self::get_openai_text_models();
			case 'deepseek':
				return self::get_deepseek_text_models();
			case 'anthropic':
				return self::get_anthropic_text_models();
			case 'yandex':
				return class_exists( 'SmartyPress_AI_Yandex_Models' ) ? SmartyPress_AI_Yandex_Models::get_model_descriptions() : array();
			case 'gigachat':
				return class_exists( 'SmartyPress_AI_GigaChat_Models' ) ? SmartyPress_AI_GigaChat_Models::get_model_descriptions() : array();
			default:
				return array();
		}
	}

	/**
	 * Get image models for provider.
	 *
	 * @param string $provider Provider ID.
	 * @return array
	 */
	public static function get_image_models( $provider ) {
		switch ( $provider ) {
			case 'openrouter':
				return self::get_openrouter_models( 'image' );
			case 'openai':
				return self::get_openai_image_models();
			case 'yandex':
				return class_exists( 'SmartyPress_AI_Yandex_Models' ) ? SmartyPress_AI_Yandex_Models::get_image_models() : array();
			case 'gigachat':
				return class_exists( 'SmartyPress_AI_GigaChat_Models' ) ? SmartyPress_AI_GigaChat_Models::get_image_models() : array();
			default:
				return array();
		}
	}

	/**
	 * Get image edit models for provider.
	 *
	 * @param string $provider Provider ID.
	 * @return array
	 */
	public static function get_image_edit_models( $provider ) {
		switch ( $provider ) {
			case 'openrouter':
				return self::get_openrouter_image_edit_models();
			case 'openai':
				return self::get_openai_image_edit_models();
			default:
				return array();
		}
	}

	/**
	 * Get a normalized model by provider and model ID.
	 *
	 * @param string $provider Provider ID.
	 * @param string $model    Model ID.
	 * @return array|null
	 */
	public static function get_model( $provider, $model ) {
		$text_models = self::get_text_models( $provider );
		if ( isset( $text_models[ $model ] ) ) {
			return self::ensure_normalized_model( $provider, $model, $text_models[ $model ], 'text' );
		}

		$image_models = self::get_image_models( $provider );
		if ( isset( $image_models[ $model ] ) ) {
			return self::ensure_normalized_model( $provider, $model, $image_models[ $model ], 'image' );
		}

		return null;
	}

	/**
	 * Check if model supports capability.
	 *
	 * @param string $provider   Provider ID.
	 * @param string $model      Model ID.
	 * @param string $capability Capability name.
	 * @return bool
	 */
	public static function supports( $provider, $model, $capability ) {
		$model_data = self::get_model( $provider, $model );

		return ! empty( $model_data['capabilities'][ $capability ] );
	}

	/**
	 * Filter models by user-selected allow list.
	 *
	 * Empty selection means all models are available.
	 *
	 * @param array $models   Models keyed by ID.
	 * @param array $selected Selected model IDs.
	 * @return array
	 */
	public static function filter_models_by_selection( array $models, array $selected ) {
		$selected = array_values( array_filter( array_map( 'sanitize_text_field', $selected ) ) );

		if ( empty( $selected ) ) {
			return $models;
		}

		return array_intersect_key( $models, array_flip( $selected ) );
	}

	/**
	 * Filter request params based on model capabilities.
	 *
	 * @param string $provider Provider ID.
	 * @param string $model    Model ID.
	 * @param array  $params   Request params.
	 * @return array
	 */
	public static function filter_request_params( $provider, $model, array $params ) {
		$model_data = self::get_model( $provider, $model );

		if ( ! $model_data ) {
			return $params;
		}

		$supported = $model_data['supported_parameters'] ?? array();

		if ( 'openrouter' === $provider ) {
			return self::filter_openrouter_request_params( $params, $model_data, $supported );
		}

		if ( isset( $params['temperature'] ) && ! in_array( 'temperature', $supported, true ) ) {
			unset( $params['temperature'] );
		}

		if ( isset( $params['tools'] ) && ! in_array( 'tools', $supported, true ) ) {
			unset( $params['tools'] );
			unset( $params['tool_choice'] );
		}

		if ( isset( $params['reasoning'] ) && ! in_array( 'reasoning', $supported, true ) ) {
			unset( $params['reasoning'] );
		}

		if ( isset( $params['include_reasoning'] ) && ! in_array( 'include_reasoning', $supported, true ) ) {
			unset( $params['include_reasoning'] );
		}

		if ( isset( $params['modalities'] ) ) {
			$output_modalities = $model_data['output_modalities'] ?? array();
			$params['modalities'] = array_values(
				array_filter(
					(array) $params['modalities'],
					function ( $modality ) use ( $output_modalities ) {
						return in_array( $modality, $output_modalities, true );
					}
				)
			);

			if ( empty( $params['modalities'] ) ) {
				unset( $params['modalities'] );
			}
		}

		return $params;
	}

	/**
	 * Filter OpenRouter params using per-model metadata.
	 *
	 * @param array $params     Request params.
	 * @param array $model_data Normalized model data.
	 * @param array $supported  Supported parameter names.
	 * @return array
	 */
	private static function filter_openrouter_request_params( array $params, array $model_data, array $supported ) {
		if ( isset( $params['temperature'] ) && ! in_array( 'temperature', $supported, true ) ) {
			unset( $params['temperature'] );
		}

		if ( isset( $params['max_tokens'] ) ) {
			if ( in_array( 'max_tokens', $supported, true ) ) {
				// Keep max_tokens as-is.
			} elseif ( in_array( 'max_completion_tokens', $supported, true ) ) {
				if ( ! isset( $params['max_completion_tokens'] ) ) {
					$params['max_completion_tokens'] = $params['max_tokens'];
				}
				unset( $params['max_tokens'] );
			} else {
				unset( $params['max_tokens'] );
			}
		}

		if ( isset( $params['max_completion_tokens'] ) && ! in_array( 'max_completion_tokens', $supported, true ) ) {
			unset( $params['max_completion_tokens'] );
		}

		if ( isset( $params['tools'] ) && ! in_array( 'tools', $supported, true ) ) {
			unset( $params['tools'] );
		}

		if ( isset( $params['tool_choice'] ) && ( ! isset( $params['tools'] ) || ! in_array( 'tool_choice', $supported, true ) ) ) {
			unset( $params['tool_choice'] );
		}

		if ( isset( $params['reasoning'] ) && ! in_array( 'reasoning', $supported, true ) ) {
			unset( $params['reasoning'] );
		}

		if ( isset( $params['include_reasoning'] ) && ! in_array( 'include_reasoning', $supported, true ) ) {
			unset( $params['include_reasoning'] );
		}

		if ( isset( $params['response_format'] ) && ! in_array( 'response_format', $supported, true ) && ! in_array( 'structured_outputs', $supported, true ) ) {
			unset( $params['response_format'] );
		}

		if ( isset( $params['modalities'] ) ) {
			$output_modalities   = $model_data['output_modalities'] ?? array();
			$params['modalities'] = array_values(
				array_filter(
					(array) $params['modalities'],
					function ( $modality ) use ( $output_modalities ) {
						return in_array( $modality, $output_modalities, true );
					}
				)
			);

			if ( empty( $params['modalities'] ) ) {
				unset( $params['modalities'] );
			}
		}

		if ( isset( $params['image_config'] ) && empty( $model_data['capabilities']['image_output'] ) ) {
			unset( $params['image_config'] );
		}

		return $params;
	}

	/**
	 * Normalize OpenRouter model metadata.
	 *
	 * @param array  $model Raw OpenRouter model.
	 * @param string $type  Model type.
	 * @return array
	 */
	public static function normalize_openrouter_model( array $model, $type = 'text' ) {
		$id                   = sanitize_text_field( $model['id'] ?? '' );
		$name                 = sanitize_text_field( $model['name'] ?? $id );
		$architecture         = isset( $model['architecture'] ) && is_array( $model['architecture'] ) ? $model['architecture'] : array();
		$input_modalities     = self::sanitize_list( $architecture['input_modalities'] ?? array( 'text' ) );
		$output_modalities    = self::sanitize_list( $architecture['output_modalities'] ?? array( 'text' ) );
		$supported_parameters = self::sanitize_list( $model['supported_parameters'] ?? array() );
		$provider_parts       = explode( '/', $id );
		$token_param          = self::get_openrouter_token_param( $supported_parameters );
		$resolution_map       = self::get_openrouter_resolution_aspect_ratio_map( $id, $output_modalities );
		$capabilities         = array(
			'text_output'  => in_array( 'text', $output_modalities, true ),
			'image_output' => in_array( 'image', $output_modalities, true ),
			'image_input'  => in_array( 'image', $input_modalities, true ),
			'tools'        => in_array( 'tools', $supported_parameters, true ),
			'reasoning'    => in_array( 'reasoning', $supported_parameters, true ) || in_array( 'include_reasoning', $supported_parameters, true ),
			'temperature'  => in_array( 'temperature', $supported_parameters, true ),
			'web_search'   => false,
		);
		$capabilities         = apply_filters( 'smartypress_ai_openrouter_model_capabilities', $capabilities, $id, $model );
		$capabilities         = apply_filters( 'smartypress_ai_model_capabilities', $capabilities, 'openrouter', $id, $model );

		if ( 'image' === $type && ! in_array( 'image', $output_modalities, true ) ) {
			$output_modalities[] = 'image';
			$resolution_map      = self::get_openrouter_resolution_aspect_ratio_map( $id, $output_modalities );
		}

		return array(
			'id'                   => $id,
			'name'                 => $name,
			'provider'             => 'openrouter',
			'upstream_provider'    => sanitize_key( $provider_parts[0] ?? 'openrouter' ),
			'type'                 => 'image' === $type ? 'image' : 'text',
			'input_modalities'     => $input_modalities,
			'output_modalities'    => $output_modalities,
			'supported_parameters' => $supported_parameters,
			'capabilities'         => $capabilities,
			'request'              => array(
				'token_param'         => $token_param,
				'default_max_tokens'  => 2000,
				'default_temperature' => 0.7,
			),
			'pricing'              => isset( $model['pricing'] ) && is_array( $model['pricing'] ) ? $model['pricing'] : array(),
			'context_length'       => absint( $model['context_length'] ?? ( $model['top_provider']['context_length'] ?? 0 ) ),
			'expensive'            => self::is_expensive_openrouter_model( $model ),
			'reasoning'            => in_array( 'reasoning', $supported_parameters, true ) || in_array( 'include_reasoning', $supported_parameters, true ),
			'aspect_ratios'        => 'image' === $type ? array_values( $resolution_map ) : array(),
			'resolutions'          => 'image' === $type ? array_keys( $resolution_map ) : array(),
		);
	}

	/**
	 * Get token limit parameter supported by OpenRouter model.
	 *
	 * @param array $supported_parameters Supported parameter names.
	 * @return string
	 */
	private static function get_openrouter_token_param( array $supported_parameters ) {
		if ( in_array( 'max_tokens', $supported_parameters, true ) ) {
			return 'max_tokens';
		}

		if ( in_array( 'max_completion_tokens', $supported_parameters, true ) ) {
			return 'max_completion_tokens';
		}

		return '';
	}

	/**
	 * Get OpenRouter models with cache and fallback.
	 *
	 * @param string $type Model type.
	 * @return array
	 */
	private static function get_openrouter_models( $type ) {
		$type          = 'image' === $type ? 'image' : 'text';
		$transient_key = 'image' === $type ? self::OPENROUTER_IMAGE_TRANSIENT : self::OPENROUTER_TEXT_TRANSIENT;
		$cached        = get_transient( $transient_key );

		if ( is_array( $cached ) && ! empty( $cached ) ) {
			$cached = self::ensure_openrouter_cached_metadata( $cached, $type );
			set_transient( $transient_key, $cached, self::OPENROUTER_CACHE_TTL );
			return $cached;
		}

		$models = self::fetch_openrouter_models( $type );

		if ( ! empty( $models ) ) {
			set_transient( $transient_key, $models, self::OPENROUTER_CACHE_TTL );
			return $models;
		}

		return 'image' === $type ? self::get_openrouter_image_fallback_models() : self::get_openrouter_text_fallback_models();
	}

	/**
	 * Get OpenRouter models that can accept and output images.
	 *
	 * @return array
	 */
	private static function get_openrouter_image_edit_models() {
		$models = self::get_openrouter_models( 'image' );

		return array_filter(
			$models,
			function ( $model ) {
				return ! empty( $model['capabilities']['image_input'] ) && ! empty( $model['capabilities']['image_output'] );
			}
		);
	}

	/**
	 * Enrich cached OpenRouter models with metadata added after the cache was created.
	 *
	 * @param array  $models Cached models.
	 * @param string $type   Model type.
	 * @return array
	 */
	private static function ensure_openrouter_cached_metadata( array $models, $type ) {
		foreach ( $models as $model_id => $model ) {
			if ( ! is_array( $model ) ) {
				continue;
			}

			$model['capabilities'] = is_array( $model['capabilities'] ?? null ) ? $model['capabilities'] : array();
			$model['capabilities']['web_search'] = false;
			$model['capabilities'] = apply_filters( 'smartypress_ai_openrouter_model_capabilities', $model['capabilities'], $model['id'] ?? $model_id, $model );
			$model['capabilities'] = apply_filters( 'smartypress_ai_model_capabilities', $model['capabilities'], 'openrouter', $model['id'] ?? $model_id, $model );

			if ( 'image' !== $type ) {
				$models[ $model_id ] = $model;
				continue;
			}

			$output_modalities = self::sanitize_list( $model['output_modalities'] ?? array() );
			if ( empty( $output_modalities ) && ! empty( $model['capabilities']['image_output'] ) ) {
				$output_modalities = array( 'image' );
			}

			$resolution_map = self::get_openrouter_resolution_aspect_ratio_map( $model['id'] ?? $model_id, $output_modalities );
			if ( empty( $resolution_map ) ) {
				$model['aspect_ratios'] = array();
				$model['resolutions']   = array();
			} else {
				$model['aspect_ratios'] = array_values( $resolution_map );
				$model['resolutions']   = array_keys( $resolution_map );
			}

			$models[ $model_id ] = $model;
		}

		return $models;
	}

	/**
	 * Fetch models from OpenRouter API.
	 *
	 * @param string $type Model type.
	 * @return array
	 */
	private static function fetch_openrouter_models( $type ) {
		$url      = add_query_arg( 'output_modalities', 'image' === $type ? 'image' : 'text', self::OPENROUTER_MODELS_URL );
		$response = wp_remote_get(
			$url,
			array(
				'timeout' => 12,
				'headers' => array(
					'Accept' => 'application/json',
				),
			)
		);

		if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
			return array();
		}

		$data = json_decode( wp_remote_retrieve_body( $response ), true );
		if ( empty( $data['data'] ) || ! is_array( $data['data'] ) ) {
			return array();
		}

		$models = array();
		foreach ( $data['data'] as $model ) {
			if ( ! is_array( $model ) || empty( $model['id'] ) ) {
				continue;
			}

			$normalized = self::normalize_openrouter_model( $model, $type );
			$models[ $normalized['id'] ] = $normalized;
		}

		return $models;
	}

	/**
	 * Normalize existing model metadata.
	 *
	 * @param string       $provider Provider ID.
	 * @param string       $model_id Model ID.
	 * @param array|string $model    Model metadata.
	 * @param string       $type     Model type.
	 * @return array
	 */
	private static function ensure_normalized_model( $provider, $model_id, $model, $type ) {
		if ( is_array( $model ) && isset( $model['capabilities'] ) ) {
			return $model;
		}

		$name = is_array( $model ) ? ( $model['name'] ?? $model_id ) : $model;
		$supported_parameters = is_array( $model ) && ! empty( $model['supported_parameters'] )
			? (array) $model['supported_parameters']
			: array( 'temperature', 'max_tokens' );
		$capabilities = array(
			'text_output'  => 'text' === $type,
			'image_output' => 'image' === $type,
			'image_input'  => false,
			'tools'        => in_array( 'tools', $supported_parameters, true ),
			'reasoning'    => ! empty( $model['reasoning'] ),
			'temperature'  => in_array( 'temperature', $supported_parameters, true ),
			'web_search'   => false,
		);
		$capabilities = apply_filters( 'smartypress_ai_model_capabilities', $capabilities, $provider, $model_id, is_array( $model ) ? $model : array() );

		return array(
			'id'                   => $model_id,
			'name'                 => $name,
			'provider'             => $provider,
			'type'                 => $type,
			'input_modalities'     => array( 'text' ),
			'output_modalities'    => 'image' === $type ? array( 'image' ) : array( 'text' ),
			'supported_parameters' => $supported_parameters,
			'capabilities'         => $capabilities,
			'request'              => array(
				'token_param'         => 'max_tokens',
				'default_max_tokens'  => 2000,
				'default_temperature' => 0.7,
			),
			'expensive'            => is_array( $model ) && ! empty( $model['expensive'] ),
			'reasoning'            => is_array( $model ) && ! empty( $model['reasoning'] ),
			'resolutions'          => is_array( $model ) && ! empty( $model['resolutions'] ) ? $model['resolutions'] : array(),
		);
	}

	/**
	 * Sanitize scalar list.
	 *
	 * @param array $values Values.
	 * @return array
	 */
	private static function sanitize_list( $values ) {
		if ( ! is_array( $values ) ) {
			return array();
		}

		return array_values( array_filter( array_map( 'sanitize_text_field', $values ) ) );
	}

	/**
	 * Detect expensive OpenRouter model from pricing.
	 *
	 * @param array $model Raw model.
	 * @return bool
	 */
	private static function is_expensive_openrouter_model( $model ) {
		$prompt_price     = isset( $model['pricing']['prompt'] ) ? (float) $model['pricing']['prompt'] : 0;
		$completion_price = isset( $model['pricing']['completion'] ) ? (float) $model['pricing']['completion'] : 0;

		return $prompt_price >= 0.000005 || $completion_price >= 0.000015;
	}

	/**
	 * Get OpenRouter UI resolution values mapped to API aspect ratios.
	 *
	 * @param string $model_id          Model ID.
	 * @param array  $output_modalities Model output modalities.
	 * @return array Resolution/aspect-ratio map.
	 */
	private static function get_openrouter_resolution_aspect_ratio_map( $model_id, array $output_modalities ) {
		if ( ! in_array( 'image', $output_modalities, true ) ) {
			return array();
		}

		$map = array(
			'1024x1024' => '1:1',
			'832x1248' => '2:3',
			'1248x832' => '3:2',
			'864x1184' => '3:4',
			'1184x864' => '4:3',
			'896x1152' => '4:5',
			'1152x896' => '5:4',
			'768x1344' => '9:16',
			'1344x768' => '16:9',
			'1536x672' => '21:9',
		);

		if ( 'google/gemini-3.1-flash-image-preview' === $model_id ) {
			$map['1:4'] = '1:4';
			$map['4:1'] = '4:1';
			$map['1:8'] = '1:8';
			$map['8:1'] = '8:1';
		}

		return $map;
	}

	/**
	 * Fallback OpenRouter text models.
	 *
	 * @return array
	 */
	private static function get_openrouter_text_fallback_models() {
		$models = array(
			'openai/gpt-5.2'                   => 'GPT-5.2',
			'openai/gpt-4o-mini'               => 'GPT-4o Mini',
			'anthropic/claude-sonnet-4'        => 'Claude Sonnet 4',
			'google/gemini-2.5-flash'          => 'Gemini 2.5 Flash',
			'deepseek/deepseek-chat-v3.1'      => 'DeepSeek Chat V3.1',
			'meta-llama/llama-3.3-70b-instruct' => 'Llama 3.3 70B Instruct',
		);

		$models = apply_filters( 'smartypress_ai_openrouter_text_fallback_models', $models );

		return self::normalize_fallback_models( $models, 'text' );
	}

	/**
	 * Fallback OpenRouter image models.
	 *
	 * @return array
	 */
	private static function get_openrouter_image_fallback_models() {
		$models = array(
			'google/gemini-2.5-flash-image'       => 'Gemini 2.5 Flash Image',
			'black-forest-labs/flux.2-pro'        => 'FLUX.2 Pro',
			'black-forest-labs/flux.2-flex'       => 'FLUX.2 Flex',
			'sourceful/riverflow-v2-standard-preview' => 'Riverflow V2 Standard Preview',
		);

		return self::normalize_fallback_models( $models, 'image' );
	}

	/**
	 * Normalize simple fallback models.
	 *
	 * @param array  $models Models.
	 * @param string $type   Type.
	 * @return array
	 */
	private static function normalize_fallback_models( $models, $type ) {
		$normalized = array();

		foreach ( $models as $id => $name ) {
			$normalized[ $id ] = self::normalize_openrouter_model(
				array(
					'id'                   => $id,
					'name'                 => $name,
					'architecture'         => array(
						'input_modalities'  => array( 'text' ),
						'output_modalities' => 'image' === $type ? array( 'image' ) : array( 'text' ),
					),
					'supported_parameters' => array( 'temperature', 'max_tokens' ),
				),
				$type
			);
		}

		return $normalized;
	}

	/**
	 * Static OpenAI text models.
	 *
	 * @return array
	 */
	private static function get_openai_text_models() {
		return array(
			'gpt-5.3-codex' => array( 'name' => 'GPT-5.3 Codex', 'expensive' => true, 'reasoning' => true ),
			'gpt-5.2' => array( 'name' => 'GPT-5.2', 'expensive' => true, 'reasoning' => true ),
			'gpt-5.1' => array( 'name' => 'GPT-5.1', 'expensive' => true, 'reasoning' => false ),
			'gpt-5' => array( 'name' => 'GPT-5', 'expensive' => true, 'reasoning' => false ),
			'gpt-4.1' => array( 'name' => 'GPT-4.1', 'expensive' => true, 'reasoning' => false ),
			'o3' => array( 'name' => 'O3', 'expensive' => true, 'reasoning' => true ),
			'o3-mini' => array( 'name' => 'O3 Mini', 'expensive' => true, 'reasoning' => true ),
			'gpt-4o-2024-11-20' => array( 'name' => 'GPT-4o (Nov 2024)', 'expensive' => false, 'reasoning' => false ),
			'o1' => array( 'name' => 'O1', 'expensive' => true, 'reasoning' => true ),
			'o1-mini' => array( 'name' => 'O1 Mini', 'expensive' => true, 'reasoning' => true ),
			'o1-preview' => array( 'name' => 'O1 Preview', 'expensive' => true, 'reasoning' => true ),
			'gpt-4o' => array( 'name' => 'GPT-4o', 'expensive' => false, 'reasoning' => false ),
			'gpt-4o-mini' => array( 'name' => 'GPT-4o Mini', 'expensive' => false, 'reasoning' => false ),
			'gpt-4-turbo' => array( 'name' => 'GPT-4 Turbo', 'expensive' => true, 'reasoning' => false ),
			'gpt-3.5-turbo' => array( 'name' => 'GPT-3.5 Turbo', 'expensive' => false, 'reasoning' => false ),
		);
	}

	/**
	 * Static Deepseek text models.
	 *
	 * @return array
	 */
	private static function get_deepseek_text_models() {
		return array(
			'deepseek-v4-flash' => array( 'name' => 'DeepSeek V4 Flash', 'expensive' => false, 'reasoning' => true ),
			'deepseek-v4-pro' => array( 'name' => 'DeepSeek V4 Pro', 'expensive' => true, 'reasoning' => true ),
		);
	}

	/**
	 * Static Anthropic Claude text and vision models.
	 *
	 * @return array
	 */
	private static function get_anthropic_text_models() {
		$models = array(
			'claude-sonnet-4-6' => array(
				'name'      => 'Claude Sonnet 4.6',
				'expensive' => true,
				'reasoning' => true,
			),
			'claude-opus-4-7' => array(
				'name'      => 'Claude Opus 4.7',
				'expensive' => true,
				'reasoning' => true,
			),
			'claude-opus-4-6' => array(
				'name'      => 'Claude Opus 4.6',
				'expensive' => true,
				'reasoning' => true,
			),
			'claude-haiku-4-5' => array(
				'name'      => 'Claude Haiku 4.5',
				'expensive' => false,
				'reasoning' => false,
			),
			'claude-sonnet-4-5' => array(
				'name'      => 'Claude Sonnet 4.5',
				'expensive' => true,
				'reasoning' => true,
			),
			'claude-opus-4-5' => array(
				'name'      => 'Claude Opus 4.5',
				'expensive' => true,
				'reasoning' => true,
			),
			'claude-opus-4-1' => array(
				'name'      => 'Claude Opus 4.1',
				'expensive' => true,
				'reasoning' => true,
			),
			'claude-sonnet-4-0' => array(
				'name'      => 'Claude Sonnet 4',
				'expensive' => true,
				'reasoning' => true,
			),
			'claude-opus-4-0' => array(
				'name'      => 'Claude Opus 4',
				'expensive' => true,
				'reasoning' => true,
			),
			'claude-3-haiku-20240307' => array(
				'name'      => 'Claude 3 Haiku',
				'expensive' => false,
				'reasoning' => false,
			),
		);

		foreach ( $models as $model_id => $model ) {
			$models[ $model_id ] = array_merge(
				$model,
				array(
					'id'                   => $model_id,
					'provider'             => 'anthropic',
					'type'                 => 'text',
					'input_modalities'     => array( 'text', 'image' ),
					'output_modalities'    => array( 'text' ),
					'supported_parameters' => array( 'temperature', 'max_tokens' ),
					'capabilities'         => array(
						'text_output'  => true,
						'image_output' => false,
						'image_input'  => true,
						'tools'        => false,
						'reasoning'    => ! empty( $model['reasoning'] ),
						'temperature'  => true,
						'web_search'   => false,
					),
					'request'              => array(
						'token_param'         => 'max_tokens',
						'default_max_tokens'  => 2000,
						'default_temperature' => 0.7,
					),
				)
			);
		}

		return $models;
	}

	/**
	 * Static OpenAI image models.
	 *
	 * @return array
	 */
	private static function get_openai_image_models() {
		return array(
			'gpt-image-1' => array( 'name' => 'GPT Image 1', 'resolutions' => array( '1024x1024', '1024x1536', '1536x1024', 'auto' ) ),
			'gpt-image-1.5' => array( 'name' => 'GPT Image 1.5', 'resolutions' => array( '1024x1024', '1024x1536', '1536x1024', 'auto' ) ),
			'dall-e-3' => array( 'name' => 'DALL-E 3', 'resolutions' => array( '1024x1024', '1024x1792', '1792x1024' ) ),
			'dall-e-3-hd' => array( 'name' => 'DALL-E 3 HD', 'resolutions' => array( '1024x1024', '1024x1792', '1792x1024' ) ),
		);
	}

	/**
	 * Static OpenAI image edit models.
	 *
	 * @return array
	 */
	private static function get_openai_image_edit_models() {
		return array(
			'gpt-image-1' => array( 'name' => 'GPT Image 1', 'resolutions' => array( '1024x1024', '1024x1536', '1536x1024', 'auto' ) ),
			'gpt-image-1.5' => array( 'name' => 'GPT Image 1.5', 'resolutions' => array( '1024x1024', '1024x1536', '1536x1024', 'auto' ) ),
		);
	}
}
