<?php
/**
 * Google Gemini Provider
 *
 * Handles communication with Google Gemini API.
 *
 * @package Askany
 * @since 1.0.0
 */

namespace Askany\Providers;

/**
 * Gemini provider class.
 */
class GeminiProvider extends AbstractProvider {
	/**
	 * Constructor.
	 *
	 * @param string $api_key API key for the provider.
	 * @param string $api_url Optional custom API URL.
	 */
	public function __construct( string $api_key, string $api_url = '' ) {
		// Ensure URL has {model} placeholder, otherwise use default
		if ( ! empty( $api_url ) && strpos( $api_url, '{model}' ) === false ) {
			// error_log( 'Gemini API: URL missing {model} placeholder, using default URL' );
			$api_url = $this->get_default_endpoint();
		}
		
		parent::__construct( $api_key, $api_url );
	}
	
	/**
	 * {@inheritdoc}
	 */
	public function get_provider_name(): string {
		return 'gemini';
	}

	/**
	 * {@inheritdoc}
	 */
	public function get_provider_display_name(): string {
		return 'Google Gemini';
	}

	/**
	 * {@inheritdoc}	
	 */
	public function get_available_models(): array {
		return array(
			'gemini-2.5-flash-lite' => [
                'name'        		  => 'Gemini 2.5 Flash-Lite',
                'description' 		  => 'Cost-efficient, high throughput Gemini variant',
                'max_tokens'  		  => 1048576,
                'cost_per_1k_tokens'  => 0.00005,
				'recommended' 		  => true,
            ],

			'gemini-2.5-flash'     	 => array(
                'name'               => 'Gemini 2.5 Flash',
                'description'        => 'Best price-performance ratio (Recommended)',
                'max_tokens'         => 8192,
                'cost_per_1k_tokens' => 0.000075,
            ),
			'gemini-2.5-pro'       	 => array(
                'name'               => 'Gemini 2.5 Pro',
                'description'        => 'Advanced thinking and reasoning',
                'max_tokens'         => 8192,
                'cost_per_1k_tokens' => 0.00125,
            ),
			'gemini-2.0-flash-exp' 	=> [
                'name' 				=> 'Gemini 2.0 Flash',
                'description' 		=> 'Experimental model with enhanced capabilities',
                'max_tokens' 		=> 8192,
                'cost_per_1k_tokens'=> 0.000075,
            ],
			'gemini-1.5-flash'     	 => array(
                'name'               => 'Gemini 1.5 Flash',
                'description'        => 'Fast and efficient',
                'max_tokens'         => 1048576,
                'cost_per_1k_tokens' => 0.000075,
            ),
			'gemini-1.5-flash-8b'  	 => array(
                'name'               => 'Gemini 1.5 Flash-8B',
                'description'        => 'Smaller, faster variant',
                'max_tokens'         => 1048576,
                'cost_per_1k_tokens' => 0.0000375,
            ),
			'gemini-1.5-pro'       	 => array(
                'name'               => 'Gemini 1.5 Pro',
                'description'        => 'Most capable Gemini 1.5 model',
                'max_tokens'         => 2097152,
                'cost_per_1k_tokens' => 0.00125,
            ),
			'gemini-pro'           	 => array(
                'name'               => 'Gemini Pro',
                'description'        => 'Standard Gemini model',
                'max_tokens'         => 32768,
                'cost_per_1k_tokens' => 0.0005,
            ),
		);
	}

	/**
	 * {@inheritdoc}
	 */
	public function get_default_model(): string {
		return 'gemini-2.5-flash';
	}

	/**
	 * {@inheritdoc}
	 */
	public function get_default_endpoint(): string {
		// Gemini API v1beta endpoint - model name will be inserted directly
		return 'https://generativelanguage.googleapis.com/v1beta/models/{model}:generateContent';
	}

