//! REST API authentication signing helpers.
//!
//! This module provides utilities for constructing authentication credentials
//! for REST endpoints that require signature-based authentication.
//!
//! ## Authentication Protocol
//!
//! The authentication message format is: `{METHOD}{PATH}{TIMESTAMP}`
//!
//! For example: `GET/orders/pending1707000000`
//!
//! This message is signed using EIP-191 (`personal_sign`) and the resulting
//! credentials are sent as HTTP headers:
//! - `X-Auth-Address`: Signer's EVM address
//! - `X-Auth-Timestamp`: Unix timestamp (must be within server tolerance)
//! - `X-Auth-Signature`: EIP-191 signature over the message
//!
//! ## Example
//!
//! ```ignore
//! use tenbin_sdk::signing::rest_auth::generate_auth_credentials;
//! use std::time::{SystemTime, UNIX_EPOCH};
//!
//! // Get current timestamp
//! let timestamp = SystemTime::now()
//!     .duration_since(UNIX_EPOCH)
//!     .unwrap()
//!     .as_secs();
//!
//! // Generate credentials for GET /orders/pending
//! let auth = generate_auth_credentials(
//!     "GET",
//!     "/orders/pending",
//!     my_address,
//!     timestamp,
//!     &my_signer,
//! ).await?;
//!
//! // Use with authenticated endpoints
//! let orders = client.rest.get_pending_orders(&auth).await?;
//! ```

use alloy::primitives::{Address, B256};

use crate::{
    ClientError,
    signing::{challenge::signing_digest, signer::PayloadSigner},
};

// Re-export for public API usage
pub use crate::transport::http::AuthCredentials;

/// Construct the authentication message for a REST request.
///
/// The message format is: `{METHOD}{PATH}{TIMESTAMP}`
/// This message should be signed using EIP-191 personal_sign.
pub fn auth_message(method: &str, path: &str, timestamp: u64) -> String {
    format!("{}{}{}", method, path, timestamp)
}

/// Compute the signing digest for REST authentication.
///
/// This returns the EIP-191 hash of the authentication message
/// that should be signed.
pub fn auth_signing_digest(method: &str, path: &str, timestamp: u64) -> B256 {
    let message = auth_message(method, path, timestamp);
    signing_digest(&message)
}

/// Generate authentication credentials for a REST request.
///
/// This helper constructs the authentication message, signs it using the
/// provided signer, and returns the complete credentials needed for
/// authenticated requests.
///
/// # Arguments
/// * `method` - HTTP method (e.g., "GET", "DELETE")
/// * `path` - Request path (e.g., "/orders/pending")
/// * `address` - Signer's EVM address
/// * `timestamp` - Unix timestamp in seconds
/// * `signer` - Implementation of PayloadSigner to sign the message
///
/// # Example
/// ```ignore
/// let creds = generate_auth_credentials(
///     "GET",
///     "/orders/pending",
///     my_address,
///     current_timestamp(),
///     &my_signer,
/// ).await?;
/// ```
pub async fn generate_auth_credentials<S: PayloadSigner>(
    method: &str,
    path: &str,
    address: Address,
    timestamp: u64,
    signer: &S,
) -> Result<AuthCredentials, ClientError> {
    let digest = auth_signing_digest(method, path, timestamp);
    let signature = signer.sign_hash(&digest).await?;

    Ok(AuthCredentials {
        address,
        timestamp,
        signature: signature.to_string(),
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::fixtures::{FailingSigner, StaticSigner};

    #[test]
    fn auth_message_format_is_correct() {
        let msg = auth_message("GET", "/orders/pending", 1707000000);
        assert_eq!(msg, "GET/orders/pending1707000000");
    }

    #[test]
    fn auth_message_delete_format() {
        let msg = auth_message(
            "DELETE",
            "/order/123e4567-e89b-12d3-a456-426614174000",
            1707000000,
        );
        assert_eq!(
            msg,
            "DELETE/order/123e4567-e89b-12d3-a456-4266141740001707000000"
        );
    }

    #[test]
    fn auth_signing_digest_is_deterministic() {
        let d1 = auth_signing_digest("GET", "/orders/pending", 1707000000);
        let d2 = auth_signing_digest("GET", "/orders/pending", 1707000000);
        assert_eq!(d1, d2);
    }

    #[test]
    fn auth_signing_digest_differs_with_different_inputs() {
        let d1 = auth_signing_digest("GET", "/orders/pending", 1707000000);
        let d2 = auth_signing_digest("GET", "/orders/pending", 1707000001);
        assert_ne!(d1, d2);
    }

    #[tokio::test]
    async fn generate_auth_credentials_success() {
        let address = Address::ZERO;
        let timestamp = 1707000000u64;

        let creds =
            generate_auth_credentials("GET", "/orders/pending", address, timestamp, &StaticSigner)
                .await
                .unwrap();

        assert_eq!(creds.address, address);
        assert_eq!(creds.timestamp, timestamp);
        assert!(!creds.signature.is_empty());
    }

    #[tokio::test]
    async fn generate_auth_credentials_signing_failure() {
        let address = Address::ZERO;
        let timestamp = 1707000000u64;

        let err =
            generate_auth_credentials("GET", "/orders/pending", address, timestamp, &FailingSigner)
                .await
                .unwrap_err();

        assert!(matches!(err, ClientError::Auth(_)));
    }
}
