//! Shared HTTP utilities for HCFS
//!
//! Common HTTP client configuration and header parsing utilities
//! used across both client and server components.

use reqwest::header::HeaderMap;
use std::time::Duration;

/// Standard HTTP client configuration
#[derive(Debug, Clone)]
pub struct HttpClientConfig {
    /// Request timeout (default: 5 minutes for large file transfers)
    pub timeout: Duration,
    /// Connection timeout (default: 30 seconds)
    pub connect_timeout: Duration,
    /// Whether to accept invalid TLS certificates (for testing only)
    pub accept_invalid_certs: bool,
}

impl Default for HttpClientConfig {
    fn default() -> Self {
        Self {
            timeout: Duration::from_secs(300),
            connect_timeout: Duration::from_secs(30),
            accept_invalid_certs: false,
        }
    }
}

impl HttpClientConfig {
    /// Create a new config with default timeouts
    pub fn new() -> Self {
        Self::default()
    }

    /// Set the request timeout
    pub fn with_timeout(mut self, timeout: Duration) -> Self {
        self.timeout = timeout;
        self
    }

    /// Set the connection timeout
    pub fn with_connect_timeout(mut self, connect_timeout: Duration) -> Self {
        self.connect_timeout = connect_timeout;
        self
    }

    /// Set whether to accept invalid TLS certificates (use only for testing)
    pub fn with_accept_invalid_certs(mut self, accept: bool) -> Self {
        self.accept_invalid_certs = accept;
        self
    }
}

/// Build a reqwest Client with standard configuration
///
/// # Arguments
/// * `config` - HTTP client configuration
///
/// # Returns
/// A configured reqwest::Client or an error
pub fn build_http_client(config: &HttpClientConfig) -> Result<reqwest::Client, reqwest::Error> {
    reqwest::Client::builder()
        .read_timeout(config.timeout)
        .connect_timeout(config.connect_timeout)
        .danger_accept_invalid_certs(config.accept_invalid_certs)
        .build()
}

/// Parse a header value as u64
///
/// # Arguments
/// * `headers` - The response headers
/// * `name` - The header name to parse
///
/// # Returns
/// The parsed u64 value, or None if missing or invalid
pub fn parse_header_u64(headers: &HeaderMap, name: &str) -> Option<u64> {
    headers
        .get(name)
        .and_then(|v| v.to_str().ok())
        .and_then(|s| s.parse().ok())
}

/// Parse a header value as a 32-byte array (hex-encoded)
///
/// # Arguments
/// * `headers` - The response headers
/// * `name` - The header name to parse
///
/// # Returns
/// The parsed 32-byte array, or None if missing or invalid
pub fn parse_header_bytes32(headers: &HeaderMap, name: &str) -> Option<[u8; 32]> {
    let header_value = headers.get(name)?;
    let hex_str = header_value.to_str().ok()?;
    let bytes = hex::decode(hex_str).ok()?;

    if bytes.len() != 32 {
        return None;
    }

    let mut result = [0u8; 32];
    result.copy_from_slice(&bytes);
    Some(result)
}

/// Build authorization headers for API requests
///
/// # Arguments
/// * `api_key` - Optional API key for X-API-Key header
/// * `bearer_token` - Optional bearer token for Authorization header
///
/// # Returns
/// A HeaderMap with the appropriate authorization headers
pub fn build_auth_headers(api_key: Option<&str>, bearer_token: Option<&str>) -> HeaderMap {
    let mut headers = HeaderMap::new();

    if let Some(key) = api_key
        && let Ok(value) = key.parse()
    {
        headers.insert("X-API-Key", value);
    }

    if let Some(token) = bearer_token
        && let Ok(value) = format!("Bearer {}", token).parse()
    {
        headers.insert(reqwest::header::AUTHORIZATION, value);
    }

    headers
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_http_client_config_default() {
        let config = HttpClientConfig::default();
        assert_eq!(config.timeout, Duration::from_secs(300));
        assert_eq!(config.connect_timeout, Duration::from_secs(30));
        assert!(!config.accept_invalid_certs);
    }

    #[test]
    fn test_http_client_config_builder() {
        let config = HttpClientConfig::new()
            .with_timeout(Duration::from_secs(60))
            .with_connect_timeout(Duration::from_secs(10))
            .with_accept_invalid_certs(true);

        assert_eq!(config.timeout, Duration::from_secs(60));
        assert_eq!(config.connect_timeout, Duration::from_secs(10));
        assert!(config.accept_invalid_certs);
    }

    #[test]
    fn test_build_http_client() {
        let config = HttpClientConfig::default();
        let client = build_http_client(&config);
        assert!(client.is_ok());
    }

    #[test]
    fn test_parse_header_u64() {
        let mut headers = HeaderMap::new();
        headers.insert("Content-Length", "12345".parse().unwrap());

        assert_eq!(parse_header_u64(&headers, "Content-Length"), Some(12345));
        assert_eq!(parse_header_u64(&headers, "Missing-Header"), None);
    }

    #[test]
    fn test_parse_header_u64_invalid() {
        let mut headers = HeaderMap::new();
        headers.insert("Bad-Value", "not-a-number".parse().unwrap());

        assert_eq!(parse_header_u64(&headers, "Bad-Value"), None);
    }

    #[test]
    fn test_parse_header_bytes32() {
        let mut headers = HeaderMap::new();
        let test_bytes = [42u8; 32];
        headers.insert("X-Revision-Id", hex::encode(test_bytes).parse().unwrap());

        let result = parse_header_bytes32(&headers, "X-Revision-Id");
        assert_eq!(result, Some(test_bytes));
    }

    #[test]
    fn test_parse_header_bytes32_wrong_length() {
        let mut headers = HeaderMap::new();
        let short_bytes = [42u8; 16]; // Only 16 bytes, not 32
        headers.insert("X-Revision-Id", hex::encode(short_bytes).parse().unwrap());

        assert_eq!(parse_header_bytes32(&headers, "X-Revision-Id"), None);
    }

    #[test]
    fn test_parse_header_bytes32_invalid_hex() {
        let mut headers = HeaderMap::new();
        headers.insert("X-Revision-Id", "not-valid-hex".parse().unwrap());

        assert_eq!(parse_header_bytes32(&headers, "X-Revision-Id"), None);
    }

    #[test]
    fn test_build_auth_headers_both() {
        let headers = build_auth_headers(Some("test-api-key"), Some("test-token"));

        assert_eq!(
            headers.get("X-API-Key").and_then(|v| v.to_str().ok()),
            Some("test-api-key")
        );
        assert_eq!(
            headers
                .get(reqwest::header::AUTHORIZATION)
                .and_then(|v| v.to_str().ok()),
            Some("Bearer test-token")
        );
    }

    #[test]
    fn test_build_auth_headers_api_key_only() {
        let headers = build_auth_headers(Some("test-api-key"), None);

        assert!(headers.get("X-API-Key").is_some());
        assert!(headers.get(reqwest::header::AUTHORIZATION).is_none());
    }

    #[test]
    fn test_build_auth_headers_bearer_only() {
        let headers = build_auth_headers(None, Some("test-token"));

        assert!(headers.get("X-API-Key").is_none());
        assert!(headers.get(reqwest::header::AUTHORIZATION).is_some());
    }

    #[test]
    fn test_build_auth_headers_none() {
        let headers = build_auth_headers(None, None);

        assert!(headers.is_empty());
    }
}
