//! Bridge Transfer Management
//!
//! Manages cross-chain transfer lifecycle including:
//! - Transfer initiation
//! - Proof verification
//! - Status tracking
//! - Completion and failure handling

use crate::{AssetId, BridgeAddress, BridgeError, BridgeResult, ChainType};
use borsh::{BorshDeserialize, BorshSerialize};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::collections::HashMap;
use std::fmt;

/// Unique transfer identifier
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
pub struct TransferId(pub String);

impl TransferId {
    /// Create a new transfer ID
    pub fn new(id: impl Into<String>) -> Self {
        Self(id.into())
    }

    /// Generate transfer ID from parameters
    pub fn generate(
        sender: &BridgeAddress,
        recipient: &BridgeAddress,
        asset: &AssetId,
        amount: u128,
        nonce: u64,
    ) -> Self {
        let mut hasher = Sha256::new();
        hasher.update(&sender.address);
        hasher.update(&recipient.address);
        hasher.update(asset.identifier.as_bytes());
        hasher.update(amount.to_le_bytes());
        hasher.update(nonce.to_le_bytes());
        Self(hex::encode(&hasher.finalize()[..16]))
    }
}

impl fmt::Display for TransferId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

/// Transfer direction
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
pub enum TransferDirection {
    /// From external chain to Synor (Lock → Mint)
    Inbound,
    /// From Synor to external chain (Burn → Unlock)
    Outbound,
}

impl fmt::Display for TransferDirection {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            TransferDirection::Inbound => write!(f, "inbound"),
            TransferDirection::Outbound => write!(f, "outbound"),
        }
    }
}

/// Transfer status
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
pub enum TransferStatus {
    /// Transfer initiated, awaiting lock confirmation
    Pending,
    /// Lock confirmed, awaiting sufficient confirmations
    Locked,
    /// Sufficient confirmations, ready for minting/unlocking
    Confirmed,
    /// Tokens minted on destination (inbound) or burned (outbound)
    Minted,
    /// Original tokens unlocked (outbound only)
    Unlocked,
    /// Transfer completed successfully
    Completed,
    /// Transfer failed
    Failed,
    /// Transfer expired
    Expired,
    /// Transfer refunded
    Refunded,
}

impl TransferStatus {
    /// Check if transfer is finalized
    pub fn is_finalized(&self) -> bool {
        matches!(
            self,
            TransferStatus::Completed
                | TransferStatus::Failed
                | TransferStatus::Expired
                | TransferStatus::Refunded
        )
    }

    /// Check if transfer can be retried
    pub fn can_retry(&self) -> bool {
        matches!(
            self,
            TransferStatus::Pending | TransferStatus::Failed | TransferStatus::Expired
        )
    }
}

impl fmt::Display for TransferStatus {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            TransferStatus::Pending => write!(f, "pending"),
            TransferStatus::Locked => write!(f, "locked"),
            TransferStatus::Confirmed => write!(f, "confirmed"),
            TransferStatus::Minted => write!(f, "minted"),
            TransferStatus::Unlocked => write!(f, "unlocked"),
            TransferStatus::Completed => write!(f, "completed"),
            TransferStatus::Failed => write!(f, "failed"),
            TransferStatus::Expired => write!(f, "expired"),
            TransferStatus::Refunded => write!(f, "refunded"),
        }
    }
}

/// Cross-chain bridge transfer
#[derive(Debug, Clone, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
pub struct BridgeTransfer {
    /// Unique transfer ID
    pub id: TransferId,
    /// Transfer direction
    pub direction: TransferDirection,
    /// Source chain
    pub source_chain: ChainType,
    /// Destination chain
    pub destination_chain: ChainType,
    /// Asset being transferred
    pub asset: AssetId,
    /// Amount in smallest unit
    pub amount: u128,
    /// Sender address on source chain
    pub sender: BridgeAddress,
    /// Recipient address on destination chain
    pub recipient: BridgeAddress,
    /// Current status
    pub status: TransferStatus,
    /// Source chain transaction hash
    pub source_tx_hash: Option<Vec<u8>>,
    /// Destination chain transaction hash
    pub destination_tx_hash: Option<Vec<u8>>,
    /// Block number where lock occurred
    pub lock_block: Option<u64>,
    /// Current confirmations
    pub confirmations: u64,
    /// Required confirmations
    pub required_confirmations: u64,
    /// Transfer initiation timestamp
    pub created_at: u64,
    /// Last update timestamp
    pub updated_at: u64,
    /// Expiry timestamp (0 = no expiry)
    pub expires_at: u64,
    /// Error message if failed
    pub error: Option<String>,
    /// Nonce for uniqueness
    pub nonce: u64,
}

impl BridgeTransfer {
    /// Create a new transfer
    pub fn new(
        direction: TransferDirection,
        source_chain: ChainType,
        destination_chain: ChainType,
        asset: AssetId,
        amount: u128,
        sender: BridgeAddress,
        recipient: BridgeAddress,
        required_confirmations: u64,
        nonce: u64,
        current_time: u64,
    ) -> Self {
        let id = TransferId::generate(&sender, &recipient, &asset, amount, nonce);

        Self {
            id,
            direction,
            source_chain,
            destination_chain,
            asset,
            amount,
            sender,
            recipient,
            status: TransferStatus::Pending,
            source_tx_hash: None,
            destination_tx_hash: None,
            lock_block: None,
            confirmations: 0,
            required_confirmations,
            created_at: current_time,
            updated_at: current_time,
            expires_at: 0,
            error: None,
            nonce,
        }
    }

