<?php

namespace Tops\AmazonSellingPartnerAPI;

/**
 * Signing AWS requests with Signature Version 4
 * 
 * In this class we calculate aws signature version 4
 * Step 1: Create a canonical request for Signature Version 4
 * Step 2: Create a string to sign for Signature Version 4
 * Step 3: Calculate the signature for AWS Signature Version 4
 * Step 4: Add the signature to the HTTP request 
 * 
 * Ref link:
 * https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
 * https://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html
 * https://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
 * https://docs.aws.amazon.com/general/latest/gr/sigv4-add-signature-to-request.html
 * 
 */
class Signature
{ 
    public static function calculateSignature(Configuration $config, string $host, string $method, $uri = '', $query_string = '', $data = [], string $service_name="execute-api")
    {
        return self::calculateSignatureForService(
            $host,
            $method,
            $uri,
            $query_string,
            $data,
            $service_name,
            $config->getAccessKey(),
            $config->getSecretKey(),
            $config->getRegion()            
        );

    }

    public static function calculateSignatureForService(string $host,string $method, $uri,$query_string,$data,string $service,string $accessKey,string $secretKey,string $region)
    {
        $terminationString = 'aws4_request';
        $algorithm = 'AWS4-HMAC-SHA256';
        $amzdate = gmdate('Ymd\THis\Z');
        $date = substr($amzdate, 0, 8);

        // Prepare payload
        if (is_array($data))
        {
            $param = json_encode($data);
            if ('[]' == $param) {
                $requestPayload = '';
            } else {
                $requestPayload = $param;
            }
        }
        else
        {
            $requestPayload = $data;
        }     
 
        // Hashed payload
        $hashedPayload = hash('sha256', $requestPayload);
      
        //Compute Canonical Headers
        $canonicalHeaders = [
            'host' => $host,
            'x-amz-date' => $amzdate
        ];

        $canonicalHeadersStr = '';
        foreach ($canonicalHeaders as $h => $v) {
            $canonicalHeadersStr .= $h.':'.$v."\n";
        }       
        
        $signedHeadersStr = join(';', array_keys($canonicalHeaders));
       
        //Prepare credentials scope
        $credentialScope = $date.'/'. $region.'/'.$service.'/'.$terminationString;
      
        // step 1 create canonical request    
        $canonicalRequest = $method."\n".$uri."\n".$query_string."\n".$canonicalHeadersStr."\n".$signedHeadersStr."\n".$hashedPayload;
       
        // step 2 create string to sign
        $stringToSign = $algorithm."\n".$amzdate."\n".$credentialScope."\n".hash('sha256', $canonicalRequest);

        // step 3 calculate the signature for aws
        $kSecret = 'AWS4'.$secretKey;
        $kDate = hash_hmac('sha256', $date, $kSecret, true);
        $kRegion = hash_hmac('sha256', $region, $kDate, true);
        $kService = hash_hmac('sha256', $service, $kRegion, true);
        $kSigning = hash_hmac('sha256', $terminationString, $kService, true);
      
        //Compute the signature
        $signature = trim(hash_hmac('sha256', $stringToSign, $kSigning));

        //Finalize the authorization structure
        $authorizationHeader = $algorithm." Credential={$accessKey}/{$credentialScope}, SignedHeaders={$signedHeadersStr}, Signature={$signature}";

        return array_merge($canonicalHeaders, [
            'Authorization' => $authorizationHeader,
        ]);
    }
}