<?php
/**
 * API client foundation class.
 *
 * @package Pendid_AI_Studio
 */

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

/**
 * Handles future Pendid API requests.
 */
class Pendid_AI_Studio_API_Client {

	/**
	 * Pendid API base URL.
	 *
	 * @var string
	 */
	const API_BASE_URL = 'https://studio.pendid.com/api/wp';

	/**
	 * Pendid manage account URL.
	 *
	 * @var string
	 */
	const MANAGE_ACCOUNT_URL = 'https://studio.pendid.com/plans';

	/**
	 * Pendid account URL.
	 *
	 * @var string
	 */
	const ACCOUNT_URL = 'https://studio.pendid.com/account';

	/**
	 * Returns the manage account URL.
	 *
	 * @return string
	 */
	public function get_manage_account_url() {
		return esc_url_raw( self::MANAGE_ACCOUNT_URL );
	}

	/**
	 * Returns the Pendid account URL.
	 *
	 * @return string
	 */
	public function get_account_url() {
		return esc_url_raw( self::ACCOUNT_URL );
	}

	/**
	 * Exchanges a short-lived connection code for a plugin token.
	 *
	 * @param string $code Pendid connection code.
	 * @return array<string, mixed>
	 */
	public function exchange_connection_code( $code ) {
		$code = is_scalar( $code ) ? sanitize_text_field( (string) $code ) : '';

		if ( '' === $code ) {
			return array(
				'ok'    => false,
				'error' => __( 'Connection code is required.', 'pendid-ai-studio' ),
			);
		}

		$response = wp_remote_post(
			esc_url_raw( self::API_BASE_URL . '/connect/exchange' ),
			array(
				'timeout' => 30,
				'headers' => array(
					'Accept'       => 'application/json',
					'Content-Type' => 'application/json',
				),
				'body'    => wp_json_encode(
					array(
						'code' => $code,
					)
				),
			)
		);

		if ( is_wp_error( $response ) ) {
			return array(
				'ok'    => false,
				'error' => __( 'Unable to reach Pendid Studio.', 'pendid-ai-studio' ),
			);
		}

		$status_code = (int) wp_remote_retrieve_response_code( $response );

		if ( 200 > $status_code || 299 < $status_code ) {
			return array(
				'ok'    => false,
				'error' => __( 'Unable to complete connection.', 'pendid-ai-studio' ),
			);
		}

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

		if ( JSON_ERROR_NONE !== json_last_error() || ! is_array( $data ) ) {
			return array(
				'ok'    => false,
				'error' => __( 'Invalid connection response.', 'pendid-ai-studio' ),
			);
		}

		if ( empty( $data['ok'] ) || empty( $data['token'] ) || ! is_scalar( $data['token'] ) ) {
			return array(
				'ok'    => false,
				'error' => __( 'Unable to complete connection.', 'pendid-ai-studio' ),
			);
		}

		$account = isset( $data['account'] ) && is_array( $data['account'] ) ? $data['account'] : array();

		return array(
			'ok'      => true,
			'token'   => sanitize_text_field( (string) $data['token'] ),
			'account' => $this->sanitize_response_data( $account ),
		);
	}

	/**
	 * Revokes a stored plugin access token in Pendid Studio.
	 *
	 * @param string $access_token Stored account access token.
	 * @return array<string, mixed>
	 */
	public function revoke_token( $access_token ) {
		$access_token = is_scalar( $access_token ) ? sanitize_text_field( (string) $access_token ) : '';

		if ( '' === $access_token ) {
			return array(
				'ok'    => false,
				'error' => __( 'Could not revoke the remote connection.', 'pendid-ai-studio' ),
			);
		}

		$response = wp_remote_post(
			esc_url_raw( self::API_BASE_URL . '/revoke' ),
			array(
				'timeout' => 30,
				'headers' => array(
					'Accept'        => 'application/json',
					'Content-Type'  => 'application/json',
					'Authorization' => 'Bearer ' . $access_token,
				),
				'body'    => wp_json_encode( array() ),
			)
		);

		if ( is_wp_error( $response ) ) {
			return array(
				'ok'    => false,
				'error' => __( 'Could not revoke the remote connection.', 'pendid-ai-studio' ),
			);
		}

		$status_code = (int) wp_remote_retrieve_response_code( $response );

		if ( 200 > $status_code || 299 < $status_code ) {
			return array(
				'ok'    => false,
				'error' => __( 'Could not revoke the remote connection.', 'pendid-ai-studio' ),
			);
		}

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

		if ( JSON_ERROR_NONE !== json_last_error() || ! is_array( $data ) || empty( $data['ok'] ) ) {
			return array(
				'ok'    => false,
				'error' => __( 'Could not revoke the remote connection.', 'pendid-ai-studio' ),
			);
		}

		return array(
			'ok' => true,
		);
	}

