<?php
defined( 'ABSPATH' ) || exit;

/**
 * Provider model discovery and filtering used by the Settings AJAX controller.
 */
final class Costudio_Admin_Model_Discovery {

	private function __construct() {
	}

	/**
	 * @return array<int, array{value:string,label:string}>|\WP_Error
	 */
	public static function fetch_text_models( string $provider, string $api_key ) {
		switch ( $provider ) {
			case 'gemini':
				$raw = self::fetch_gemini_models_all_pages( $api_key );
				if ( is_wp_error( $raw ) ) {
					return $raw;
				}
				return self::filter_gemini_text_generation_models( $raw );

			case 'openai':
				$ids = self::fetch_openai_model_ids( $api_key );
				if ( is_wp_error( $ids ) ) {
					return $ids;
				}
				return self::filter_openai_chat_text_models( $ids );

			case 'claude':
				return self::fetch_claude_text_models( $api_key );

			default:
				return new WP_Error( 'costudio_unknown_provider', __( 'Unknown provider.', 'contentosapp-studio' ) );
		}
	}

	/**
	 * @return array<int, array{value:string,label:string}>|\WP_Error
	 */
	public static function fetch_image_models( string $provider, string $api_key ) {
		$models = array();

		switch ( $provider ) {
			case 'gemini_imagen':
				$raw = self::fetch_gemini_models_all_pages( $api_key );
				if ( is_wp_error( $raw ) ) {
					return $raw;
				}
				$models = self::filter_gemini_image_models( $raw );
				break;

			case 'openai_dalle':
				$ids = self::fetch_openai_model_ids( $api_key );
				if ( is_wp_error( $ids ) ) {
					return $ids;
				}
				$models = self::filter_openai_image_models( $ids );
				break;
		}

		return self::dedupe_and_sort_models( $models );
	}

	/**
	 * @return array<int, array{value:string,label:string}>|\WP_Error
	 */
	public static function fetch_search_models( string $provider, string $api_key ) {
		switch ( $provider ) {
			case 'gemini':
				if ( empty( $api_key ) ) {
					return new WP_Error( 'costudio_missing_api_key', __( 'Enter an API key first.', 'contentosapp-studio' ) );
				}
				$raw = self::fetch_gemini_models_all_pages( $api_key );
				if ( is_wp_error( $raw ) ) {
					return $raw;
				}
				return self::filter_gemini_text_generation_models( $raw );

			case 'tavily':
				return array(
					array( 'value' => 'advanced', 'label' => 'Advanced (Recommended)' ),
					array( 'value' => 'basic', 'label' => 'Basic' ),
				);

			case 'exa':
				return array(
					array( 'value' => 'auto', 'label' => 'Auto (Recommended)' ),
					array( 'value' => 'fast', 'label' => 'Fast' ),
					array( 'value' => 'deep', 'label' => 'Deep' ),
				);

			case 'serper':
				return array(
					array( 'value' => 'search', 'label' => 'Web (Default)' ),
					array( 'value' => 'news', 'label' => 'News' ),
				);

			default:
				return new WP_Error( 'costudio_search_models_unavailable', __( 'This provider does not have multiple models.', 'contentosapp-studio' ) );
		}
	}

	/**
	 * Fetch all Gemini model definitions (paginated) from the Generative Language API.
	 *
	 * @return array<int, array<string, mixed>>|\WP_Error
	 */
	public static function fetch_gemini_models_all_pages( string $api_key ) {
		$merged     = array();
		$page_token = '';
		for ( $i = 0; $i < 15; $i++ ) {
			$url = 'https://generativelanguage.googleapis.com/v1beta/models?key=' . rawurlencode( $api_key ) . '&pageSize=100';
			if ( '' !== $page_token ) {
				$url .= '&pageToken=' . rawurlencode( $page_token );
			}
			$res = wp_remote_get( $url, array( 'timeout' => 20 ) );
			if ( is_wp_error( $res ) ) {
				return $res;
			}
			$code = (int) wp_remote_retrieve_response_code( $res );
			$body = json_decode( wp_remote_retrieve_body( $res ), true );
			if ( 200 !== $code ) {
				return new WP_Error(
					'costudio_gemini_models',
					$body['error']['message'] ?? "HTTP {$code}"
				);
			}
			$merged     = array_merge( $merged, $body['models'] ?? array() );
			$page_token = $body['nextPageToken'] ?? '';
			if ( '' === $page_token ) {
				break;
			}
		}
		return $merged;
	}

