<?php
/**
 * affilun Cloud usage client.
 *
 * @package Affilun\Application\Cloud
 */

declare(strict_types=1);

namespace Affilun\Application\Cloud;

use Affilun\Application\Settings\CloudSettings;
use InvalidArgumentException;
use RuntimeException;

/**
 * Fetches current affilun Cloud usage/quota summaries for a signed site.
 */
final class CloudUsageClient {

	/**
	 * Transport callback.
	 *
	 * @var callable
	 */
	private $transport;

	/**
	 * @param CloudRequestSigner $signer    Request signer.
	 * @param callable|null      $transport Transport override.
	 *
	 * @phpstan-param (callable(string,array<string,string>):array<string,mixed>)|null $transport
	 */
	public function __construct(
		private CloudRequestSigner $signer = new CloudRequestSigner(),
		?callable $transport = null
	) {
		$this->transport = $transport ?? self::defaultTransport();
	}

	/**
	 * Fetch current usage summary.
	 *
	 * @param CloudSettings $settings  Cloud settings.
	 * @param int           $timestamp Timestamp.
	 * @param string        $nonce     Nonce.
	 *
	 * @return array<string,mixed>
	 *
	 * @throws InvalidArgumentException When Cloud settings are incomplete.
	 * @throws RuntimeException When Cloud response fails.
	 */
	public function current( CloudSettings $settings, int $timestamp, string $nonce ): array {
		if ( ! $settings->isConfigured() ) {
			throw new InvalidArgumentException( 'affilun Cloud settings are not configured.' );
		}

		$path    = '/usage';
		$headers = $this->signer->sign(
			$settings->siteId(),
			$settings->apiKey(),
			'GET',
			$path,
			'',
			$timestamp,
			$nonce
		);

		$response = ( $this->transport )( $settings->endpoint() . $path, $headers );
		$status   = isset( $response['status'] ) ? (int) $response['status'] : 0;
		$data     = isset( $response['data'] ) && is_array( $response['data'] ) ? $response['data'] : null;
		if ( $status < 200 || $status >= 300 || $data === null ) {
			throw new RuntimeException( 'affilun Cloud usage fetch failed.' );
		}

		return $data;
	}

	/**
	 * Default WordPress HTTP transport.
	 *
	 * @return callable
	 *
	 * @phpstan-return callable(string,array<string,string>):array<string,mixed>
	 */
	private static function defaultTransport(): callable {
		return static function ( string $url, array $headers ): array {
			$response = CloudHttpOptions::safeGet(
				$url,
				array(
					'timeout' => 5,
					'headers' => $headers,
				)
			);
			if ( function_exists( 'is_wp_error' ) && is_wp_error( $response ) ) {
				return array(
					'status' => 503,
					'data'   => null,
				);
			}

			$raw     = is_array( $response ) ? (string) wp_remote_retrieve_body( $response ) : '';
			$decoded = json_decode( $raw, true );
			return array(
				'status' => is_array( $response ) ? (int) wp_remote_retrieve_response_code( $response ) : 0,
				'data'   => is_array( $decoded ) ? $decoded : null,
			);
		};
	}
}
