//! Nonce manager for handling transaction nonces.

use crate::rpc::RpcClient;
use crate::Result;
use alloy_primitives::Address;
use std::sync::Arc;
use tokio::sync::Mutex;

/// NonceManager for managing transaction nonces
pub struct NonceManager {
    rpc: Arc<RpcClient>,
    address: Address,
    nonce: Mutex<Option<u64>>,
}

impl NonceManager {
    /// Create a new nonce manager
    pub fn new(rpc: Arc<RpcClient>, address: Address) -> Self {
        Self {
            rpc,
            address,
            nonce: Mutex::new(None),
        }
    }

    /// Get the current nonce (cached)
    pub async fn get_nonce(&self) -> Result<u64> {
        let mut guard = self.nonce.lock().await;
        if let Some(nonce) = *guard {
            return Ok(nonce);
        }

        // Fetch from RPC
        let nonce = self.rpc.get_transaction_count(self.address, "pending").await?;
        *guard = Some(nonce);
        Ok(nonce)
    }

    /// Get and increment the nonce
    pub async fn get_and_increment_nonce(&self) -> Result<u64> {
        let mut guard = self.nonce.lock().await;
        let nonce = if let Some(n) = *guard {
            n
        } else {
            // Fetch from RPC
            self.rpc.get_transaction_count(self.address, "pending").await?
        };

        *guard = Some(nonce + 1);
        Ok(nonce)
    }

    /// Reset the nonce cache (useful after errors)
    pub fn reset_nonce(&self) {
        let mut guard = self.nonce.blocking_lock();
        *guard = None;
    }

    /// Set a specific nonce
    pub async fn set_nonce(&self, nonce: u64) {
        let mut guard = self.nonce.lock().await;
        *guard = Some(nonce);
    }
}

/// Create a new nonce manager (convenience function)
pub fn create_nonce_manager(rpc: Arc<RpcClient>, address: Address) -> NonceManager {
    NonceManager::new(rpc, address)
}