	/**
	 * Fetch a single Gemini model definition by model id.
	 *
	 * @return array<string, mixed>|\WP_Error
	 */
	public static function fetch_gemini_model( string $api_key, string $model ) {
		$model = self::normalize_gemini_model_id( $model );
		if ( '' === $model ) {
			return new WP_Error( 'costudio_missing_model', __( 'Select a model first.', 'contentosapp-studio' ) );
		}

		$url  = 'https://generativelanguage.googleapis.com/v1beta/models/' . rawurlencode( $model ) . '?key=' . rawurlencode( $api_key );
		$res  = wp_remote_get( $url, array( 'timeout' => 15 ) );
		if ( is_wp_error( $res ) ) {
			return $res;
		}

		$code = (int) wp_remote_retrieve_response_code( $res );
		$body = json_decode( wp_remote_retrieve_body( $res ), true );
		if ( 200 !== $code ) {
			return new WP_Error(
				'costudio_gemini_model',
				is_array( $body ) ? ( $body['error']['message'] ?? "HTTP {$code}" ) : "HTTP {$code}"
			);
		}

		return is_array( $body ) ? $body : array();
	}

	public static function normalize_gemini_model_id( string $model ): string {
		$model = trim( $model );
		if ( 0 === strpos( $model, 'models/' ) ) {
			$model = substr( $model, 7 );
		}
		return trim( $model );
	}

	/**
	 * Keep only Gemini models suitable for long-form text generation.
	 */
	public static function filter_gemini_text_generation_models( array $raw_models ): array {
		$out = array();
		foreach ( $raw_models as $m ) {
			if ( ! self::is_gemini_article_text_model( $m ) ) {
				continue;
			}
			$name  = str_replace( 'models/', '', $m['name'] ?? '' );
			$out[] = array(
				'value' => $name,
				'label' => $m['displayName'] ?? $name,
			);
		}
		return self::dedupe_and_sort_models( $out );
	}

	public static function is_gemini_article_text_model( array $m ): bool {
		$methods = $m['supportedGenerationMethods'] ?? array();
		if ( ! in_array( 'generateContent', $methods, true ) ) {
			return false;
		}
		$name        = str_replace( 'models/', '', $m['name'] ?? '' );
		$n           = strtolower( $name );
		$display     = strtolower( (string) ( $m['displayName'] ?? '' ) );
		$description = strtolower( (string) ( $m['description'] ?? '' ) );
		$haystack    = $n . ' ' . $display . ' ' . $description;

		if ( preg_match( '/nano\s*banana|nanobanana|nano-banana|nano_banana/i', $haystack ) ) {
			return false;
		}

		$block = array( 'embed', 'embedding', 'imagen', 'tts', 'aqa', 'bison', 'gecko', 'palm', 'audio', 'robotics', 'textembedding', 'learnlm' );
		foreach ( $block as $b ) {
			if ( false !== strpos( $n, $b ) ) {
				return false;
			}
		}

		if ( false !== strpos( $n, 'nano' ) ) {
			return false;
		}

		return (bool) preg_match( '/^(gemini-|gemma-)/i', $name );
	}

	public static function filter_openai_chat_text_models( array $ids ): array {
		$models = array();
		sort( $ids );
		foreach ( $ids as $id ) {
			if ( ! self::is_openai_chat_text_model( (string) $id ) ) {
				continue;
			}
			$models[] = array(
				'value' => (string) $id,
				'label' => (string) $id,
			);
		}
		return $models;
	}