    /// Create inbound transfer (external → Synor)
    pub fn inbound(
        source_chain: ChainType,
        asset: AssetId,
        amount: u128,
        sender: BridgeAddress,
        recipient: BridgeAddress,
        required_confirmations: u64,
        nonce: u64,
        current_time: u64,
    ) -> Self {
        Self::new(
            TransferDirection::Inbound,
            source_chain,
            ChainType::Synor,
            asset,
            amount,
            sender,
            recipient,
            required_confirmations,
            nonce,
            current_time,
        )
    }

    /// Create outbound transfer (Synor → external)
    pub fn outbound(
        destination_chain: ChainType,
        asset: AssetId,
        amount: u128,
        sender: BridgeAddress,
        recipient: BridgeAddress,
        required_confirmations: u64,
        nonce: u64,
        current_time: u64,
    ) -> Self {
        Self::new(
            TransferDirection::Outbound,
            ChainType::Synor,
            destination_chain,
            asset,
            amount,
            sender,
            recipient,
            required_confirmations,
            nonce,
            current_time,
        )
    }

    /// Set expiry
    pub fn with_expiry(mut self, expires_at: u64) -> Self {
        self.expires_at = expires_at;
        self
    }

    /// Update status
    pub fn set_status(&mut self, status: TransferStatus, current_time: u64) {
        self.status = status;
        self.updated_at = current_time;
    }

    /// Set lock confirmed
    pub fn confirm_lock(&mut self, tx_hash: Vec<u8>, block_number: u64, current_time: u64) {
        self.source_tx_hash = Some(tx_hash);
        self.lock_block = Some(block_number);
        self.status = TransferStatus::Locked;
        self.updated_at = current_time;
    }

    /// Update confirmations
    pub fn update_confirmations(&mut self, confirmations: u64, current_time: u64) {
        self.confirmations = confirmations;
        self.updated_at = current_time;

        if confirmations >= self.required_confirmations && self.status == TransferStatus::Locked {
            self.status = TransferStatus::Confirmed;
        }
    }

    /// Mark as minted
    pub fn confirm_mint(&mut self, tx_hash: Vec<u8>, current_time: u64) {
        self.destination_tx_hash = Some(tx_hash);
        self.status = TransferStatus::Minted;
        self.updated_at = current_time;

        // For inbound, minting is the final step
        if self.direction == TransferDirection::Inbound {
            self.status = TransferStatus::Completed;
        }
    }

    /// Mark as unlocked
    pub fn confirm_unlock(&mut self, tx_hash: Vec<u8>, current_time: u64) {
        self.destination_tx_hash = Some(tx_hash);
        self.status = TransferStatus::Unlocked;
        self.updated_at = current_time;

        // For outbound, unlocking is the final step
        if self.direction == TransferDirection::Outbound {
            self.status = TransferStatus::Completed;
        }
    }

    /// Mark as failed
    pub fn fail(&mut self, error: impl Into<String>, current_time: u64) {
        self.error = Some(error.into());
        self.status = TransferStatus::Failed;
        self.updated_at = current_time;
    }

    /// Check if expired
    pub fn is_expired(&self, current_time: u64) -> bool {
        self.expires_at > 0 && current_time >= self.expires_at
    }

    /// Check if transfer has sufficient confirmations
    pub fn has_sufficient_confirmations(&self) -> bool {
        self.confirmations >= self.required_confirmations
    }

    /// Get completion percentage (0-100)
    pub fn completion_percentage(&self) -> u8 {
        match self.status {
            TransferStatus::Pending => 0,
            TransferStatus::Locked => 25,
            TransferStatus::Confirmed => 50,
            TransferStatus::Minted => 75,
            TransferStatus::Unlocked => 90,
            TransferStatus::Completed => 100,
            TransferStatus::Failed | TransferStatus::Expired | TransferStatus::Refunded => 0,
        }
    }
}

/// Transfer manager
pub struct TransferManager {
    /// Transfers by ID
    transfers: HashMap<TransferId, BridgeTransfer>,
    /// Transfers by sender
    by_sender: HashMap<BridgeAddress, Vec<TransferId>>,
    /// Transfers by recipient
    by_recipient: HashMap<BridgeAddress, Vec<TransferId>>,
    /// Pending transfers
    pending: Vec<TransferId>,
    /// Next nonce
    next_nonce: u64,
}

impl TransferManager {
    /// Create a new transfer manager
    pub fn new() -> Self {
        Self {
            transfers: HashMap::new(),
            by_sender: HashMap::new(),
            by_recipient: HashMap::new(),
            pending: Vec::new(),
            next_nonce: 0,
        }
    }

    /// Get next nonce
    pub fn next_nonce(&mut self) -> u64 {
        let nonce = self.next_nonce;
        self.next_nonce += 1;
        nonce
    }

    /// Create a new inbound transfer
    pub fn create_inbound(
        &mut self,
        source_chain: ChainType,
        asset: AssetId,
        amount: u128,
        sender: BridgeAddress,
        recipient: BridgeAddress,
        required_confirmations: u64,
        current_time: u64,
    ) -> BridgeResult<TransferId> {
        let nonce = self.next_nonce();
        let transfer = BridgeTransfer::inbound(
            source_chain,
            asset,
            amount,
            sender.clone(),
            recipient.clone(),
            required_confirmations,
            nonce,
            current_time,
        );

        self.register_transfer(transfer)
    }

