<?php
/**
 * Abstract AI Provider Class
 *
 * Base class for all AI providers
 *
 * @package SmartyPress_AI_Engine
 * @since 1.0.0
 */

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

/**
 * Abstract AI Provider class
 */
abstract class SmartyPress_AI_AI_Provider {

	/**
	 * API Key
	 *
	 * @var string
	 */
	protected $api_key;

	/**
	 * Model name
	 *
	 * @var string
	 */
	protected $model;

	/**
	 * API endpoint URL
	 *
	 * @var string
	 */
	protected $api_endpoint;

	/**
	 * Proxy URL (optional)
	 *
	 * @var string
	 */
	protected $proxy_url = '';

	/**
	 * Constructor
	 *
	 * @param string $api_key API key.
	 * @param string $model Model name.
	 */
	public function __construct( $api_key, $model ) {
		$this->api_key = $api_key;
		$this->model   = $model;
	}

	/**
	 * Set proxy URL
	 *
	 * @param string $proxy_url Proxy URL.
	 */
	public function set_proxy( $proxy_url ) {
		$this->proxy_url = $proxy_url;
	}

	/**
	 * Generate content using AI
	 *
	 * @param string $prompt The prompt to send to AI.
	 * @param array  $params Additional parameters.
	 * @return array Response with content or error.
	 */
	abstract public function generate_content( $prompt, $params = array() );

	/**
	 * Validate API key
	 *
	 * @return bool True if valid, false otherwise.
	 */
	abstract public function validate_api_key();

	/**
	 * Get available models
	 *
	 * @return array List of available models.
	 */
	abstract public function get_available_models();

	/**
	 * Get provider name for logging
	 *
	 * @return string Provider name.
	 */
	public function get_provider_name() {
		$class_name = get_class( $this );
		// Extract provider name from class name (e.g., 'SmartyPress_AI_OpenAI_Provider' -> 'openai')
		if ( preg_match( '/SmartyPress_AI_(\w+)_Provider/', $class_name, $matches ) ) {
			return strtolower( $matches[1] );
		}
		return strtolower( str_replace( array( 'SmartyPress_AI_', '_Provider' ), '', $class_name ) );
	}