	public static function is_openai_chat_text_model( string $id ): bool {
		$lower              = strtolower( $id );
		$blocked_substrings = array(
			'embedding',
			'embed',
			'whisper',
			'dall-e',
			'dall_e',
			'tts',
			'moderation',
			'davinci',
			'babbage',
			'ada',
			'curie',
			'cushman',
			'realtime',
			'transcrib',
			'sora',
			'gpt-image',
			'computer-use',
			'search-api',
			'clip',
			'audio',
		);
		foreach ( $blocked_substrings as $b ) {
			if ( false !== strpos( $lower, $b ) ) {
				return false;
			}
		}
		if ( preg_match( '/^ft:/i', $id ) ) {
			return true;
		}
		return (bool) preg_match( '/^(gpt-|chatgpt-|o[0-9])/i', $id );
	}

	public static function filter_claude_messages_text_models( array $data_rows ): array {
		$models = array();
		foreach ( $data_rows as $m ) {
			$id = (string) ( $m['id'] ?? '' );
			if ( ! self::is_claude_messages_text_model( $id ) ) {
				continue;
			}
			$models[] = array(
				'value' => $id,
				'label' => (string) ( $m['display_name'] ?? $id ),
			);
		}
		return $models;
	}

	public static function is_claude_messages_text_model( string $id ): bool {
		if ( ! preg_match( '/^claude-/i', $id ) ) {
			return false;
		}
		$lower = strtolower( $id );
		foreach ( array( 'embed', 'embedding' ) as $b ) {
			if ( false !== strpos( $lower, $b ) ) {
				return false;
			}
		}
		return true;
	}

	public static function is_gemini_image_generation_model( array $m ): bool {
		$methods     = (array) ( $m['supportedGenerationMethods'] ?? array() );
		$name        = self::normalize_gemini_model_id( (string) ( $m['name'] ?? '' ) );
		$n           = strtolower( $name );
		$display     = strtolower( (string) ( $m['displayName'] ?? '' ) );
		$description = strtolower( (string) ( $m['description'] ?? '' ) );
		$haystack    = $n . ' ' . $display . ' ' . $description;

		if ( in_array( 'generateImages', $methods, true ) ) {
			return true;
		}

		if ( preg_match( '/nano\s*banana|nanobanana|nano-banana|nano_banana/i', $haystack ) ) {
			return true;
		}

		if ( preg_match( '/^(imagegeneration@|imagen-(3|4)(?:\.0)?[-.])/i', $name ) ) {
			return true;
		}

		if ( ! preg_match( '/^gemini-.*-image(?:-|$)/i', $name ) ) {
			return false;
		}

		foreach ( array( 'audio', 'tts', 'live', 'veo', 'video', 'embed', 'embedding', 'learnlm', 'aqa', 'robotics', 'translate' ) as $blocked ) {
			if ( false !== strpos( $n, $blocked ) ) {
				return false;
			}
		}

		return in_array( 'generateContent', $methods, true ) || empty( $methods );
	}

	private static function filter_gemini_image_models( array $raw_models ): array {
		$models = array();
		foreach ( $raw_models as $m ) {
			$name     = self::normalize_gemini_model_id( (string) ( $m['name'] ?? '' ) );
			$label    = (string) ( $m['displayName'] ?? $name );
			if ( ! self::is_gemini_image_generation_model( $m ) ) {
				continue;
			}
			$models[] = array(
				'value' => $name,
				'label' => $label,
			);
		}
		return $models;
	}

	/**
	 * Normalize cached/fetched model rows so every ambiguous display label shows
	 * the exact model id that will be sent to the provider API.
	 */
	public static function normalize_model_rows( array $models ): array {
		$normalized = array();
		foreach ( $models as $row ) {
			if ( ! is_array( $row ) ) {
				continue;
			}
			$value = trim( (string) ( $row['value'] ?? '' ) );
			if ( '' === $value ) {
				continue;
			}
			$label = trim( (string) ( $row['label'] ?? $value ) );
			if ( '' === $label ) {
				$label = $value;
			}
			if ( 0 !== strcasecmp( $label, $value ) && false === stripos( $label, $value ) ) {
				$label .= ' - ' . $value;
			}
			if ( preg_match( '/^imagen-4\.0-/i', $value ) && false === stripos( $label, 'deprecated' ) ) {
				$label .= ' (deprecated Aug 17, 2026)';
			}
			$row['value'] = $value;
			$row['label'] = $label;
			$normalized[] = $row;
		}
		return $normalized;
	}

