use sha2::{Sha256, Digest};
use hmac::{Hmac, Mac};
use base64::{Engine as _, engine::general_purpose};
use crate::error::{CommonResult as Result, CommonError as Error};

/// Hash a string using SHA-256
pub fn hash_sha256(input: &str) -> String {
    let mut hasher = Sha256::new();
    hasher.update(input.as_bytes());
    format!("{:x}", hasher.finalize())
}

/// Generate HMAC signature
pub fn generate_hmac_signature(data: &str, secret: &str) -> Result<String> {
    let mut mac = Hmac::<Sha256>::new_from_slice(secret.as_bytes())
        .map_err(|_| Error::InternalError("Invalid HMAC secret".to_string()))?;
    
    mac.update(data.as_bytes());
    let result = mac.finalize();
    let signature = general_purpose::STANDARD.encode(result.into_bytes());
    
    Ok(signature)
}

/// Verify HMAC signature
pub fn verify_hmac_signature(data: &str, signature: &str, secret: &str) -> Result<bool> {
    let expected = generate_hmac_signature(data, secret)?;
    Ok(signature == expected)
}

/// Encode data to base64
pub fn encode_base64(data: &[u8]) -> String {
    general_purpose::STANDARD.encode(data)
}

/// Decode base64 data
pub fn decode_base64(data: &str) -> Result<Vec<u8>> {
    general_purpose::STANDARD.decode(data)
        .map_err(|e| Error::SerializationError(format!("Base64 decode error: {}", e)))
}

/// Convert bytes to hex string
pub fn bytes_to_hex(bytes: &[u8]) -> String {
    bytes.iter().map(|b| format!("{:02x}", b)).collect()
}

/// Convert hex string to bytes
pub fn hex_to_bytes(hex: &str) -> Result<Vec<u8>> {
    if hex.len() % 2 != 0 {
        return Err(Error::ValidationError("Hex string must have even length".to_string()));
    }
    
    let mut bytes = Vec::new();
    for chunk in hex.as_bytes().chunks(2) {
        let byte_str = std::str::from_utf8(chunk)
            .map_err(|_| Error::ValidationError("Invalid hex string".to_string()))?;
        let byte = u8::from_str_radix(byte_str, 16)
            .map_err(|_| Error::ValidationError("Invalid hex character".to_string()))?;
        bytes.push(byte);
    }
    
    Ok(bytes)
}