	/**
	 * Make API request
	 *
	 * @param array $messages Messages to send.
	 * @param array $params Additional parameters.
	 * @return array Response data.
	 */
	protected function make_request( $messages, $params = array() ) {
		$request_timeout = ! empty( $params['request_timeout'] ) ? absint( $params['request_timeout'] ) : 60;
		unset( $params['request_timeout'] );

		$default_params = array(
			'model'       => $this->model,
			'messages'    => $messages,
			'temperature' => 0.7,
		);

		$body = wp_parse_args( $params, $default_params );

		$request_args = array(
			'headers' => array(
				'Content-Type'  => 'application/json',
				'Authorization' => 'Bearer ' . $this->api_key,
			),
			'body'    => wp_json_encode( $body ),
			'timeout' => $request_timeout,
		);

		// Add proxy support if configured
		if ( ! empty( $this->proxy_url ) ) {
			// Parse proxy URL to extract components
			$proxy_parts = wp_parse_url( $this->proxy_url );
			if ( $proxy_parts ) {
				$proxy_host = $proxy_parts['host'] ?? '';
				$proxy_port = $proxy_parts['port'] ?? '';

				if ( $proxy_host && $proxy_port ) {
					// WordPress HTTP API proxy format
					$request_args['proxy'] = $proxy_host . ':' . $proxy_port;

					// Add authentication if present
					if ( ! empty( $proxy_parts['user'] ) ) {
						$proxy_auth = $proxy_parts['user'];
						if ( ! empty( $proxy_parts['pass'] ) ) {
							$proxy_auth .= ':' . $proxy_parts['pass'];
						}
						$request_args['headers']['Proxy-Authorization'] = 'Basic ' . base64_encode( $proxy_auth ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
					}
				}
			}
		}

		$response = wp_remote_post(
			$this->api_endpoint,
			$request_args
		);

		if ( is_wp_error( $response ) ) {
			$error_msg = $response->get_error_message();

			// Log the error.
			if ( class_exists( 'SmartyPress_AI_Logger' ) ) {
				SmartyPress_AI_Logger::log_api_error(
					$this->get_provider_name(),
					$this->api_endpoint,
					$body,
					array(
						'code' => 0,
						'body' => $error_msg,
					),
					$error_msg,
					'wp_error'
				);
			}

			return array(
				'success' => false,
				'error'   => $error_msg,
			);
		}

		$response_code = wp_remote_retrieve_response_code( $response );
		$response_body = wp_remote_retrieve_body( $response );
		$data          = json_decode( $response_body, true );

		if ( $response_code !== 200 ) {
			$error_message = $data['error']['message'] ?? __( 'Unknown API error', 'smartypress-ai-engine' );
			$error_code    = '';

			// Detect authentication errors (401, 403, or message containing auth-related keywords)
			if ( in_array( $response_code, array( 401, 403 ), true )
				|| stripos( $error_message, 'authentication' ) !== false
				|| stripos( $error_message, 'invalid' ) !== false
				|| stripos( $error_message, 'api key' ) !== false
				|| stripos( $error_message, 'unauthorized' ) !== false
			) {
				$error_code        = 'api_key_invalid';
				$user_error_message = __( 'API key is invalid or has been revoked. Please check or replace your API key in SmartyPress AI > Settings.', 'smartypress-ai-engine' );
			} elseif ( $response_code === 429 || stripos( $error_message, 'rate limit' ) !== false ) {
				$error_code        = 'rate_limit';
				$user_error_message = $error_message;
			} elseif ( stripos( $error_message, 'quota' ) !== false || stripos( $error_message, 'billing' ) !== false ) {
				$error_code        = 'quota_exceeded';
				$user_error_message = $error_message;
			} else {
				$user_error_message = $error_message;
			}

			// Log the error.
			if ( class_exists( 'SmartyPress_AI_Logger' ) ) {
				SmartyPress_AI_Logger::log_api_error(
					$this->get_provider_name(),
					$this->api_endpoint,
					$body,
					array(
						'code' => $response_code,
						'body' => $response_body,
					),
					$error_message,
					$error_code
				);
			}

			if ( ! empty( $error_code ) && 'api_key_invalid' === $error_code ) {
				return array(
					'success'    => false,
					'error'      => $user_error_message,
					'error_code' => $error_code,
				);
			}

			return array(
				'success'    => false,
				'error'      => $user_error_message,
				'error_code' => $error_code,
			);
		}

		// Log successful request if debug logging is enabled.
		if ( class_exists( 'SmartyPress_AI_Logger' ) ) {
			SmartyPress_AI_Logger::log_api_success(
				$this->get_provider_name(),
				$this->api_endpoint,
				$body,
				array(
					'code'    => $response_code,
					'content' => $data['choices'][0]['message']['content'] ?? '',
					'usage'   => $data['usage'] ?? array(),
				)
			);
		}

		$usage         = $data['usage'] ?? array();
		$choice        = $data['choices'][0] ?? array();
		$finish_reason = is_array( $choice ) ? sanitize_text_field( $choice['finish_reason'] ?? '' ) : '';
		$response_model = sanitize_text_field( $data['model'] ?? ( $body['model'] ?? $this->model ) );

		// Record token usage for statistics.
		if ( class_exists( 'SmartyPress_AI_Token_Tracker' ) ) {
			SmartyPress_AI_Token_Tracker::record( array(
				'provider' => $this->get_provider_name(),
				'model'    => $body['model'] ?? $this->model,
				'usage'    => $usage,
			) );
		}

		return array(
			'success'       => true,
			'content'       => $data['choices'][0]['message']['content'] ?? '',
			'usage'         => $usage,
			'finish_reason' => $finish_reason,
			'model'         => $response_model,
		);
	}

	/**
	 * Build messages array
	 *
	 * @param string $system_prompt System prompt.
	 * @param string $user_prompt User prompt.
	 * @return array Messages array.
	 */
	protected function build_messages( $system_prompt, $user_prompt ) {
		return array(
			array(
				'role'    => 'system',
				'content' => $system_prompt,
			),
			array(
				'role'    => 'user',
				'content' => $user_prompt,
			),
		);
	}

	/**
	 * Generate image using AI
	 *
	 * Override in child class to support image generation.
	 *
	 * @param string $prompt Image generation prompt.
	 * @param array  $params Additional parameters (model, resolution).
	 * @return array Response with image data or error.
	 */
	public function generate_image( $prompt, $params = array() ) {
		return array(
			'success' => false,
			'error'   => __( 'Image generation is not supported by this provider.', 'smartypress-ai-engine' ),
		);
	}

	/**
	 * Get available image models
	 *
	 * Override in child class to return supported image models.
	 *
	 * @return array List of available image models with resolutions.
	 */
	public function get_available_image_models() {
		return array();
	}

	/**
	 * Make API request to a custom endpoint
	 *
	 * Used for image generation and other non-chat endpoints.
	 *
	 * @param string $endpoint API endpoint URL.
	 * @param array  $body     Request body.
	 * @param int    $timeout  Request timeout in seconds.
	 * @return array Response data.
	 */
	protected function make_api_request( $endpoint, $body, $timeout = 120 ) {
		$request_args = array(
			'headers' => array(
				'Content-Type'  => 'application/json',
				'Authorization' => 'Bearer ' . $this->api_key,
			),
			'body'    => wp_json_encode( $body ),
			'timeout' => $timeout,
		);

		// Add proxy support if configured.
		if ( ! empty( $this->proxy_url ) ) {
			$proxy_parts = wp_parse_url( $this->proxy_url );
			if ( $proxy_parts ) {
				$proxy_host = $proxy_parts['host'] ?? '';
				$proxy_port = $proxy_parts['port'] ?? '';

				if ( $proxy_host && $proxy_port ) {
					$request_args['proxy'] = $proxy_host . ':' . $proxy_port;

					if ( ! empty( $proxy_parts['user'] ) ) {
						$proxy_auth = $proxy_parts['user'];
						if ( ! empty( $proxy_parts['pass'] ) ) {
							$proxy_auth .= ':' . $proxy_parts['pass'];
						}
						$request_args['headers']['Proxy-Authorization'] = 'Basic ' . base64_encode( $proxy_auth ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
					}
				}
			}
		}

		$response = wp_remote_post( $endpoint, $request_args );

		if ( is_wp_error( $response ) ) {
			$error_msg = $response->get_error_message();

			if ( class_exists( 'SmartyPress_AI_Logger' ) ) {
				SmartyPress_AI_Logger::log_api_error(
					$this->get_provider_name(),
					$endpoint,
					$body,
					array(
						'code' => 0,
						'body' => $error_msg,
					),
					$error_msg,
					'wp_error'
				);
			}

			return array(
				'success' => false,
				'error'   => $error_msg,
			);
		}

		$response_code = wp_remote_retrieve_response_code( $response );
		$response_body = wp_remote_retrieve_body( $response );
		$data          = json_decode( $response_body, true );

		if ( $response_code !== 200 ) {
			$error_message = $data['error']['message'] ?? __( 'Unknown API error', 'smartypress-ai-engine' );

			if ( class_exists( 'SmartyPress_AI_Logger' ) ) {
				SmartyPress_AI_Logger::log_api_error(
					$this->get_provider_name(),
					$endpoint,
					$body,
					array(
						'code' => $response_code,
						'body' => $response_body,
					),
					$error_message
				);
			}

			return array(
				'success' => false,
				'error'   => $error_message,
			);
		}

		// Record token usage for image API calls.
		if ( class_exists( 'SmartyPress_AI_Token_Tracker' ) && ! empty( $data['usage'] ) ) {
			SmartyPress_AI_Token_Tracker::record( array(
				'provider' => $this->get_provider_name(),
				'model'    => $body['model'] ?? $this->model,
				'usage'    => $data['usage'],
			) );
		}

		return array(
			'success' => true,
			'data'    => $data,
		);
	}

	/**
	 * Detect image MIME type from file contents, with extension fallback.
	 *
	 * Some providers validate that the data URL MIME matches the actual image
	 * bytes, so relying only on the filename extension can break vision calls.
	 *
	 * @param string $image_path Image path.
	 * @return string
	 */
	protected function detect_image_mime_type( $image_path ) {
		$mime = '';

		if ( function_exists( 'wp_get_image_mime' ) ) {
			$mime = (string) wp_get_image_mime( $image_path );
		}

		if ( '' === $mime && function_exists( 'finfo_open' ) ) {
			$finfo = finfo_open( FILEINFO_MIME_TYPE );
			if ( $finfo ) {
				$mime = (string) finfo_file( $finfo, $image_path );
				finfo_close( $finfo );
			}
		}

		if ( '' === $mime && function_exists( 'mime_content_type' ) ) {
			$mime = (string) mime_content_type( $image_path );
		}

		if ( '' === $mime || 0 !== strpos( $mime, 'image/' ) ) {
			$filetype = wp_check_filetype( $image_path, null );
			$mime     = $filetype['type'] ?: '';
		}

		return ( '' !== $mime && 0 === strpos( $mime, 'image/' ) ) ? $mime : 'image/jpeg';
	}
}