	public static function normalize_model_cache( array $cache ): array {
		$normalized = array();
		foreach ( $cache as $provider => $models ) {
			$normalized[ $provider ] = is_array( $models )
				? self::normalize_model_rows( $models )
				: array();
		}
		return $normalized;
	}

	private static function filter_openai_image_models( array $ids ): array {
		$models = array();
		sort( $ids );
		foreach ( $ids as $id ) {
			$lower = strtolower( (string) $id );
			if ( false !== strpos( $lower, 'gpt-image' ) || false !== strpos( $lower, 'dall-e' ) || false !== strpos( $lower, 'dall_e' ) ) {
				$models[] = array(
					'value' => (string) $id,
					'label' => (string) $id,
				);
			}
		}
		return $models;
	}

	/**
	 * @return array<int, string>|\WP_Error
	 */
	private static function fetch_openai_model_ids( string $api_key ) {
		$res = wp_remote_get(
			'https://api.openai.com/v1/models',
			array(
				'timeout' => 15,
				'headers' => array( 'Authorization' => 'Bearer ' . $api_key ),
			)
		);
		if ( is_wp_error( $res ) ) {
			return $res;
		}
		$code = (int) wp_remote_retrieve_response_code( $res );
		$body = json_decode( wp_remote_retrieve_body( $res ), true );
		if ( 200 !== $code ) {
			return new WP_Error( 'costudio_openai_models', $body['error']['message'] ?? "HTTP {$code}" );
		}
		return array_column( $body['data'] ?? array(), 'id' );
	}

	/**
	 * @return array<int, array{value:string,label:string}>|\WP_Error
	 */
	private static function fetch_claude_text_models( string $api_key ) {
		$models   = array();
		$after_id = '';
		for ( $p = 0; $p < 20; $p++ ) {
			$url = 'https://api.anthropic.com/v1/models';
			$q   = array( 'limit' => 1000 );
			if ( '' !== $after_id ) {
				$q['after_id'] = $after_id;
			}
			$url = add_query_arg( $q, $url );
			$res = wp_remote_get(
				$url,
				array(
					'timeout' => 20,
					'headers' => array(
						'x-api-key'         => $api_key,
						'anthropic-version' => '2023-06-01',
					),
				)
			);
			if ( is_wp_error( $res ) ) {
				return $res;
			}
			$code = (int) wp_remote_retrieve_response_code( $res );
			$body = json_decode( wp_remote_retrieve_body( $res ), true );
			if ( 200 !== $code ) {
				return new WP_Error( 'costudio_claude_models', $body['error']['message'] ?? "HTTP {$code}" );
			}
			$batch = self::filter_claude_messages_text_models( $body['data'] ?? array() );
			foreach ( $batch as $row ) {
				$models[] = $row;
			}
			if ( empty( $body['has_more'] ) ) {
				break;
			}
			$after_id = $body['last_id'] ?? '';
			if ( '' === $after_id ) {
				break;
			}
		}

		usort(
			$models,
			static function ( $a, $b ) {
				return strcasecmp( $a['label'] ?? '', $b['label'] ?? '' );
			}
		);

		return $models;
	}

	private static function dedupe_and_sort_models( array $models ): array {
		$models = self::normalize_model_rows( $models );
		$seen  = array();
		$dedup = array();
		foreach ( $models as $row ) {
			$k = $row['value'];
			if ( isset( $seen[ $k ] ) ) {
				continue;
			}
			$seen[ $k ] = true;
			$dedup[]    = $row;
		}
		usort(
			$dedup,
			static function ( $a, $b ) {
				return strcasecmp( $a['label'], $b['label'] );
			}
		);
		return $dedup;
	}
}
