<?php
/**
 * AI provider cascade chain.
 *
 * @package MasterQuiz
 * @since   1.0.0
 */

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

/**
 * Class MasterQuiz_Provider_Chain
 *
 * Manages cascading AI provider calls: Gemini -> OpenRouter -> Groq.
 * Each provider tries all its models before falling back to the next provider.
 *
 * @since 1.0.0
 */
class MasterQuiz_Provider_Chain {

	/**
	 * The model that was used for the last successful call.
	 *
	 * @var string
	 */
	private $used_model = '';

	/**
	 * Attempt log for debugging.
	 *
	 * @var array
	 */
	private $attempts = array();

	/**
	 * Make a cascading API call through available providers.
	 *
	 * Tries Gemini models first, then OpenRouter models, then Groq models.
	 * In fast mode, only the first Gemini model is tried before moving on.
	 *
	 * @since 1.0.0
	 *
	 * @param string $prompt  The prompt to send.
	 * @param array  $schema  Optional JSON schema for structured output.
	 * @param string $mode    Quiz mode: 'fast' or 'quality'.
	 * @param string $phase   Phase identifier for logging.
	 * @return string|WP_Error Raw response text or error with all attempts.
	 */
	public function call( $prompt, $schema = array(), $mode = 'fast', $phase = '' ) {
		$this->attempts = array();
		$is_fast        = ( 'fast' === $mode );
		$gemini_timeout = $is_fast ? 30 : 60;
		$other_timeout  = $is_fast ? 15 : 60;

		// 1. Try Gemini models.
		$gemini_key = MasterQuiz_Settings::get_api_key( 'gemini' );
		if ( ! empty( $gemini_key ) ) {
			$gemini   = new MasterQuiz_Gemini_Provider( $gemini_key );
			$models   = $gemini->get_models();
			// Fast mode: prefer flash-lite — significantly faster responses.
			$try_list = $is_fast ? array( 'gemini-2.5-flash-lite' ) : $models;

			// Disable thinking in fast mode for significantly faster responses.
			if ( $is_fast ) {
				$gemini->set_disable_thinking( true );
			}

			foreach ( $try_list as $model ) {
				$result = $gemini->call( $prompt, $schema, $gemini_timeout, $model );

				if ( ! is_wp_error( $result ) ) {
					$this->used_model = 'gemini/' . $model;
					MasterQuiz_Logger::info( 'Provider chain success.', array( 'model' => $this->used_model, 'phase' => $phase ) );
					return $result;
				}

				$this->attempts[] = array(
					'phase'    => $phase,
					'provider' => 'gemini',
					'model'    => $model,
					'error'    => $result->get_error_message(),
				);
			}
		}

		// 2. Try Groq models (fast fallback — typically quicker than OpenRouter).
		$groq_key = MasterQuiz_Settings::get_api_key( 'groq' );
		if ( ! empty( $groq_key ) ) {
			$groq       = new MasterQuiz_Groq_Provider( $groq_key );
			$models     = $groq->get_models();
			$groq_list  = $is_fast ? array_slice( $models, 0, 1 ) : $models;

			foreach ( $groq_list as $model ) {
				$result = $groq->call( $prompt, $schema, $other_timeout, $model );

				if ( ! is_wp_error( $result ) ) {
					$this->used_model = 'groq/' . $model;
					MasterQuiz_Logger::info( 'Provider chain success.', array( 'model' => $this->used_model, 'phase' => $phase ) );
					return $result;
				}

				$this->attempts[] = array(
					'phase'    => $phase,
					'provider' => 'groq',
					'model'    => $model,
					'error'    => $result->get_error_message(),
				);
			}
		}

		// 3. Try OpenRouter models.
		$openrouter_key = MasterQuiz_Settings::get_api_key( 'openrouter' );
		if ( ! empty( $openrouter_key ) ) {
			$openrouter = new MasterQuiz_Openrouter_Provider( $openrouter_key );
			$models     = $openrouter->get_models();
			$or_list    = $is_fast ? array_slice( $models, 0, 1 ) : $models;

			foreach ( $or_list as $model ) {
				$result = $openrouter->call( $prompt, $schema, $other_timeout, $model );

				if ( ! is_wp_error( $result ) ) {
					$this->used_model = $model;
					MasterQuiz_Logger::info( 'Provider chain success.', array( 'model' => $this->used_model, 'phase' => $phase ) );
					return $result;
				}

				$this->attempts[] = array(
					'phase'    => $phase,
					'provider' => 'openrouter',
					'model'    => $model,
					'error'    => $result->get_error_message(),
				);
			}
		}

		// All providers failed — technical details already logged above.
		MasterQuiz_Logger::error( 'All providers failed.', array( 'phase' => $phase, 'attempts' => $this->attempts ) );

		// Build a user-friendly message based on the failure reason.
		$last_error = end( $this->attempts );
		$hint       = isset( $last_error['error'] ) ? $last_error['error'] : '';

		if ( false !== stripos( $hint, 'quota' ) || false !== stripos( $hint, 'rate' ) ) {
			$message = esc_html__( 'AI service rate limit reached. Please wait a moment and try again.', 'masterquiz-ai' );
		} elseif ( false !== stripos( $hint, 'invalid' ) || false !== stripos( $hint, 'api key' ) || false !== stripos( $hint, 'unauthorized' ) ) {
			$message = esc_html__( 'AI service authentication failed. Please check your API keys in Settings.', 'masterquiz-ai' );
		} else {
			$message = esc_html__( 'Quiz generation failed. The AI service is temporarily unavailable — please try again.', 'masterquiz-ai' );
		}

		return new WP_Error( 'all_providers_failed', $message );
	}

	/**
	 * Test a specific provider connection.
	 *
	 * @since 1.0.0
	 *
	 * @param string $provider Provider slug (gemini, openrouter, groq).
	 * @param string $api_key  API key to test.
	 * @return true|WP_Error True on success, WP_Error on failure.
	 */
	public function test_provider( $provider, $api_key ) {
		switch ( $provider ) {
			case 'gemini':
				$p = new MasterQuiz_Gemini_Provider( $api_key );
				break;
			case 'openrouter':
				$p = new MasterQuiz_Openrouter_Provider( $api_key );
				break;
			case 'groq':
				$p = new MasterQuiz_Groq_Provider( $api_key );
				break;
			default:
				return new WP_Error(
					'invalid_provider',
					esc_html__( 'Unknown provider.', 'masterquiz-ai' )
				);
		}

		return $p->test_connection();
	}

	/**
	 * Get the model used in the last successful call.
	 *
	 * @since 1.0.0
	 *
	 * @return string Model identifier.
	 */
	public function get_used_model() {
		return $this->used_model;
	}

	/**
	 * Get the attempt log from the last call.
	 *
	 * @since 1.0.0
	 *
	 * @return array Array of attempt records.
	 */
	public function get_attempts() {
		return $this->attempts;
	}
}