	/**
	 * Generates an image through Pendid Studio.
	 *
	 * @param string $access_token Stored account access token.
	 * @param string $prompt       Image prompt.
	 * @param string $aspect_ratio Image aspect ratio.
	 * @return array<string, mixed>
	 */
	public function generate_image( $access_token, $args ) {
		$access_token = is_scalar( $access_token ) ? sanitize_text_field( (string) $access_token ) : '';
		$args         = is_array( $args ) ? $args : array();
		$prompt       = isset( $args['prompt'] ) && is_scalar( $args['prompt'] ) ? trim( sanitize_textarea_field( (string) $args['prompt'] ) ) : '';

		if ( '' === $prompt ) {
			return array( 'ok' => false, 'reason' => 'prompt_required', 'status' => 400, 'error' => __( 'Prompt is required.', 'pendid-ai-studio' ) );
		}

		if ( '' === $access_token ) {
			return array( 'ok' => false, 'reason' => 'invalid_token', 'status' => 401, 'error' => __( 'The Pendid connection is not available.', 'pendid-ai-studio' ) );
		}

		$fields = array(
			'mode'             => isset( $args['mode'] ) ? sanitize_key( (string) $args['mode'] ) : 'generate',
			'prompt'           => $prompt,
			'aspect_ratio'     => isset( $args['aspect_ratio'] ) ? sanitize_text_field( (string) $args['aspect_ratio'] ) : '1:1',
			'image_type'       => isset( $args['image_type'] ) ? sanitize_key( (string) $args['image_type'] ) : 'intro-cover',
			'format'           => isset( $args['format'] ) ? sanitize_key( (string) $args['format'] ) : 'intro-square',
			'tone'             => isset( $args['tone'] ) ? sanitize_text_field( (string) $args['tone'] ) : 'حرفه‌ای',
			'source_image_url' => isset( $args['source_image_url'] ) ? esc_url_raw( (string) $args['source_image_url'] ) : '',
			'source_r2_key'    => isset( $args['source_r2_key'] ) ? sanitize_text_field( (string) $args['source_r2_key'] ) : '',
		);
		$files  = array();

		if ( ! empty( $args['reference_file'] ) && is_array( $args['reference_file'] ) ) {
			$files['reference_file'] = $args['reference_file'];
		}

		$multipart = $this->build_multipart_body( $fields, $files );

		if ( is_wp_error( $multipart ) ) {
			return array( 'ok' => false, 'reason' => 'invalid_input_image', 'status' => 400, 'error' => $multipart->get_error_message() );
		}

		$response = wp_remote_post(
			esc_url_raw( self::API_BASE_URL . '/generate' ),
			array(
				'timeout' => 120,
				'headers' => array(
					'Accept'        => 'application/json',
					'Authorization' => 'Bearer ' . $access_token,
					'Content-Type'  => 'multipart/form-data; boundary=' . $multipart['boundary'],
				),
				'body'    => $multipart['body'],
			)
		);

		if ( is_wp_error( $response ) ) {
			return array( 'ok' => false, 'reason' => 'service_unavailable', 'status' => 0, 'error' => __( 'Unable to reach Pendid Studio.', 'pendid-ai-studio' ) );
		}

		$status_code = (int) wp_remote_retrieve_response_code( $response );
		$body        = wp_remote_retrieve_body( $response );
		$data        = json_decode( $body, true );
		$valid_json  = JSON_ERROR_NONE === json_last_error() && is_array( $data );
		$reason      = $valid_json && isset( $data['error'] ) && is_scalar( $data['error'] ) ? sanitize_key( (string) $data['error'] ) : 'generation_failed';

		if ( 200 > $status_code || 299 < $status_code ) {
			return array( 'ok' => false, 'reason' => $reason, 'status' => $status_code, 'error' => __( 'Image generation was not approved by Pendid Studio.', 'pendid-ai-studio' ) );
		}

		if ( ! $valid_json ) {
			return array( 'ok' => false, 'reason' => 'invalid_response', 'status' => $status_code, 'error' => __( 'Pendid Studio returned an invalid response.', 'pendid-ai-studio' ) );
		}

		if ( empty( $data['ok'] ) ) {
			return array( 'ok' => false, 'reason' => $reason, 'status' => $status_code, 'error' => __( 'Image generation failed. Please try again.', 'pendid-ai-studio' ) );
		}

		return $this->sanitize_response_data( $data );
	}

