<?php
/**
 * @package Hafenstudios\Kurato
 */

namespace Hafenstudios\Kurato\Mail;

defined( 'ABSPATH' ) || exit;

/**
 * AWS Signature Version 4 fuer SES-API-Requests.
 *
 * SES ist der einzige Anbieter ohne simplen API-Key: jeder Request wird
 * kryptografisch signiert. Der Signer ist bewusst eigenstaendig und
 * deterministisch (fester Zeitstempel -> feste Signatur), damit er isoliert
 * gegen bekannte Werte getestet werden kann.
 *
 * Ablauf (AWS4-HMAC-SHA256): kanonischer Request -> String-to-Sign ->
 * abgeleiteter Signierschluessel (HMAC-Kette) -> Signatur -> Authorization-Header.
 */
final class AwsV4Signer {

	private const ALGO = 'AWS4-HMAC-SHA256';

	private string $access_key;

	private string $secret_key;

	private string $region;

	private string $service;

	/**
	 * @param string $access_key AWS Access Key ID.
	 * @param string $secret_key AWS Secret Access Key.
	 * @param string $region     Region (z.B. eu-central-1).
	 * @param string $service    Dienst (Default ses).
	 */
	public function __construct( string $access_key, string $secret_key, string $region, string $service = 'ses' ) {
		$this->access_key = $access_key;
		$this->secret_key = $secret_key;
		$this->region     = $region;
		$this->service    = $service;
	}

	/**
	 * Signiert einen POST-Request und liefert die zu sendenden Header.
	 *
	 * @param string $host      Host (z.B. email.eu-central-1.amazonaws.com).
	 * @param string $uri       Kanonischer Pfad (z.B. /v2/email/outbound-emails).
	 * @param string $payload   Roher Body.
	 * @param string $timestamp Optionaler ISO8601-Basic-Zeitstempel (Test); sonst jetzt.
	 * @return array<string, string> Header: Authorization, X-Amz-Date, Content-Type, Host.
	 */
	public function sign_post( string $host, string $uri, string $payload, string $timestamp = '' ): array {
		$timestamp = '' !== $timestamp ? $timestamp : gmdate( 'Ymd\THis\Z' );
		$date      = substr( $timestamp, 0, 8 );
		$scope     = $date . '/' . $this->region . '/' . $this->service . '/aws4_request';

		$content_type = 'application/json';
		$payload_hash = hash( 'sha256', $payload );

		// Kanonische Header (alphabetisch, klein).
		$canonical_headers = 'content-type:' . $content_type . "\n"
			. 'host:' . $host . "\n"
			. 'x-amz-date:' . $timestamp . "\n";
		$signed_headers    = 'content-type;host;x-amz-date';

		$canonical_request = "POST\n"
			. $uri . "\n"
			. "\n" // leere Query
			. $canonical_headers . "\n"
			. $signed_headers . "\n"
			. $payload_hash;

		$string_to_sign = self::ALGO . "\n"
			. $timestamp . "\n"
			. $scope . "\n"
			. hash( 'sha256', $canonical_request );

		$signature = hash_hmac( 'sha256', $string_to_sign, $this->signing_key( $date ) );

		$authorization = self::ALGO
			. ' Credential=' . $this->access_key . '/' . $scope
			. ', SignedHeaders=' . $signed_headers
			. ', Signature=' . $signature;

		return array(
			'Authorization' => $authorization,
			'X-Amz-Date'    => $timestamp,
			'Content-Type'  => $content_type,
			'Host'          => $host,
		);
	}

	/**
	 * Leitet den Signierschluessel ab (HMAC-Kette).
	 *
	 * @param string $date Datum (Ymd).
	 * @return string Roher Binaerschluessel.
	 */
	private function signing_key( string $date ): string {
		$k_date    = hash_hmac( 'sha256', $date, 'AWS4' . $this->secret_key, true );
		$k_region  = hash_hmac( 'sha256', $this->region, $k_date, true );
		$k_service = hash_hmac( 'sha256', $this->service, $k_region, true );
		return hash_hmac( 'sha256', 'aws4_request', $k_service, true );
	}
}
