// src/functions/sigv4.rs

use hmac::{Hmac, Mac};
use sha2::{Sha256, Digest};
use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
use hex;

/// AWS Signature Version 4 Signing Utilities
///
/// Use this module to generate all headers necessary for authenticated AWS API requests
/// (e.g., S3, DynamoDB, etc.) via SigV4 signing.
///
/// Example usage:
/// ```ignore
/// let (authz, headers) = build_aws_sigv4_headers(
///     "GET", "/bucket/key", "", "payload_sha256", "bucket.s3.amazonaws.com",
///     "20250715T110100Z", "20250715", "us-east-1", "s3",
///     "AKIA...", "SECRET..."
/// );
/// // Use `headers` in reqwest client.
/// ```

/// Calculate the AWS Signature V4 signing key for a given secret, date, region, and service.
pub fn get_signature_key(secret_key: &str, date: &str, region: &str, service: &str) -> Vec<u8> {
    let k_date = hmac_sha256(format!("AWS4{}", secret_key).as_bytes(), date.as_bytes());
    let k_region = hmac_sha256(&k_date, region.as_bytes());
    let k_service = hmac_sha256(&k_region, service.as_bytes());
    hmac_sha256(&k_service, b"aws4_request")
}

/// Utility: HMAC-SHA256 for key and message.
pub fn hmac_sha256(key: &[u8], msg: &[u8]) -> Vec<u8> {
    let mut mac = Hmac::<Sha256>::new_from_slice(key)
        .expect("HMAC can take key of any size");
    mac.update(msg);
    mac.finalize().into_bytes().to_vec()
}

/// Utility: Hex-encoded SHA256 for a byte slice.
pub fn hex_sha256(data: &[u8]) -> String {
    let mut hasher = Sha256::new();
    hasher.update(data);
    hex::encode(hasher.finalize())
}

/// Utility: Hex-encoded SHA256 for a string slice.
pub fn hex_sha256_str(data: &str) -> String {
    let mut hasher = Sha256::new();
    hasher.update(data.as_bytes());
    hex::encode(hasher.finalize())
}

/// Build all required AWS SigV4 headers for an HTTP request.
///
/// Returns the Authorization header value and a HeaderMap ready for reqwest.
///
/// # Arguments:
/// - `method`: "GET", "PUT", etc.
/// - `canonical_uri`: Path (e.g. "/mybucket/myfile.txt")
/// - `canonical_querystring`: Query (e.g. "versionId=...") or "" if none
/// - `payload_hash`: Hex-encoded SHA256 of payload (e.g. for GET, use hex of empty string)
/// - `host`: e.g. "mybucket.s3.amazonaws.com"
/// - `amz_date`: e.g. "20250715T110100Z"
/// - `date_stamp`: e.g. "20250715"
/// - `region`: AWS region, e.g. "us-east-1"
/// - `service`: e.g. "s3"
/// - `access_key`: AWS Access Key ID
/// - `secret_key`: AWS Secret Access Key
pub fn build_aws_sigv4_headers(
    method: &str,
    canonical_uri: &str,
    canonical_querystring: &str,
    payload_hash: &str,
    host: &str,
    amz_date: &str,
    date_stamp: &str,
    region: &str,
    service: &str,
    access_key: &str,
    secret_key: &str,
) -> (String, HeaderMap) {
    // Step 1: Create canonical headers
    let canonical_headers = format!(
        "host:{}\nx-amz-content-sha256:{}\nx-amz-date:{}\n",
        host, payload_hash, amz_date
    );
    let signed_headers = "host;x-amz-content-sha256;x-amz-date";
    let canonical_request = format!(
        "{}\n{}\n{}\n{}\n{}\n{}",
        method,
        canonical_uri,
        canonical_querystring,
        canonical_headers,
        signed_headers,
        payload_hash
    );
    let algorithm = "AWS4-HMAC-SHA256";
    let credential_scope = format!("{}/{}/{}/aws4_request", date_stamp, region, service);
    let string_to_sign = format!(
        "{}\n{}\n{}\n{}",
        algorithm,
        amz_date,
        credential_scope,
        hex_sha256(canonical_request.as_bytes())
    );
    let signing_key = get_signature_key(secret_key, date_stamp, region, service);
    let mut mac = Hmac::<Sha256>::new_from_slice(&signing_key)
        .expect("HMAC can take key of any size");
    mac.update(string_to_sign.as_bytes());
    let signature = hex::encode(mac.finalize().into_bytes());
    let authorization_header = format!(
        "{} Credential={}/{}, SignedHeaders={}, Signature={}",
        algorithm, access_key, credential_scope, signed_headers, signature
    );

    // Step 2: Build the headers map
    let mut headers = HeaderMap::new();
    headers.insert(HeaderName::from_static("host"), HeaderValue::from_str(host).unwrap());
    headers.insert(HeaderName::from_static("x-amz-date"), HeaderValue::from_str(amz_date).unwrap());
    headers.insert(HeaderName::from_static("x-amz-content-sha256"), HeaderValue::from_str(payload_hash).unwrap());
    headers.insert(HeaderName::from_static("authorization"), HeaderValue::from_str(&authorization_header).unwrap());

    (authorization_header, headers)
}