    /// Create a new outbound transfer
    pub fn create_outbound(
        &mut self,
        destination_chain: ChainType,
        asset: AssetId,
        amount: u128,
        sender: BridgeAddress,
        recipient: BridgeAddress,
        required_confirmations: u64,
        current_time: u64,
    ) -> BridgeResult<TransferId> {
        let nonce = self.next_nonce();
        let transfer = BridgeTransfer::outbound(
            destination_chain,
            asset,
            amount,
            sender.clone(),
            recipient.clone(),
            required_confirmations,
            nonce,
            current_time,
        );

        self.register_transfer(transfer)
    }

    /// Register a transfer
    fn register_transfer(&mut self, transfer: BridgeTransfer) -> BridgeResult<TransferId> {
        let id = transfer.id.clone();

        if self.transfers.contains_key(&id) {
            return Err(BridgeError::TransferAlreadyExists(id.to_string()));
        }

        self.by_sender
            .entry(transfer.sender.clone())
            .or_default()
            .push(id.clone());

        self.by_recipient
            .entry(transfer.recipient.clone())
            .or_default()
            .push(id.clone());

        self.pending.push(id.clone());
        self.transfers.insert(id.clone(), transfer);

        Ok(id)
    }

    /// Get transfer by ID
    pub fn get(&self, id: &TransferId) -> Option<&BridgeTransfer> {
        self.transfers.get(id)
    }

    /// Get mutable transfer by ID
    pub fn get_mut(&mut self, id: &TransferId) -> Option<&mut BridgeTransfer> {
        self.transfers.get_mut(id)
    }

    /// Confirm lock
    pub fn confirm_lock(
        &mut self,
        id: &TransferId,
        tx_hash: Vec<u8>,
        block_number: u64,
        current_time: u64,
    ) -> BridgeResult<()> {
        let transfer = self
            .transfers
            .get_mut(id)
            .ok_or_else(|| BridgeError::TransferNotFound(id.to_string()))?;

        transfer.confirm_lock(tx_hash, block_number, current_time);
        Ok(())
    }

    /// Update confirmations
    pub fn update_confirmations(
        &mut self,
        id: &TransferId,
        confirmations: u64,
        current_time: u64,
    ) -> BridgeResult<()> {
        let transfer = self
            .transfers
            .get_mut(id)
            .ok_or_else(|| BridgeError::TransferNotFound(id.to_string()))?;

        transfer.update_confirmations(confirmations, current_time);

        // Remove from pending if confirmed
        if transfer.status == TransferStatus::Confirmed {
            self.pending.retain(|pid| pid != id);
        }

        Ok(())
    }

    /// Confirm mint
    pub fn confirm_mint(
        &mut self,
        id: &TransferId,
        tx_hash: Vec<u8>,
        current_time: u64,
    ) -> BridgeResult<()> {
        let transfer = self
            .transfers
            .get_mut(id)
            .ok_or_else(|| BridgeError::TransferNotFound(id.to_string()))?;

        transfer.confirm_mint(tx_hash, current_time);
        Ok(())
    }

    /// Confirm unlock
    pub fn confirm_unlock(
        &mut self,
        id: &TransferId,
        tx_hash: Vec<u8>,
        current_time: u64,
    ) -> BridgeResult<()> {
        let transfer = self
            .transfers
            .get_mut(id)
            .ok_or_else(|| BridgeError::TransferNotFound(id.to_string()))?;

        transfer.confirm_unlock(tx_hash, current_time);
        Ok(())
    }

    /// Mark transfer as failed
    pub fn fail_transfer(
        &mut self,
        id: &TransferId,
        error: impl Into<String>,
        current_time: u64,
    ) -> BridgeResult<()> {
        let transfer = self
            .transfers
            .get_mut(id)
            .ok_or_else(|| BridgeError::TransferNotFound(id.to_string()))?;

        transfer.fail(error, current_time);
        self.pending.retain(|pid| pid != id);

        Ok(())
    }

    /// Get transfers by sender
    pub fn by_sender(&self, sender: &BridgeAddress) -> Vec<&BridgeTransfer> {
        self.by_sender
            .get(sender)
            .map(|ids| ids.iter().filter_map(|id| self.transfers.get(id)).collect())
            .unwrap_or_default()
    }

    /// Get transfers by recipient
    pub fn by_recipient(&self, recipient: &BridgeAddress) -> Vec<&BridgeTransfer> {
        self.by_recipient
            .get(recipient)
            .map(|ids| ids.iter().filter_map(|id| self.transfers.get(id)).collect())
            .unwrap_or_default()
    }

    /// Get pending transfers
    pub fn pending_transfers(&self) -> Vec<&BridgeTransfer> {
        self.pending
            .iter()
            .filter_map(|id| self.transfers.get(id))
            .collect()
    }

    /// Get transfers ready for confirmation
    pub fn ready_for_confirmation(&self) -> Vec<&BridgeTransfer> {
        self.transfers
            .values()
            .filter(|t| t.status == TransferStatus::Confirmed)
            .collect()
    }

    /// Check and expire old transfers
    pub fn expire_old_transfers(&mut self, current_time: u64) -> Vec<TransferId> {
        let expired: Vec<TransferId> = self
            .transfers
            .iter()
            .filter(|(_, t)| !t.status.is_finalized() && t.is_expired(current_time))
            .map(|(id, _)| id.clone())
            .collect();

        for id in &expired {
            if let Some(transfer) = self.transfers.get_mut(id) {
                transfer.set_status(TransferStatus::Expired, current_time);
            }
            self.pending.retain(|pid| pid != id);
        }

        expired
    }