	/**
	 * Builds a raw multipart request compatible with the WordPress HTTP API.
	 *
	 * @param array<string, scalar>              $fields Form fields.
	 * @param array<string, array<string, mixed>> $files  File definitions.
	 * @return array{boundary:string,body:string}|WP_Error
	 */
	private function build_multipart_body( $fields, $files ) {
		$boundary = '--------------------------' . wp_generate_password( 24, false, false );
		$eol      = "\r\n";
		$body     = '';

		foreach ( $fields as $name => $value ) {
			$body .= '--' . $boundary . $eol;
			$body .= 'Content-Disposition: form-data; name="' . sanitize_key( $name ) . '"' . $eol . $eol;
			$body .= (string) $value . $eol;
		}

		foreach ( $files as $field_name => $file ) {
			$path = isset( $file['path'] ) && is_scalar( $file['path'] ) ? (string) $file['path'] : '';

			if ( ! $path || ! is_readable( $path ) ) {
				return new WP_Error( 'pendid_reference_unreadable', __( 'The reference image could not be read.', 'pendid-ai-studio' ) );
			}

			$bytes = file_get_contents( $path );

			if ( false === $bytes ) {
				return new WP_Error( 'pendid_reference_unreadable', __( 'The reference image could not be read.', 'pendid-ai-studio' ) );
			}

			$name = isset( $file['name'] ) ? sanitize_file_name( (string) $file['name'] ) : 'reference-image';
			$type = isset( $file['type'] ) ? sanitize_text_field( (string) $file['type'] ) : 'application/octet-stream';
			$body .= '--' . $boundary . $eol;
			$body .= 'Content-Disposition: form-data; name="' . sanitize_key( $field_name ) . '"; filename="' . $name . '"' . $eol;
			$body .= 'Content-Type: ' . $type . $eol . $eol;
			$body .= $bytes . $eol;
		}

		$body .= '--' . $boundary . '--' . $eol;

		return array( 'boundary' => $boundary, 'body' => $body );
	}

	/**
	 * Fetches account status from Pendid Studio.
	 *
	 * @param string $access_token Stored account access token.
	 * @return array<string, mixed>
	 */
	public function get_account_status( $access_token ) {
		$access_token = is_scalar( $access_token ) ? sanitize_text_field( (string) $access_token ) : '';

		if ( '' === $access_token ) {
			return array(
				'ok'    => false,
				'error' => __( 'Access token is required.', 'pendid-ai-studio' ),
			);
		}

		$response = wp_remote_get(
			esc_url_raw( self::API_BASE_URL . '/account' ),
			array(
				'timeout' => 30,
				'headers' => array(
					'Accept'        => 'application/json',
					'Authorization' => 'Bearer ' . $access_token,
				),
			)
		);

		if ( is_wp_error( $response ) ) {
			return array(
				'ok'    => false,
				'error' => __( 'Unable to reach Pendid Studio.', 'pendid-ai-studio' ),
			);
		}

		$status_code = (int) wp_remote_retrieve_response_code( $response );

		if ( 200 > $status_code || 299 < $status_code ) {
			return array(
				'ok'    => false,
				'error' => __( 'Unable to fetch account status.', 'pendid-ai-studio' ),
			);
		}

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

		if ( JSON_ERROR_NONE !== json_last_error() || ! is_array( $data ) ) {
			return array(
				'ok'    => false,
				'error' => __( 'Invalid account status response.', 'pendid-ai-studio' ),
			);
		}

		if ( empty( $data['ok'] ) ) {
			return array(
				'ok'    => false,
				'error' => isset( $data['error'] ) && is_scalar( $data['error'] )
					? sanitize_text_field( (string) $data['error'] )
					: __( 'Unable to fetch account status.', 'pendid-ai-studio' ),
			);
		}

		$account = isset( $data['account'] ) && is_array( $data['account'] ) ? $data['account'] : array();

		return array(
			'ok'      => true,
			'account' => $this->sanitize_response_data( $account ),
		);
	}

	/**
	 * Sanitizes response data recursively.
	 *
	 * @param mixed $data Response data.
	 * @return mixed
	 */
	private function sanitize_response_data( $data ) {
		if ( is_array( $data ) ) {
			$sanitized = array();

			foreach ( $data as $key => $value ) {
				$sanitized_key               = is_string( $key ) ? sanitize_key( $key ) : $key;
				$sanitized[ $sanitized_key ] = $this->sanitize_response_data( $value );
			}

			return $sanitized;
		}

		if ( is_bool( $data ) || is_int( $data ) || is_float( $data ) || null === $data ) {
			return $data;
		}

		if ( ! is_scalar( $data ) ) {
			return '';
		}

		$value = (string) $data;

		if ( 0 === strpos( $value, 'http://' ) || 0 === strpos( $value, 'https://' ) ) {
			return esc_url_raw( $value );
		}

		return sanitize_text_field( $value );
	}
}
