<?php
/**
 * Google Gemini AI provider.
 *
 * @package MasterQuiz
 * @since   1.0.0
 */

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

/**
 * Class MasterQuiz_Gemini_Provider
 *
 * Implements AI calls to the Google Gemini API using wp_remote_post().
 * Supports structured JSON output via responseSchema, with fallback to
 * unstructured prompting when the model returns 400.
 *
 * @since 1.0.0
 */
class MasterQuiz_Gemini_Provider extends MasterQuiz_Ai_Provider {

	/**
	 * Gemini API base URLs (v1 + v1beta fallback).
	 *
	 * @var array
	 */
	private $base_urls = array(
		// v1beta is required: v1 endpoint does not support thinkingConfig,
		// responseMimeType, or responseSchema fields that this plugin uses.
		'https://generativelanguage.googleapis.com/v1beta/models/',
	);

	/**
	 * Whether to disable thinking for speed (set by provider chain for fast mode).
	 *
	 * @var bool
	 */
	private $disable_thinking = false;

	/**
	 * Set whether to disable Gemini thinking for faster responses.
	 *
	 * @since 1.0.1
	 *
	 * @param bool $disable True to disable thinking.
	 */
	public function set_disable_thinking( $disable ) {
		$this->disable_thinking = (bool) $disable;
	}

	/**
	 * @inheritDoc
	 */
	public function call( $prompt, $schema = array(), $timeout = 60, $model = '' ) {
		if ( ! $this->has_api_key() ) {
			return new WP_Error( 'missing_key', esc_html__( 'Gemini API key is not configured.', 'masterquiz-ai' ) );
		}

		if ( empty( $model ) ) {
			$models = $this->get_models();
			$model  = $models[0];
		}

		$start_time = microtime( true );

		// Build structured payload (schema-guided output).
		$structured_payload = array(
			'contents'         => array(
				array(
					'parts' => array(
						array( 'text' => $prompt ),
					),
				),
			),
			'generationConfig' => array(
				'temperature' => 0.3,
			),
		);

		// Disable thinking for fast mode — saves 10-30s per call.
		if ( $this->disable_thinking ) {
			$structured_payload['generationConfig']['thinkingConfig'] = array(
				'thinkingBudget' => 0,
			);
		}

		if ( ! empty( $schema ) ) {
			$structured_payload['generationConfig']['responseMimeType'] = 'application/json';
			$structured_payload['generationConfig']['responseSchema']   = $schema;
		}

		// Build fallback payload (unstructured, schema in prompt).
		$fallback_prompt  = 'Return ONLY JSON matching this schema: ' . wp_json_encode( $schema ) . "\n" . $prompt;
		$fallback_payload = array(
			'contents'         => array(
				array(
					'parts' => array(
						array( 'text' => $fallback_prompt ),
					),
				),
			),
			'generationConfig' => array(
				'temperature' => 0.3,
			),
		);

		if ( $this->disable_thinking ) {
			$fallback_payload['generationConfig']['thinkingConfig'] = array(
				'thinkingBudget' => 0,
			);
		}

		// Try each base URL (v1 first, then v1beta).
		$last_error = null;
		foreach ( $this->base_urls as $base_url ) {
			$url = $base_url . $model . ':generateContent?key=' . $this->api_key;

			// Attempt 1: Structured output.
			$result = $this->make_request( $url, $structured_payload, $timeout );

			if ( is_wp_error( $result ) ) {
				$last_error = $result;
				$code       = $result->get_error_data();

				// If 400 (schema not supported), try unstructured fallback.
				if ( 400 === $code && ! empty( $schema ) ) {
					$result = $this->make_request( $url, $fallback_payload, $timeout );
					if ( is_wp_error( $result ) ) {
						$last_error = $result;
					}
				}
			}

			if ( ! is_wp_error( $result ) ) {
				$elapsed = round( microtime( true ) - $start_time, 2 );
				MasterQuiz_Logger::info( 'Gemini call completed.', array( 'model' => $model, 'seconds' => $elapsed ) );
				return $result;
			}
		}

		$elapsed   = round( microtime( true ) - $start_time, 2 );
		$error_msg = $last_error ? $last_error->get_error_message() : 'Unknown error';
		MasterQuiz_Logger::warning( 'Gemini call failed.', array( 'model' => $model, 'error' => $error_msg, 'seconds' => $elapsed ) );

		// Return the actual API error for better debugging.
		return $last_error ? $last_error : new WP_Error(
			'gemini_failed',
			/* translators: %s: model name */
			sprintf( esc_html__( 'Gemini API call failed for model %s.', 'masterquiz-ai' ), $model )
		);
	}

	/**
	 * Execute a single HTTP request to the Gemini API.
	 *
	 * @since 1.0.0
	 *
	 * @param string $url     Full Gemini endpoint URL.
	 * @param array  $payload Request body.
	 * @param int    $timeout Timeout in seconds.
	 * @return string|WP_Error Response text or error (with HTTP code as error data).
	 */
	private function make_request( $url, $payload, $timeout ) {
		$response = wp_remote_post(
			$url,
			array(
				'timeout' => $timeout,
				'headers' => array( 'Content-Type' => 'application/json' ),
				'body'    => wp_json_encode( $payload ),
			)
		);

		if ( is_wp_error( $response ) ) {
			return new WP_Error( 'request_failed', $response->get_error_message(), 0 );
		}

		$code = wp_remote_retrieve_response_code( $response );
		$body = json_decode( wp_remote_retrieve_body( $response ), true );

		if ( $code < 200 || $code >= 300 ) {
			$msg = isset( $body['error']['message'] ) ? $body['error']['message'] : 'HTTP ' . $code;
			return new WP_Error( 'api_error', $msg, $code );
		}

		if ( isset( $body['candidates'][0]['content']['parts'][0]['text'] ) ) {
			return $body['candidates'][0]['content']['parts'][0]['text'];
		}

		return new WP_Error( 'invalid_response', esc_html__( 'Invalid Gemini response structure.', 'masterquiz-ai' ), $code );
	}

	/**
	 * @inheritDoc
	 */
	public function get_provider_name() {
		return 'gemini';
	}

	/**
	 * @inheritDoc
	 */
	public function get_models() {
		return array(
			'gemini-2.5-flash',
			'gemini-2.5-flash-lite',
			'gemini-2.5-pro',
		);
	}

	/**
	 * @inheritDoc
	 */
	public function test_connection() {
		if ( ! $this->has_api_key() ) {
			return new WP_Error( 'missing_key', esc_html__( 'API key not provided.', 'masterquiz-ai' ) );
		}

		$result = $this->call( 'Respond with the JSON: {"status":"ok"}', array(), 15, 'gemini-2.5-flash-lite' );

		if ( is_wp_error( $result ) ) {
			return $result;
		}

		return true;
	}
}