    /// Get transfer statistics
    pub fn stats(&self) -> TransferStats {
        let mut stats = TransferStats::default();

        for transfer in self.transfers.values() {
            stats.total_count += 1;
            stats.total_volume += transfer.amount;

            match transfer.status {
                TransferStatus::Pending | TransferStatus::Locked => stats.pending_count += 1,
                TransferStatus::Confirmed | TransferStatus::Minted | TransferStatus::Unlocked => {
                    stats.in_progress_count += 1
                }
                TransferStatus::Completed => {
                    stats.completed_count += 1;
                    stats.completed_volume += transfer.amount;
                }
                TransferStatus::Failed => stats.failed_count += 1,
                TransferStatus::Expired | TransferStatus::Refunded => stats.expired_count += 1,
            }

            match transfer.direction {
                TransferDirection::Inbound => stats.inbound_count += 1,
                TransferDirection::Outbound => stats.outbound_count += 1,
            }
        }

        stats
    }
}

impl Default for TransferManager {
    fn default() -> Self {
        Self::new()
    }
}

/// Transfer statistics
#[derive(Debug, Clone, Default)]
pub struct TransferStats {
    pub total_count: u64,
    pub total_volume: u128,
    pub pending_count: u64,
    pub in_progress_count: u64,
    pub completed_count: u64,
    pub completed_volume: u128,
    pub failed_count: u64,
    pub expired_count: u64,
    pub inbound_count: u64,
    pub outbound_count: u64,
}

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

    // ==================== Helper Functions ====================

    fn test_sender() -> BridgeAddress {
        BridgeAddress::from_eth([0xaa; 20])
    }

    fn test_recipient() -> BridgeAddress {
        BridgeAddress::from_synor([0xbb; 32])
    }

    fn test_sender_alt() -> BridgeAddress {
        BridgeAddress::from_eth([0xcc; 20])
    }

    fn test_recipient_alt() -> BridgeAddress {
        BridgeAddress::from_synor([0xdd; 32])
    }

    // ==================== TransferId Tests ====================

    #[test]
    fn test_transfer_id_new() {
        let id = TransferId::new("test-id");
        assert_eq!(id.0, "test-id");
    }

    #[test]
    fn test_transfer_id_display() {
        let id = TransferId::new("my-transfer-123");
        assert_eq!(format!("{}", id), "my-transfer-123");
    }

    #[test]
    fn test_transfer_id_generate_deterministic() {
        let sender = test_sender();
        let recipient = test_recipient();
        let asset = AssetId::eth();

        let id1 = TransferId::generate(&sender, &recipient, &asset, 1000, 0);
        let id2 = TransferId::generate(&sender, &recipient, &asset, 1000, 0);

        assert_eq!(id1, id2);
    }

    #[test]
    fn test_transfer_id_generate_different_nonce() {
        let sender = test_sender();
        let recipient = test_recipient();
        let asset = AssetId::eth();

        let id1 = TransferId::generate(&sender, &recipient, &asset, 1000, 0);
        let id2 = TransferId::generate(&sender, &recipient, &asset, 1000, 1);

        assert_ne!(id1, id2);
    }

    #[test]
    fn test_transfer_id_generate_different_amount() {
        let sender = test_sender();
        let recipient = test_recipient();
        let asset = AssetId::eth();

        let id1 = TransferId::generate(&sender, &recipient, &asset, 1000, 0);
        let id2 = TransferId::generate(&sender, &recipient, &asset, 2000, 0);

        assert_ne!(id1, id2);
    }

    #[test]
    fn test_transfer_id_generate_different_sender() {
        let sender1 = test_sender();
        let sender2 = test_sender_alt();
        let recipient = test_recipient();
        let asset = AssetId::eth();

        let id1 = TransferId::generate(&sender1, &recipient, &asset, 1000, 0);
        let id2 = TransferId::generate(&sender2, &recipient, &asset, 1000, 0);

        assert_ne!(id1, id2);
    }

    #[test]
    fn test_transfer_id_hash_equality() {
        use std::collections::HashSet;

        let id1 = TransferId::new("test-id");
        let id2 = TransferId::new("test-id");
        let id3 = TransferId::new("different-id");

        let mut set = HashSet::new();
        set.insert(id1.clone());

        assert!(set.contains(&id2));
        assert!(!set.contains(&id3));
    }

    // ==================== TransferDirection Tests ====================

    #[test]
    fn test_transfer_direction_display_inbound() {
        assert_eq!(format!("{}", TransferDirection::Inbound), "inbound");
    }

    #[test]
    fn test_transfer_direction_display_outbound() {
        assert_eq!(format!("{}", TransferDirection::Outbound), "outbound");
    }

    // ==================== TransferStatus Tests ====================

    #[test]
    fn test_transfer_status_is_finalized_completed() {
        assert!(TransferStatus::Completed.is_finalized());
    }

    #[test]
    fn test_transfer_status_is_finalized_failed() {
        assert!(TransferStatus::Failed.is_finalized());
    }

    #[test]
    fn test_transfer_status_is_finalized_expired() {
        assert!(TransferStatus::Expired.is_finalized());
    }

    #[test]
    fn test_transfer_status_is_finalized_refunded() {
        assert!(TransferStatus::Refunded.is_finalized());
    }

    #[test]
    fn test_transfer_status_is_not_finalized_pending() {
        assert!(!TransferStatus::Pending.is_finalized());
    }

    #[test]
    fn test_transfer_status_is_not_finalized_locked() {
        assert!(!TransferStatus::Locked.is_finalized());
    }

    #[test]
    fn test_transfer_status_is_not_finalized_confirmed() {
        assert!(!TransferStatus::Confirmed.is_finalized());
    }

    #[test]
    fn test_transfer_status_can_retry_pending() {
        assert!(TransferStatus::Pending.can_retry());
    }

    #[test]
    fn test_transfer_status_can_retry_failed() {
        assert!(TransferStatus::Failed.can_retry());
    }

    #[test]
    fn test_transfer_status_can_retry_expired() {
        assert!(TransferStatus::Expired.can_retry());
    }

    #[test]
    fn test_transfer_status_cannot_retry_completed() {
        assert!(!TransferStatus::Completed.can_retry());
    }

    #[test]
    fn test_transfer_status_cannot_retry_locked() {
        assert!(!TransferStatus::Locked.can_retry());
    }

    #[test]
    fn test_transfer_status_display_all() {
        assert_eq!(format!("{}", TransferStatus::Pending), "pending");
        assert_eq!(format!("{}", TransferStatus::Locked), "locked");
        assert_eq!(format!("{}", TransferStatus::Confirmed), "confirmed");
        assert_eq!(format!("{}", TransferStatus::Minted), "minted");
        assert_eq!(format!("{}", TransferStatus::Unlocked), "unlocked");
        assert_eq!(format!("{}", TransferStatus::Completed), "completed");
        assert_eq!(format!("{}", TransferStatus::Failed), "failed");
        assert_eq!(format!("{}", TransferStatus::Expired), "expired");
        assert_eq!(format!("{}", TransferStatus::Refunded), "refunded");
    }

    // ==================== BridgeTransfer Tests ====================

    #[test]
    fn test_bridge_transfer_new_inbound() {
        let current_time = 1700000000;
        let transfer = BridgeTransfer::inbound(
            ChainType::Ethereum,
            AssetId::eth(),
            1000,
            test_sender(),
            test_recipient(),
            12,
            0,
            current_time,
        );

        assert_eq!(transfer.direction, TransferDirection::Inbound);
        assert_eq!(transfer.source_chain, ChainType::Ethereum);
        assert_eq!(transfer.destination_chain, ChainType::Synor);
        assert_eq!(transfer.amount, 1000);
        assert_eq!(transfer.status, TransferStatus::Pending);
        assert_eq!(transfer.confirmations, 0);
        assert_eq!(transfer.required_confirmations, 12);
    }

    #[test]
    fn test_bridge_transfer_new_outbound() {
        let current_time = 1700000000;
        let transfer = BridgeTransfer::outbound(
            ChainType::Ethereum,
            AssetId::eth(),
            2000,
            test_recipient(),
            test_sender(),
            6,
            1,
            current_time,
        );

        assert_eq!(transfer.direction, TransferDirection::Outbound);
        assert_eq!(transfer.source_chain, ChainType::Synor);
        assert_eq!(transfer.destination_chain, ChainType::Ethereum);
    }

    #[test]
    fn test_bridge_transfer_with_expiry() {
        let current_time = 1700000000;
        let transfer = BridgeTransfer::inbound(
            ChainType::Ethereum,
            AssetId::eth(),
            1000,
            test_sender(),
            test_recipient(),
            12,
            0,
            current_time,
        )
        .with_expiry(current_time + 3600);

        assert_eq!(transfer.expires_at, current_time + 3600);
    }

    #[test]
    fn test_bridge_transfer_confirm_lock() {
        let current_time = 1700000000;
        let mut transfer = BridgeTransfer::inbound(
            ChainType::Ethereum,
            AssetId::eth(),
            1000,
            test_sender(),
            test_recipient(),
            12,
            0,
            current_time,
        );

        let tx_hash = vec![0x11; 32];
        transfer.confirm_lock(tx_hash.clone(), 100, current_time + 10);

        assert_eq!(transfer.status, TransferStatus::Locked);
        assert_eq!(transfer.source_tx_hash, Some(tx_hash));
        assert_eq!(transfer.lock_block, Some(100));
    }

    #[test]
    fn test_bridge_transfer_update_confirmations_partial() {
        let current_time = 1700000000;
        let mut transfer = BridgeTransfer::inbound(
            ChainType::Ethereum,
            AssetId::eth(),
            1000,
            test_sender(),
            test_recipient(),
            12,
            0,
            current_time,
        );

        transfer.confirm_lock(vec![0x11; 32], 100, current_time + 10);
        transfer.update_confirmations(6, current_time + 60);

        assert_eq!(transfer.status, TransferStatus::Locked);
        assert_eq!(transfer.confirmations, 6);
    }

    #[test]
    fn test_bridge_transfer_update_confirmations_sufficient() {
        let current_time = 1700000000;
        let mut transfer = BridgeTransfer::inbound(
            ChainType::Ethereum,
            AssetId::eth(),
            1000,
            test_sender(),
            test_recipient(),
            12,
            0,
            current_time,
        );

        transfer.confirm_lock(vec![0x11; 32], 100, current_time + 10);
        transfer.update_confirmations(12, current_time + 120);

        assert_eq!(transfer.status, TransferStatus::Confirmed);
        assert!(transfer.has_sufficient_confirmations());
    }

    #[test]
    fn test_bridge_transfer_confirm_mint_inbound() {
        let current_time = 1700000000;
        let mut transfer = BridgeTransfer::inbound(
            ChainType::Ethereum,
            AssetId::eth(),
            1000,
            test_sender(),
            test_recipient(),
            12,
            0,
            current_time,
        );

        transfer.confirm_lock(vec![0x11; 32], 100, current_time + 10);
        transfer.update_confirmations(12, current_time + 120);

        let dest_tx_hash = vec![0x22; 32];
        transfer.confirm_mint(dest_tx_hash.clone(), current_time + 200);

        assert_eq!(transfer.status, TransferStatus::Completed);
        assert_eq!(transfer.destination_tx_hash, Some(dest_tx_hash));
    }

    #[test]
    fn test_bridge_transfer_confirm_unlock_outbound() {
        let current_time = 1700000000;
        let mut transfer = BridgeTransfer::outbound(
            ChainType::Ethereum,
            AssetId::eth(),
            1000,
            test_recipient(),
            test_sender(),
            12,
            0,
            current_time,
        );

        transfer.confirm_lock(vec![0x11; 32], 100, current_time + 10);
        transfer.update_confirmations(12, current_time + 120);

        let dest_tx_hash = vec![0x33; 32];
        transfer.confirm_unlock(dest_tx_hash.clone(), current_time + 200);

        assert_eq!(transfer.status, TransferStatus::Completed);
    }

    #[test]
    fn test_bridge_transfer_fail() {
        let current_time = 1700000000;
        let mut transfer = BridgeTransfer::inbound(
            ChainType::Ethereum,
            AssetId::eth(),
            1000,
            test_sender(),
            test_recipient(),
            12,
            0,
            current_time,
        );

        transfer.fail("Proof verification failed", current_time + 50);

        assert_eq!(transfer.status, TransferStatus::Failed);
        assert_eq!(transfer.error, Some("Proof verification failed".to_string()));
    }

    #[test]
    fn test_bridge_transfer_is_expired_no_expiry() {
        let current_time = 1700000000;
        let transfer = BridgeTransfer::inbound(
            ChainType::Ethereum,
            AssetId::eth(),
            1000,
            test_sender(),
            test_recipient(),
            12,
            0,
            current_time,
        );

        assert!(!transfer.is_expired(current_time + 1000000));
    }

    #[test]
    fn test_bridge_transfer_is_expired_before_expiry() {
        let current_time = 1700000000;
        let transfer = BridgeTransfer::inbound(
            ChainType::Ethereum,
            AssetId::eth(),
            1000,
            test_sender(),
            test_recipient(),
            12,
            0,
            current_time,
        )
        .with_expiry(current_time + 3600);

        assert!(!transfer.is_expired(current_time + 1800));
    }

    #[test]
    fn test_bridge_transfer_is_expired_at_expiry() {
        let current_time = 1700000000;
        let transfer = BridgeTransfer::inbound(
            ChainType::Ethereum,
            AssetId::eth(),
            1000,
            test_sender(),
            test_recipient(),
            12,
            0,
            current_time,
        )
        .with_expiry(current_time + 3600);

        assert!(transfer.is_expired(current_time + 3600));
    }

    #[test]
    fn test_completion_percentage_pending() {
        let current_time = 1700000000;
        let transfer = BridgeTransfer::inbound(
            ChainType::Ethereum,
            AssetId::eth(),
            1000,
            test_sender(),
            test_recipient(),
            12,
            0,
            current_time,
        );

        assert_eq!(transfer.completion_percentage(), 0);
    }

    #[test]
    fn test_completion_percentage_locked() {
        let current_time = 1700000000;
        let mut transfer = BridgeTransfer::inbound(
            ChainType::Ethereum,
            AssetId::eth(),
            1000,
            test_sender(),
            test_recipient(),
            12,
            0,
            current_time,
        );

        transfer.confirm_lock(vec![0x11; 32], 100, current_time + 10);
        assert_eq!(transfer.completion_percentage(), 25);
    }

    #[test]
    fn test_completion_percentage_completed() {
        let current_time = 1700000000;
        let mut transfer = BridgeTransfer::inbound(
            ChainType::Ethereum,
            AssetId::eth(),
            1000,
            test_sender(),
            test_recipient(),
            12,
            0,
            current_time,
        );

        transfer.confirm_lock(vec![0x11; 32], 100, current_time + 10);
        transfer.update_confirmations(12, current_time + 120);
        transfer.confirm_mint(vec![0x22; 32], current_time + 200);

        assert_eq!(transfer.completion_percentage(), 100);
    }

    // ==================== TransferManager Tests ====================

    #[test]
    fn test_transfer_manager_new() {
        let manager = TransferManager::new();
        assert_eq!(manager.pending_transfers().len(), 0);
    }

    #[test]
    fn test_transfer_manager_default() {
        let manager = TransferManager::default();
        assert_eq!(manager.pending_transfers().len(), 0);
    }

    #[test]
    fn test_transfer_manager_next_nonce() {
        let mut manager = TransferManager::new();

        assert_eq!(manager.next_nonce(), 0);
        assert_eq!(manager.next_nonce(), 1);
        assert_eq!(manager.next_nonce(), 2);
    }

    #[test]
    fn test_transfer_manager_create_inbound() {
        let mut manager = TransferManager::new();
        let current_time = 1700000000;

        let id = manager
            .create_inbound(
                ChainType::Ethereum,
                AssetId::eth(),
                1000,
                test_sender(),
                test_recipient(),
                12,
                current_time,
            )
            .unwrap();

        let transfer = manager.get(&id).unwrap();
        assert_eq!(transfer.direction, TransferDirection::Inbound);
        assert_eq!(transfer.amount, 1000);
    }

    #[test]
    fn test_transfer_manager_create_outbound() {
        let mut manager = TransferManager::new();
        let current_time = 1700000000;

        let id = manager
            .create_outbound(
                ChainType::Ethereum,
                AssetId::eth(),
                500,
                test_recipient(),
                test_sender(),
                6,
                current_time,
            )
            .unwrap();

        let transfer = manager.get(&id).unwrap();
        assert_eq!(transfer.direction, TransferDirection::Outbound);
    }

    #[test]
    fn test_transfer_manager_get() {
        let mut manager = TransferManager::new();
        let current_time = 1700000000;

        let id = manager
            .create_inbound(
                ChainType::Ethereum,
                AssetId::eth(),
                1000,
                test_sender(),
                test_recipient(),
                12,
                current_time,
            )
            .unwrap();

        assert!(manager.get(&id).is_some());
        assert!(manager.get(&TransferId::new("nonexistent")).is_none());
    }

    #[test]
    fn test_transfer_manager_get_mut() {
        let mut manager = TransferManager::new();
        let current_time = 1700000000;

        let id = manager
            .create_inbound(
                ChainType::Ethereum,
                AssetId::eth(),
                1000,
                test_sender(),
                test_recipient(),
                12,
                current_time,
            )
            .unwrap();

        if let Some(transfer) = manager.get_mut(&id) {
            transfer.expires_at = current_time + 3600;
        }

        let transfer = manager.get(&id).unwrap();
        assert_eq!(transfer.expires_at, current_time + 3600);
    }

    #[test]
    fn test_transfer_manager_by_sender() {
        let mut manager = TransferManager::new();
        let current_time = 1700000000;

        let sender = test_sender();

        manager
            .create_inbound(
                ChainType::Ethereum,
                AssetId::eth(),
                1000,
                sender.clone(),
                test_recipient(),
                12,
                current_time,
            )
            .unwrap();

        manager
            .create_inbound(
                ChainType::Ethereum,
                AssetId::eth(),
                2000,
                sender.clone(),
                test_recipient(),
                12,
                current_time,
            )
            .unwrap();

        let transfers = manager.by_sender(&sender);
        assert_eq!(transfers.len(), 2);
    }

    #[test]
    fn test_transfer_manager_by_recipient() {
        let mut manager = TransferManager::new();
        let current_time = 1700000000;

        let recipient = test_recipient();

        manager
            .create_inbound(
                ChainType::Ethereum,
                AssetId::eth(),
                1000,
                test_sender(),
                recipient.clone(),
                12,
                current_time,
            )
            .unwrap();

        let transfers = manager.by_recipient(&recipient);
        assert_eq!(transfers.len(), 1);
    }

    #[test]
    fn test_transfer_manager_pending_transfers() {
        let mut manager = TransferManager::new();
        let current_time = 1700000000;

        let id = manager
            .create_inbound(
                ChainType::Ethereum,
                AssetId::eth(),
                1000,
                test_sender(),
                test_recipient(),
                12,
                current_time,
            )
            .unwrap();

        assert_eq!(manager.pending_transfers().len(), 1);

        manager.confirm_lock(&id, vec![0x11; 32], 100, current_time + 10).unwrap();
        manager.update_confirmations(&id, 12, current_time + 120).unwrap();

        assert_eq!(manager.pending_transfers().len(), 0);
    }

    #[test]
    fn test_transfer_manager_ready_for_confirmation() {
        let mut manager = TransferManager::new();
        let current_time = 1700000000;

        let id = manager
            .create_inbound(
                ChainType::Ethereum,
                AssetId::eth(),
                1000,
                test_sender(),
                test_recipient(),
                12,
                current_time,
            )
            .unwrap();

        assert_eq!(manager.ready_for_confirmation().len(), 0);

        manager.confirm_lock(&id, vec![0x11; 32], 100, current_time + 10).unwrap();
        manager.update_confirmations(&id, 12, current_time + 120).unwrap();

        assert_eq!(manager.ready_for_confirmation().len(), 1);
    }

    #[test]
    fn test_transfer_manager_expire_old_transfers() {
        let mut manager = TransferManager::new();
        let current_time = 1700000000;

        let id = manager
            .create_inbound(
                ChainType::Ethereum,
                AssetId::eth(),
                1000,
                test_sender(),
                test_recipient(),
                12,
                current_time,
            )
            .unwrap();

        if let Some(transfer) = manager.get_mut(&id) {
            transfer.expires_at = current_time + 1000;
        }

        let expired = manager.expire_old_transfers(current_time + 500);
        assert!(expired.is_empty());

        let expired = manager.expire_old_transfers(current_time + 1500);
        assert_eq!(expired.len(), 1);

        let transfer = manager.get(&id).unwrap();
        assert_eq!(transfer.status, TransferStatus::Expired);
    }

    #[test]
    fn test_transfer_manager_fail_transfer() {
        let mut manager = TransferManager::new();
        let current_time = 1700000000;

        let id = manager
            .create_inbound(
                ChainType::Ethereum,
                AssetId::eth(),
                1000,
                test_sender(),
                test_recipient(),
                12,
                current_time,
            )
            .unwrap();

        manager.fail_transfer(&id, "Verification failed", current_time + 50).unwrap();

        let transfer = manager.get(&id).unwrap();
        assert_eq!(transfer.status, TransferStatus::Failed);
    }

    #[test]
    fn test_transfer_manager_fail_nonexistent() {
        let mut manager = TransferManager::new();
        let result = manager.fail_transfer(&TransferId::new("nonexistent"), "Error", 0);
        assert!(result.is_err());
    }

    #[test]
    fn test_transfer_manager_stats() {
        let mut manager = TransferManager::new();
        let current_time = 1700000000;

        let id1 = manager
            .create_inbound(
                ChainType::Ethereum,
                AssetId::eth(),
                1000,
                test_sender(),
                test_recipient(),
                12,
                current_time,
            )
            .unwrap();

        manager
            .create_outbound(
                ChainType::Ethereum,
                AssetId::eth(),
                500,
                test_recipient(),
                test_sender(),
                12,
                current_time,
            )
            .unwrap();

        manager.confirm_lock(&id1, vec![0x11; 32], 100, current_time).unwrap();
        manager.update_confirmations(&id1, 12, current_time).unwrap();
        manager.confirm_mint(&id1, vec![0x22; 32], current_time).unwrap();

        let stats = manager.stats();
        assert_eq!(stats.total_count, 2);
        assert_eq!(stats.completed_count, 1);
        assert_eq!(stats.pending_count, 1);
        assert_eq!(stats.inbound_count, 1);
        assert_eq!(stats.outbound_count, 1);
    }

    #[test]
    fn test_complete_inbound_lifecycle() {
        let mut manager = TransferManager::new();
        let current_time = 1700000000;

        let id = manager
            .create_inbound(
                ChainType::Ethereum,
                AssetId::eth(),
                1000,
                test_sender(),
                test_recipient(),
                12,
                current_time,
            )
            .unwrap();

        let transfer = manager.get(&id).unwrap();
        assert_eq!(transfer.status, TransferStatus::Pending);

        manager.confirm_lock(&id, vec![0x11; 32], 100, current_time + 60).unwrap();
        let transfer = manager.get(&id).unwrap();
        assert_eq!(transfer.status, TransferStatus::Locked);

        manager.update_confirmations(&id, 6, current_time + 120).unwrap();
        let transfer = manager.get(&id).unwrap();
        assert_eq!(transfer.status, TransferStatus::Locked);

        manager.update_confirmations(&id, 12, current_time + 180).unwrap();
        let transfer = manager.get(&id).unwrap();
        assert_eq!(transfer.status, TransferStatus::Confirmed);

        manager.confirm_mint(&id, vec![0x22; 32], current_time + 240).unwrap();
        let transfer = manager.get(&id).unwrap();
        assert_eq!(transfer.status, TransferStatus::Completed);
    }

    #[test]
    fn test_complete_outbound_lifecycle() {
        let mut manager = TransferManager::new();
        let current_time = 1700000000;

        let id = manager
            .create_outbound(
                ChainType::Ethereum,
                AssetId::eth(),
                500,
                test_recipient(),
                test_sender(),
                6,
                current_time,
            )
            .unwrap();

        let transfer = manager.get(&id).unwrap();
        assert_eq!(transfer.status, TransferStatus::Pending);

        manager.confirm_lock(&id, vec![0x11; 32], 100, current_time + 60).unwrap();
        let transfer = manager.get(&id).unwrap();
        assert_eq!(transfer.status, TransferStatus::Locked);

        manager.update_confirmations(&id, 6, current_time + 120).unwrap();
        let transfer = manager.get(&id).unwrap();
        assert_eq!(transfer.status, TransferStatus::Confirmed);

        manager.confirm_unlock(&id, vec![0x33; 32], current_time + 180).unwrap();
        let transfer = manager.get(&id).unwrap();
        assert_eq!(transfer.status, TransferStatus::Completed);
    }

    #[test]
    fn test_transfer_manager_confirm_lock_nonexistent() {
        let mut manager = TransferManager::new();
        let result = manager.confirm_lock(&TransferId::new("nonexistent"), vec![], 100, 0);
        assert!(result.is_err());
    }

    #[test]
    fn test_transfer_manager_update_confirmations_nonexistent() {
        let mut manager = TransferManager::new();
        let result = manager.update_confirmations(&TransferId::new("nonexistent"), 12, 0);
        assert!(result.is_err());
    }

    #[test]
    fn test_transfer_manager_confirm_mint_nonexistent() {
        let mut manager = TransferManager::new();
        let result = manager.confirm_mint(&TransferId::new("nonexistent"), vec![], 0);
        assert!(result.is_err());
    }

    #[test]
    fn test_transfer_manager_confirm_unlock_nonexistent() {
        let mut manager = TransferManager::new();
        let result = manager.confirm_unlock(&TransferId::new("nonexistent"), vec![], 0);
        assert!(result.is_err());
    }

    #[test]
    fn test_transfer_set_status() {
        let current_time = 1700000000;
        let mut transfer = BridgeTransfer::inbound(
            ChainType::Ethereum,
            AssetId::eth(),
            1000,
            test_sender(),
            test_recipient(),
            12,
            0,
            current_time,
        );

        transfer.set_status(TransferStatus::Refunded, current_time + 100);
        assert_eq!(transfer.status, TransferStatus::Refunded);
        assert_eq!(transfer.updated_at, current_time + 100);
    }

    #[test]
    fn test_transfer_has_sufficient_confirmations() {
        let current_time = 1700000000;
        let mut transfer = BridgeTransfer::inbound(
            ChainType::Ethereum,
            AssetId::eth(),
            1000,
            test_sender(),
            test_recipient(),
            12,
            0,
            current_time,
        );

        assert!(!transfer.has_sufficient_confirmations());

        transfer.confirmations = 6;
        assert!(!transfer.has_sufficient_confirmations());

        transfer.confirmations = 12;
        assert!(transfer.has_sufficient_confirmations());

        transfer.confirmations = 20;
        assert!(transfer.has_sufficient_confirmations());
    }
}
