<?php
/**
 * Brevo REST API v3 HTTP client.
 *
 * @package RewindBrevoFormBridge
 */

namespace RewindBrevoFormBridge\Api;

use RewindBrevoFormBridge\Admin\Settings;

defined( 'ABSPATH' ) || exit;

/**
 * Thin wrapper over wp_remote_request(). Throws nothing — every outcome,
 * including transport failure, comes back as a Response value object.
 *
 * @since 0.1.0
 */
class Client {

	/**
	 * Brevo API base URL, no trailing slash.
	 *
	 * @var string
	 */
	const BASE_URL = 'https://api.brevo.com/v3';

	/**
	 * Settings service (supplies the API key).
	 *
	 * @var Settings
	 */
	private Settings $settings;

	/**
	 * Constructor.
	 *
	 * @since 0.1.0
	 * @param Settings $settings Settings service.
	 */
	public function __construct( Settings $settings ) {
		$this->settings = $settings;
	}

	/**
	 * Perform a request against the Brevo API.
	 *
	 * @since 0.1.0
	 * @param string     $method HTTP method (GET, POST, PUT, DELETE).
	 * @param string     $path   Path relative to /v3, e.g. '/account'.
	 * @param array|null $body   Request body, JSON-encoded when non-null.
	 * @param array      $args   Extra wp_remote_request() args to merge.
	 * @return Response
	 */
	public function request( string $method, string $path, ?array $body = null, array $args = array() ): Response {
		$api_key = $this->settings->api_key();

		if ( '' === $api_key ) {
			return new Response( 0, '', array(), __( 'No Brevo API key is configured. Save your API key first.', 'rewind-form-bridge-for-brevo' ) );
		}

		$url = self::BASE_URL . '/' . ltrim( $path, '/' );

		$defaults = array(
			'method'  => strtoupper( $method ),
			'timeout' => 20,
			'headers' => array(
				'api-key'      => $api_key,
				'content-type' => 'application/json',
				'accept'       => 'application/json',
				'user-agent'   => 'RewindBrevoFormBridge/' . RBFB_VERSION . '; ' . home_url(),
			),
		);

		if ( null !== $body ) {
			$defaults['body'] = wp_json_encode( $body );
		}

		$request_args = array_replace_recursive( $defaults, $args );

		/**
		 * Filter the wp_remote_request() args for Brevo API calls.
		 *
		 * Lets site owners adjust timeouts or proxy settings. Disabling SSL
		 * verification is not honoured — see below.
		 *
		 * @since 0.1.0
		 * @param array  $request_args The HTTP request arguments.
		 * @param string $method       The HTTP method.
		 * @param string $path         The API path.
		 */
		$request_args = apply_filters( 'rbfb_api_request_args', $request_args, $method, $path );

		// Hard rule: never ship a request with SSL verification off, even if
		// a filter tried to sneak it in. Unsetting restores WP's secure default.
		unset( $request_args['sslverify'] );

		$result = wp_remote_request( $url, $request_args );

		if ( is_wp_error( $result ) ) {
			return Response::from_wp_error( $result );
		}

		return new Response(
			(int) wp_remote_retrieve_response_code( $result ),
			(string) wp_remote_retrieve_body( $result ),
			$this->flatten_headers( $result )
		);
	}

	/**
	 * GET convenience wrapper.
	 *
	 * @since 0.1.0
	 * @param string $path Path relative to /v3.
	 * @param array  $args Extra request args.
	 * @return Response
	 */
	public function get( string $path, array $args = array() ): Response {
		return $this->request( 'GET', $path, null, $args );
	}

	/**
	 * POST convenience wrapper.
	 *
	 * @since 0.1.0
	 * @param string $path Path relative to /v3.
	 * @param array  $body Request body.
	 * @param array  $args Extra request args.
	 * @return Response
	 */
	public function post( string $path, array $body, array $args = array() ): Response {
		return $this->request( 'POST', $path, $body, $args );
	}

	/**
	 * PUT convenience wrapper.
	 *
	 * @since 0.1.0
	 * @param string $path Path relative to /v3.
	 * @param array  $body Request body.
	 * @param array  $args Extra request args.
	 * @return Response
	 */
	public function put( string $path, array $body, array $args = array() ): Response {
		return $this->request( 'PUT', $path, $body, $args );
	}

	/**
	 * Normalise WP HTTP API headers into a plain lower-cased array.
	 *
	 * wp_remote_retrieve_headers() returns a CaseInsensitiveDictionary on
	 * modern WP; older code paths may hand back a plain array.
	 *
	 * @param mixed $result Raw wp_remote_request() result.
	 * @return array
	 */
	private function flatten_headers( $result ): array {
		$headers = wp_remote_retrieve_headers( $result );

		if ( is_object( $headers ) && method_exists( $headers, 'getAll' ) ) {
			$headers = $headers->getAll();
		}

		if ( ! is_array( $headers ) ) {
			return array();
		}

		$flat = array();
		foreach ( $headers as $name => $value ) {
			$flat[ strtolower( (string) $name ) ] = is_array( $value ) ? (string) end( $value ) : (string) $value;
		}

		return $flat;
	}
}
