<?php
/**
 * Abstract AI provider.
 *
 * @package CatalogPilot\AiStudio
 */

namespace CatalogPilot\AiStudio\Providers;

defined( 'ABSPATH' ) || exit;

abstract class Provider {

	protected $api_key;
	protected $model;

	public function __construct( $api_key, $model ) {
		$this->api_key = (string) $api_key;
		$this->model   = (string) $model;
	}

	public function has_key() {
		return '' !== trim( (string) $this->api_key );
	}

	abstract public function id();

	/**
	 * @param string $system System prompt.
	 * @param string $user   User prompt.
	 * @return array|\WP_Error { text: string, usage: array }
	 */
	abstract public function complete( $system, $user );

	abstract public function test_connection();

	/**
	 * Multimodal completion with image input. Default: not supported.
	 *
	 * @param string $system     System prompt.
	 * @param string $user       User prompt text.
	 * @param array  $image_urls Publicly reachable image URLs.
	 */
	public function complete_vision( $system, $user, array $image_urls ) {
		return new \WP_Error( 'cpais_no_vision', __( 'This provider does not support vision input.', 'catalogpilot-ai-studio-for-woocommerce' ) );
	}

	protected function request( $url, $args ) {
		$args = wp_parse_args(
			$args,
			array(
				'timeout' => 60,
				'headers' => array(),
			)
		);

		$max_retries = 3;
		$attempt     = 0;
		$last_error  = null;

		while ( $attempt < $max_retries ) {
			$response = wp_remote_post( $url, $args );

			if ( is_wp_error( $response ) ) {
				$last_error = $response;
				// Network error — backoff and retry.
				sleep( min( 30, pow( 2, $attempt ) ) );
				$attempt++;
				continue;
			}

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

			if ( 429 === $code || ( $code >= 500 && $code < 600 ) ) {
				$retry_after = (int) wp_remote_retrieve_header( $response, 'retry-after' );
				$wait        = $retry_after > 0 ? min( 60, $retry_after ) : min( 30, pow( 2, $attempt ) );
				$last_error  = new \WP_Error(
					'cpais_provider_error',
					isset( $data['error']['message'] ) ? $data['error']['message'] : sprintf( 'HTTP %d', $code ),
					array( 'status' => $code )
				);
				sleep( $wait );
				$attempt++;
				continue;
			}

			if ( $code < 200 || $code >= 300 ) {
				$message = isset( $data['error']['message'] ) ? $data['error']['message'] : sprintf( 'HTTP %d', $code );
				return new \WP_Error( 'cpais_provider_error', $message, array( 'status' => $code ) );
			}

			return $data;
		}

		return $last_error ?: new \WP_Error( 'cpais_provider_error', __( 'Provider request failed after retries.', 'catalogpilot-ai-studio-for-woocommerce' ) );
	}
}

function cpais_factory( $provider_id, $api_key, $model ) {
	switch ( $provider_id ) {
		case 'openai':
			return new OpenAI_Provider( $api_key, $model );
		case 'anthropic':
			return new Anthropic_Provider( $api_key, $model );
		case 'gemini':
			return new Gemini_Provider( $api_key, $model );
		case 'mistral':
			return new Mistral_Provider( $api_key, $model );
		case 'deepseek':
			return new DeepSeek_Provider( $api_key, $model );
		case 'grok':
			return new Grok_Provider( $api_key, $model );
		case 'groq':
			return new Groq_Provider( $api_key, $model );
		case 'openrouter':
			return new OpenRouter_Provider( $api_key, $model );
		case 'perplexity':
			return new Perplexity_Provider( $api_key, $model );
	}
	return new \WP_Error( 'cpais_unknown_provider', __( 'Unknown provider.', 'catalogpilot-ai-studio-for-woocommerce' ) );
}

function cpais_text_providers_definitions() {
	return array(
		'openai' => array(
			'label'  => 'OpenAI',
			'models' => array( 'gpt-4o-mini', 'gpt-4o', 'gpt-4.1-mini', 'gpt-4.1' ),
		),
		'anthropic' => array(
			'label'  => 'Anthropic',
			'models' => array( 'claude-haiku-4-5', 'claude-sonnet-4-5', 'claude-opus-4-5' ),
		),
		'gemini' => array(
			'label'  => 'Google Gemini',
			'models' => array( 'gemini-2.5-flash', 'gemini-2.5-pro', 'gemini-2.0-flash', 'gemini-2.0-flash-lite' ),
		),
		'mistral' => array(
			'label'  => 'Mistral',
			'models' => array( 'mistral-large-latest', 'mistral-medium-latest', 'mistral-small-latest', 'ministral-8b-latest', 'ministral-3b-latest' ),
		),
		'deepseek' => array(
			'label'  => 'DeepSeek',
			'models' => array( 'deepseek-chat', 'deepseek-reasoner' ),
		),
		'grok' => array(
			'label'  => 'xAI Grok',
			'models' => array( 'grok-4', 'grok-4-mini', 'grok-3', 'grok-3-mini' ),
		),
		'groq' => array(
			'label'  => 'Groq (fast inference)',
			'models' => array(
				'llama-3.3-70b-versatile',
				'llama-3.1-8b-instant',
				'gemma2-9b-it',
				'deepseek-r1-distill-llama-70b',
			),
		),
		'openrouter' => array(
			'label'  => 'OpenRouter',
			'models' => array(
				'openai/gpt-4o-mini',
				'openai/gpt-4o',
				'anthropic/claude-sonnet-4.5',
				'anthropic/claude-haiku-4.5',
				'google/gemini-2.5-flash',
				'google/gemini-2.5-pro',
				'meta-llama/llama-3.3-70b-instruct',
				'mistralai/mistral-large',
				'deepseek/deepseek-chat',
				'deepseek/deepseek-r1',
				'x-ai/grok-4',
			),
		),
		'perplexity' => array(
			'label'  => 'Perplexity',
			'models' => array( 'sonar-pro', 'sonar', 'sonar-reasoning-pro', 'sonar-reasoning' ),
		),
	);
}
