<?php
/**
 * Lightweight AWS Signature Version 4 signer for SES API v2.
 *
 * @package EmailSmtp
 * @since 1.2.0
 */

namespace EmailSmtp\Includes;

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

final class AwsSigV4 {

	/**
	 * @param array<string, string> $headers Extra headers (without Authorization).
	 * @return array{url:string, headers:array<string,string>, body:string}
	 */
	public static function sign(
		string $method,
		string $host,
		string $uri,
		string $payload,
		string $region,
		string $service,
		string $access_key,
		string $secret_key,
		array $headers = []
	): array {
		$method    = strtoupper( $method );
		$amz_date  = gmdate( 'Ymd\THis\Z' );
		$date_stamp = gmdate( 'Ymd' );
		$payload_hash = hash( 'sha256', $payload );

		$headers = array_change_key_case( $headers, CASE_LOWER );
		$headers['host']                 = $host;
		$headers['x-amz-date']           = $amz_date;
		$headers['x-amz-content-sha256'] = $payload_hash;
		if ( ! isset( $headers['content-type'] ) ) {
			$headers['content-type'] = 'application/json';
		}

		ksort( $headers );
		$canonical_headers = '';
		$signed_headers_list = [];
		foreach ( $headers as $name => $value ) {
			$canonical_headers    .= $name . ':' . trim( preg_replace( '/\s+/', ' ', $value ) ?? $value ) . "\n";
			$signed_headers_list[] = $name;
		}
		$signed_headers = implode( ';', $signed_headers_list );

		$canonical_request = implode(
			"\n",
			[
				$method,
				$uri,
				'', // query string
				$canonical_headers,
				$signed_headers,
				$payload_hash,
			]
		);

		$credential_scope = $date_stamp . '/' . $region . '/' . $service . '/aws4_request';
		$string_to_sign   = implode(
			"\n",
			[
				'AWS4-HMAC-SHA256',
				$amz_date,
				$credential_scope,
				hash( 'sha256', $canonical_request ),
			]
		);

		$k_date    = hash_hmac( 'sha256', $date_stamp, 'AWS4' . $secret_key, true );
		$k_region  = hash_hmac( 'sha256', $region, $k_date, true );
		$k_service = hash_hmac( 'sha256', $service, $k_region, true );
		$k_signing = hash_hmac( 'sha256', 'aws4_request', $k_service, true );
		$signature = hash_hmac( 'sha256', $string_to_sign, $k_signing );

		$authorization = 'AWS4-HMAC-SHA256 Credential=' . $access_key . '/' . $credential_scope
			. ', SignedHeaders=' . $signed_headers
			. ', Signature=' . $signature;

		$out_headers = [];
		foreach ( $headers as $name => $value ) {
			// Restore conventional header casing for common headers.
			$out_headers[ self::header_name( $name ) ] = $value;
		}
		$out_headers['Authorization'] = $authorization;

		return [
			'url'     => 'https://' . $host . $uri,
			'headers' => $out_headers,
			'body'    => $payload,
		];
	}

	private static function header_name( string $lower ): string {
		$map = [
			'host'                 => 'Host',
			'content-type'         => 'Content-Type',
			'x-amz-date'           => 'X-Amz-Date',
			'x-amz-content-sha256' => 'X-Amz-Content-Sha256',
		];
		return $map[ $lower ] ?? $lower;
	}
}