	/**
	 * {@inheritdoc}
	 */
	public function send_chat_request( array $messages, array $options = array() ) {
		$defaults = array(
			'model'       => $this->get_default_model(),
			'temperature' => 0.3,
			'max_tokens'  => 800,
		);

		$params = array_merge( $defaults, $options );
		$model = $params['model'];
		
		// Ensure we have a valid model name
		if ( empty( $model ) || $model === 'model' ) {
			$model = $this->get_default_model();
			// error_log( 'Gemini API: No valid model specified, using default: ' . $model );
		}



		// Build Gemini-specific request format
		$contents = $this->convert_messages_to_gemini_format( $messages );

		if ( empty( $contents ) ) {
			// error_log( 'Gemini API: No valid contents after message conversion' );
			return false;
		}

		// Build generation config with Gemini-specific parameters
		$generation_config = array(
			'temperature'     => floatval( $params['temperature'] ),
			'maxOutputTokens' => intval( $params['max_tokens'] ),
			'topP'            => floatval( $params['top_p'] ?? 0.95 ),
			'topK'            => intval( $params['top_k'] ?? 40 ),
		);
		
		// Add optional candidate count
		if ( isset( $params['candidate_count'] ) ) {
			$generation_config['candidateCount'] = intval( $params['candidate_count'] );
		}
		
		// Build request with safety settings
		$request_data = array(
			'contents'         => $contents,
			'generationConfig' => $generation_config,
			'safetySettings'   => array(
				array(
					'category'  => 'HARM_CATEGORY_HARASSMENT',
					'threshold' => 'BLOCK_MEDIUM_AND_ABOVE',
				),
				array(
					'category'  => 'HARM_CATEGORY_HATE_SPEECH',
					'threshold' => 'BLOCK_MEDIUM_AND_ABOVE',
				),
				array(
					'category'  => 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
					'threshold' => 'BLOCK_MEDIUM_AND_ABOVE',
				),
				array(
					'category'  => 'HARM_CATEGORY_DANGEROUS_CONTENT',
					'threshold' => 'BLOCK_MEDIUM_AND_ABOVE',
				),
			),
		);

		// Replace {model} placeholder in URL and append API key
		$url = str_replace( '{model}', $model, $this->api_url );
		
		// Ensure URL doesn't already have a query string before adding key
		if ( strpos( $url, '?' ) !== false ) {
			$url .= '&key=' . $this->api_key;
		} else {
			$url = add_query_arg( 'key', $this->api_key, $url );
		}

		// Gemini doesn't use Authorization header, API key is in URL
		$data = $this->make_request( $url, $request_data, array() );

		if ( ! $data ) {
			// error_log( 'Gemini API: No data returned from request' );
			$this->last_error = 'No data returned from Gemini API';
			return false;
		}

		// Check for error in response
		if ( isset( $data['error'] ) ) {
			$error_msg = isset( $data['error']['message'] ) ? $data['error']['message'] : 'Unknown error';
			$error_code = isset( $data['error']['code'] ) ? $data['error']['code'] : 'N/A';
			// error_log( 'Gemini API Error [' . $error_code . ']: ' . $error_msg );
			$this->last_error = $error_msg;
			return false;
		}

		// Extract text from response
		// Check for various possible response structures
		$text = null;
		
		// Standard structure: candidates[0].content.parts[0].text
		if ( isset( $data['candidates'][0]['content']['parts'][0]['text'] ) ) {
			$text = $data['candidates'][0]['content']['parts'][0]['text'];
		}
		// Alternative structure: candidates[0].output
		elseif ( isset( $data['candidates'][0]['output'] ) ) {
			$text = $data['candidates'][0]['output'];
		}
		// Alternative structure: candidates[0].text
		elseif ( isset( $data['candidates'][0]['text'] ) ) {
			$text = $data['candidates'][0]['text'];
		}
		// Check if there's a direct text field
		elseif ( isset( $data['text'] ) ) {
			$text = $data['text'];
		}
		
		if ( empty( $text ) ) {
			// error_log( 'Gemini API: Could not extract text from response structure' );
			return false;
		}

		return array(
			'text' => $text,
		);
	}

	/**
	 * Convert OpenAI-style messages to Gemini format.
	 *
	 * @param array $messages Array of messages.
	 * @return array Gemini-formatted contents.
	 */
	private function convert_messages_to_gemini_format( array $messages ): array {
		$contents = array();
		$system_instruction = '';

		foreach ( $messages as $message ) {
			if ( ! isset( $message['role'] ) || ! isset( $message['content'] ) ) {
				// error_log( 'Gemini API: Invalid message format - ' . wp_json_encode( $message ) );
				continue;
			}
			
			$role = $message['role'];
			$content = $message['content'];

			// Skip empty messages
			if ( empty( $content ) ) {
				continue;
			}

			// Gemini uses system instructions separately
			if ( 'system' === $role ) {
				$system_instruction = $content;
				continue;
			}

			// Convert role names (assistant -> model for Gemini)
			$gemini_role = 'user' === $role ? 'user' : 'model';

			$contents[] = array(
				'role'  => $gemini_role,
				'parts' => array(
					array( 'text' => $content ),
				),
			);
		}

		// Prepend system instruction to first user message if exists
		if ( ! empty( $system_instruction ) && ! empty( $contents ) ) {
			foreach ( $contents as $key => $content ) {
				if ( 'user' === $content['role'] ) {
					$contents[ $key ]['parts'][0]['text'] = $system_instruction . "\n\n" . $contents[ $key ]['parts'][0]['text'];
					break;
				}
			}
		}

		return $contents;
	}

	/**
	 * Override make_request to handle Gemini's URL-based auth and better error handling.
	 *
	 * @param string $url API endpoint URL.
	 * @param array $data Request data.
	 * @param array $headers Additional headers.
	 * @return array|false Response data or false on failure.
	 */
	protected function make_request( string $url, array $data, array $headers = array() ) {
		$json_body = wp_json_encode( $data );
		if ( false === $json_body ) {
			$this->last_error = 'JSON encoding failed';
			// error_log( 'Gemini API: JSON encoding failed' );
			return false;
		}

		$default_headers = array(
			'Content-Type' => 'application/json',
		);

		$args = array(
			'method'  => 'POST',
			'timeout' => 30,
			'headers' => array_merge( $default_headers, $headers ),
			'body'    => $json_body,
		);

		$response = wp_remote_post( $url, $args );

		if ( is_wp_error( $response ) ) {
			$this->last_error = $response->get_error_message();
			// error_log( 'Gemini API Error: ' . $response->get_error_message() );
			return false;
		}

		$response_code = wp_remote_retrieve_response_code( $response );
		$response_body = wp_remote_retrieve_body( $response );

		if ( 200 !== $response_code ) {
			// Try to extract error message from response
			$decoded = json_decode( $response_body, true );
			if ( $decoded && isset( $decoded['error']['message'] ) ) {
				$this->last_error = $decoded['error']['message'];
			} else {
				$this->last_error = "HTTP {$response_code}: " . substr( $response_body, 0, 200 );
			}
			// error_log( "Gemini API Error: HTTP {$response_code} - {$response_body}" );
			return false;
		}

		$decoded = json_decode( $response_body, true );
		if ( null === $decoded ) {
			$this->last_error = 'Failed to decode JSON response';
			// error_log( 'Gemini API: Failed to decode JSON response' );
			return false;
		}

		return $decoded;
	}
}
