<?php
/**
 * Trait Loco_Automatic_Translate_Addon_Pro\AI_Translate\Services\Traits\Generative_AI_API_Client_Trait
 */

namespace Loco_Automatic_Translate_Addon_Pro\AI_Translate\Services\Traits;

use Loco_Automatic_Translate_Addon_Pro\AI_Translate\Services\Exception\Generative_AI_Exception;
use Felix_Arntz\WP_OOP_Plugin_Lib\HTTP\Contracts\Request;	
use Felix_Arntz\WP_OOP_Plugin_Lib\HTTP\Contracts\Response;	
use Felix_Arntz\WP_OOP_Plugin_Lib\HTTP\Exception\Request_Exception;
use Felix_Arntz\WP_OOP_Plugin_Lib\HTTP\HTTP;
use Generator;

/**
 * Trait for an API client class which implements the Generative_AI_API_Client interface.
 */
trait Generative_AI_API_Client_Trait {

	/**
	 * Sends the given request to the API and returns the response data.
	 *
	 * @param Request $request The request instance.
	 * @return Response The response instance.
	 *
	 * @throws Generative_AI_Exception If an error occurs while making the request.
	 */
	final public function make_request( Request $request ): Response {
		$http = $this->get_http();

		try {
			$response = $this->get_http()->request( $request );
		} catch ( Request_Exception $e ) {
			// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
			throw $this->create_request_exception( $e->getMessage() );
		}

		if ( $response->get_status() < 200 || $response->get_status() >= 300 ) {
			$data = $response->get_data();

			if ( $data && isset( $data['error']['message'] ) ) {
				$error_message = $data['error']['message'];
			} else {
				$error_message = sprintf(
					/* translators: %d: HTTP status code */
					__( 'Bad status code: %d', 'ai-services' ),
					$response->get_status()
				);
			}
			// phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
			throw $this->create_request_exception( $error_message );
		}

		return $response;
	}

	/**
	 * Processes the response data from the API.
	 *
	 * @param Response $response         The response instance. Must not be a stream response, i.e. not implement the
	 *                                   With_Stream interface.
	 * @param callable $process_callback The callback to process the response data. Receives the JSON-decoded response
	 *                                   data as associative array and should return the processed data in the desired
	 *                                   format.
	 * @return mixed The processed response data.
	 *
	 * @throws Generative_AI_Exception If an error occurs while processing the response data.
	 */
	final public function process_response_data( Response $response, $process_callback ) {
		$data = $response->get_data();
		if ( ! $data ) {
			throw new Generative_AI_Exception(
				esc_html__( 'No data received in response.', 'ai-services' )
			);
		}

		$processed_data = call_user_func( $process_callback, $data );
		if ( ! $processed_data ) {
			throw new Generative_AI_Exception(
				esc_html__( 'No data returned by process callback.', 'ai-services' )
			);
		}

		return $processed_data;
	}

	/**
	 * Creates a new exception for an AI API request error.
	 *
	 * @param string $message The error message to include in the exception.
	 * @return Generative_AI_Exception The exception instance.
	 */
	final public function create_request_exception( string $message ): Generative_AI_Exception {
		return new Generative_AI_Exception(
			esc_html(
				sprintf(
					/* translators: 1: API name, 2: error message */
					__( 'Error while making request to the %1$s API: %2$s ', 'ai-services' ),
					$this->get_api_name(),
					$message
				)
			)
		);
	}

	/**
	 * Creates a new exception for an AI API response error.
	 *
	 * @param string $message The error message to include in the exception.
	 * @return Generative_AI_Exception The exception instance.
	 */
	final public function create_response_exception( string $message ): Generative_AI_Exception {
		return new Generative_AI_Exception(
			esc_html(
				sprintf(
					/* translators: 1: API name, 2: error message */
					__( 'Error in the response from the %1$s API: %2$s ', 'ai-services' ),
					$this->get_api_name(),
					$message
				)
			)
		);
	}

	/**
	 * Creates a new exception for an AI API response error for a missing key.
	 *
	 * @param string $key The missing key in the response data.
	 * @return Generative_AI_Exception The exception instance.
	 */
	final public function create_missing_response_key_exception( string $key ): Generative_AI_Exception {
		return $this->create_response_exception(
			sprintf(
				/* translators: %s: key name */
				__( 'The response is missing the "%s" key.', 'ai-services' ),
				$key
			)
		);
	}

	/**
	 * Adds default options to the given request.
	 *
	 * @param Request $request The request instance to add the options to.
	 */
	final protected function add_default_options( Request $request ): void {
		$options = $request->get_options();
		if ( ! isset( $options['timeout'] ) ) {
			$request->add_option( 'timeout', 120 );
		}
	}

	/**
	 * Returns the HTTP instance to use for requests.
	 *
	 * @return HTTP The HTTP instance.
	 */
	abstract protected function get_http(): HTTP;

	/**
	 * Returns the human readable API name (without the "API" suffix).
	 *
	 * @return string The API name.
	 */
	abstract protected function get_api_name(): string;
}
