//! Signing integration traits.
//!
//! This module defines traits for integrating user-provided signing
//! implementations with the Tenbin SDK.
//!
//! ## Implementing a Custom Signer
//!
//! Implement `PayloadSigner` to integrate your key management solution:
//!
//! ```
//! use tenbin_sdk::signing::signer::PayloadSigner;
//! use tenbin_sdk::ClientError;
//! use alloy::primitives::B256;
//! use alloy::signers::Signature;
//! use async_trait::async_trait;
//!
//! struct MyCloudKmsSigner {
//!     key_id: String,
//! }
//!
//! #[async_trait]
//! impl PayloadSigner for MyCloudKmsSigner {
//!     async fn sign_hash(&self, digest: &B256) -> Result<Signature, ClientError> {
//!         // The digest is already correctly formatted per EIP-191 or EIP-712.
//!         // Send it to your KMS and return the signature.
//!         todo!("Call your KMS API here")
//!     }
//! }
//! ```
//!
//! ## Provided Implementations
//!
//! - `SimpleSigner`: Development/testing signer using a local private key.
//!   **Not recommended for production** where private keys should be isolated.
//!
//! Signer implementations must perform Ethereum-compatible ECDSA signing over
//! secp256k1. They may be backed by local keys, hardware wallets, MPC systems,
//! cloud KMS, or remote signing services.

use alloy::{
    primitives::B256,
    signers::{Signature, Signer, local::PrivateKeySigner},
};
use async_trait::async_trait;

use crate::ClientError;

/// Trait for user-provided signing implementations.
///
/// Implementations of this trait are responsible for signing a 32-byte digest
/// using an Ethereum-compatible signature scheme (ECDSA over secp256k1).
///
/// The digest passed to [`sign_hash`](Self::sign_hash) is constructed by the SDK
/// according to the appropriate standard (EIP-191 or EIP-712) and must be signed
/// exactly as provided without additional hashing.
///
/// # Thread Safety
///
/// Implementations must be `Send + Sync` as they may be used across async tasks.
#[async_trait]
pub trait PayloadSigner: Send + Sync {
    /// Sign a 32-byte digest and return the resulting signature.
    ///
    /// The digest has already been computed according to the Tenbin protocol
    /// (EIP-191 for authentication, EIP-712 for orders). Sign it directly
    /// without additional hashing.
    ///
    /// # Errors
    ///
    /// Return [`ClientError::Auth`] if signing fails for any reason (hardware
    /// wallet disconnected, KMS unavailable, invalid key, user rejected, etc.).
    async fn sign_hash(&self, digest: &B256) -> Result<Signature, ClientError>;
}

/// Simple signer for development and testing.
///
/// Wraps a locally-held private key for signing. **Not recommended for production**
/// where private keys should be isolated in hardware wallets, HSMs, or KMS.
///
/// # Example
///
/// ```
/// use tenbin_sdk::signing::signer::SimpleSigner;
/// use alloy::primitives::B256;
///
/// // Use a real private key in practice
/// let private_key = B256::from([0x11u8; 32]);
/// let signer = SimpleSigner::new(private_key);
/// ```
///
/// # Security Warning
///
/// This signer holds the private key in memory. For production use, implement
/// [`PayloadSigner`] with your secure key management solution.
pub struct SimpleSigner {
    pk: B256,
}

impl SimpleSigner {
    /// Construct a `SimpleSigner` from a raw 32-byte private key.
    ///
    /// # Arguments
    /// * `pk` - The secp256k1 private key as 32 bytes
    pub fn new(pk: B256) -> Self {
        Self { pk }
    }
}

#[async_trait]
impl PayloadSigner for SimpleSigner {
    async fn sign_hash(&self, digest: &B256) -> Result<Signature, ClientError> {
        // Create signer from raw private key
        let signer =
            PrivateKeySigner::from_bytes(&self.pk).map_err(|e| ClientError::Auth(e.to_string()))?;

        // Sign the pre-hashed digest
        let sig = signer
            .sign_hash(digest)
            .await
            .map_err(|e| ClientError::Auth(e.to_string()))?;

        Ok(sig)
    }
}

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

    /// Deterministically test that we can sign a digest with a valid private key.
    #[tokio::test]
    async fn simple_signer_signs_digest() {
        // A known non-zero private key (32 bytes).
        // This is fine for tests; do not use in production.
        let pk = B256::from([0x11u8; 32]);
        let signer = SimpleSigner::new(pk);

        let digest = keccak256(b"tenbin-test-digest");

        let sig = signer
            .sign_hash(&digest)
            .await
            .expect("signing should succeed");

        // Basic sanity checks: signature is not the "all zeros" default.
        // (Avoid relying on alloy internal formatting specifics.)
        let sig_bytes = sig.as_bytes();
        assert_eq!(sig_bytes.len(), 65);
        assert!(sig_bytes.iter().any(|b| *b != 0));
    }

    /// Test that invalid private keys are mapped to ClientError::Auth.
    #[tokio::test]
    async fn simple_signer_invalid_private_key_returns_auth_error() {
        // Many signer implementations reject zero private keys.
        let pk = B256::ZERO;
        let signer = SimpleSigner::new(pk);

        let digest = keccak256(b"tenbin-test-digest");

        let err = signer.sign_hash(&digest).await.unwrap_err();

        match err {
            ClientError::Auth(msg) => {
                // Don't assert exact wording; just ensure it's not empty.
                assert!(!msg.trim().is_empty());
            }
            other => panic!("expected ClientError::Auth, got: {:?}", other),
        }
    }
}
