//! Cross-Chain Bridge Infrastructure for Synor
//!
//! This crate provides bridge infrastructure for cross-chain asset transfers,
//! enabling Synor to interoperate with external blockchains like Ethereum.
//!
//! # Architecture
//!
//! ```text
//! ┌────────────────────────────────────────────────────────────────────┐
//! │                    Synor Bridge Architecture                        │
//! ├────────────────────────────────────────────────────────────────────┤
//! │                                                                    │
//! │  ┌──────────────┐    Lock-Mint    ┌──────────────────────────┐   │
//! │  │   External   │ ──────────────► │     Synor Chain          │   │
//! │  │   Chain      │                 │                          │   │
//! │  │  (Ethereum)  │ ◄────────────── │   Wrapped Tokens (sETH)  │   │
//! │  │              │    Burn-Unlock  │                          │   │
//! │  └──────────────┘                 └──────────────────────────┘   │
//! │                                                                    │
//! │  ┌─────────────────────────────────────────────────────────────┐  │
//! │  │                    Bridge Components                         │  │
//! │  │  ┌───────────┐  ┌───────────┐  ┌───────────┐  ┌──────────┐ │  │
//! │  │  │   Vault   │  │  Relayer  │  │ Validator │  │  Oracle  │ │  │
//! │  │  │  (Locks)  │  │ (Events)  │  │ (Proofs)  │  │ (Price)  │ │  │
//! │  │  └───────────┘  └───────────┘  └───────────┘  └──────────┘ │  │
//! │  └─────────────────────────────────────────────────────────────┘  │
//! │                                                                    │
//! └────────────────────────────────────────────────────────────────────┘
//! ```
//!
//! # Bridge Flow (Lock-Mint)
//!
//! ```text
//! User                  Ethereum              Relayer              Synor
//!  │                       │                    │                    │
//!  ├── Lock ETH ─────────►│                    │                    │
//!  │                       ├── LockEvent ─────►│                    │
//!  │                       │                    ├── SubmitProof ───►│
//!  │                       │                    │                    ├─ Verify
//!  │                       │                    │                    ├─ Mint sETH
//!  │◄─────────────────────────────────────────────────── sETH ──────┤
//! ```
//!
//! # Supported Bridges
//!
//! - **Ethereum**: Lock-Mint bridge for ETH and ERC-20 tokens
//! - **Bitcoin**: HTLC-based atomic swaps (via synor-ibc)
//! - **IBC Chains**: Native IBC transfers (via synor-ibc)

#![allow(dead_code)]

pub mod ethereum;
pub mod transfer;
pub mod vault;

pub use ethereum::{
    EthereumBridge, EthereumBridgeConfig, EthereumEvent, EthereumEventType, WrappedToken,
};
pub use transfer::{
    BridgeTransfer, TransferDirection, TransferId, TransferManager, TransferStatus,
};
pub use vault::{LockedAsset, Vault, VaultId, VaultManager};

use borsh::{BorshDeserialize, BorshSerialize};
use serde::{Deserialize, Serialize};
use std::fmt;
use thiserror::Error;

/// Bridge protocol version
pub const BRIDGE_VERSION: &str = "1.0.0";

/// Maximum transfer amount for safety (in smallest units)
pub const MAX_TRANSFER_AMOUNT: u128 = 1_000_000_000_000_000_000_000_000; // 1M tokens

/// Minimum confirmations for Ethereum deposits
pub const ETH_MIN_CONFIRMATIONS: u64 = 12;

/// Minimum confirmations for Bitcoin deposits
pub const BTC_MIN_CONFIRMATIONS: u64 = 6;

/// Bridge chain identifier
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
pub enum ChainType {
    /// Synor native chain
    Synor,
    /// Ethereum mainnet
    Ethereum,
    /// Ethereum Sepolia testnet
    EthereumSepolia,
    /// Bitcoin mainnet
    Bitcoin,
    /// Bitcoin testnet
    BitcoinTestnet,
    /// Cosmos SDK chain (IBC)
    Cosmos(String),
    /// Custom chain
    Custom(String),
}

impl ChainType {
    /// Get chain ID for Ethereum networks
    pub fn eth_chain_id(&self) -> Option<u64> {
        match self {
            ChainType::Ethereum => Some(1),
            ChainType::EthereumSepolia => Some(11155111),
            _ => None,
        }
    }

    /// Check if this is an EVM-compatible chain
    pub fn is_evm(&self) -> bool {
        matches!(self, ChainType::Ethereum | ChainType::EthereumSepolia)
    }
}

impl fmt::Display for ChainType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            ChainType::Synor => write!(f, "synor"),
            ChainType::Ethereum => write!(f, "ethereum"),
            ChainType::EthereumSepolia => write!(f, "ethereum-sepolia"),
            ChainType::Bitcoin => write!(f, "bitcoin"),
            ChainType::BitcoinTestnet => write!(f, "bitcoin-testnet"),
            ChainType::Cosmos(id) => write!(f, "cosmos:{}", id),
            ChainType::Custom(id) => write!(f, "custom:{}", id),
        }
    }
}

/// Asset identifier across chains
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
pub struct AssetId {
    /// Chain where the asset originates
    pub chain: ChainType,
    /// Asset identifier (contract address for ERC-20, "native" for ETH)
    pub identifier: String,
    /// Asset symbol
    pub symbol: String,
    /// Decimal places
    pub decimals: u8,
}

impl AssetId {
    /// Create native Ethereum asset
    pub fn eth() -> Self {
        Self {
            chain: ChainType::Ethereum,
            identifier: "native".to_string(),
            symbol: "ETH".to_string(),
            decimals: 18,
        }
    }

    /// Create ERC-20 asset
    pub fn erc20(address: impl Into<String>, symbol: impl Into<String>, decimals: u8) -> Self {
        Self {
            chain: ChainType::Ethereum,
            identifier: address.into(),
            symbol: symbol.into(),
            decimals,
        }
    }

    /// Create Synor native asset
    pub fn synor() -> Self {
        Self {
            chain: ChainType::Synor,
            identifier: "native".to_string(),
            symbol: "SYNOR".to_string(),
            decimals: 18,
        }
    }

    /// Create wrapped asset on Synor
    pub fn wrapped(original: &AssetId) -> Self {
        Self {
            chain: ChainType::Synor,
            identifier: format!("wrapped:{}", original.identifier),
            symbol: format!("s{}", original.symbol),
            decimals: original.decimals,
        }
    }

    /// Check if this is a wrapped asset
    pub fn is_wrapped(&self) -> bool {
        self.identifier.starts_with("wrapped:")
    }

    /// Get the original asset if this is wrapped
    pub fn unwrap_identifier(&self) -> Option<&str> {
        self.identifier.strip_prefix("wrapped:")
    }
}

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

/// Bridge address (unified format for cross-chain addresses)
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, BorshSerialize, BorshDeserialize)]
pub struct BridgeAddress {
    /// Chain type
    pub chain: ChainType,
    /// Address bytes
    pub address: Vec<u8>,
}

impl BridgeAddress {
    /// Create from Ethereum address (20 bytes)
    pub fn from_eth(address: [u8; 20]) -> Self {
        Self {
            chain: ChainType::Ethereum,
            address: address.to_vec(),
        }
    }

    /// Create from Synor address (32 bytes)
    pub fn from_synor(address: [u8; 32]) -> Self {
        Self {
            chain: ChainType::Synor,
            address: address.to_vec(),
        }
    }

    /// Create from hex string
    pub fn from_hex(chain: ChainType, hex: &str) -> Result<Self, BridgeError> {
        let hex = hex.strip_prefix("0x").unwrap_or(hex);
        let address = hex::decode(hex)
            .map_err(|e| BridgeError::InvalidAddress(format!("invalid hex: {}", e)))?;
        Ok(Self { chain, address })
    }

    /// Get as Ethereum address
    pub fn as_eth(&self) -> Option<[u8; 20]> {
        if self.address.len() == 20 {
            let mut arr = [0u8; 20];
            arr.copy_from_slice(&self.address);
            Some(arr)
        } else {
            None
        }
    }

    /// Get as Synor address
    pub fn as_synor(&self) -> Option<[u8; 32]> {
        if self.address.len() == 32 {
            let mut arr = [0u8; 32];
            arr.copy_from_slice(&self.address);
            Some(arr)
        } else {
            None
        }
    }

    /// Get as hex string
    pub fn to_hex(&self) -> String {
        format!("0x{}", hex::encode(&self.address))
    }
}

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

/// Bridge error types
#[derive(Debug, Error)]
pub enum BridgeError {
    #[error("Invalid address: {0}")]
    InvalidAddress(String),

    #[error("Invalid amount: {0}")]
    InvalidAmount(String),

    #[error("Transfer not found: {0}")]
    TransferNotFound(String),

    #[error("Vault not found: {0}")]
    VaultNotFound(String),

    #[error("Insufficient balance: need {required}, have {available}")]
    InsufficientBalance { required: u128, available: u128 },

    #[error("Asset not supported: {0}")]
    AssetNotSupported(String),

    #[error("Chain not supported: {0}")]
    ChainNotSupported(String),

    #[error("Transfer already exists: {0}")]
    TransferAlreadyExists(String),

    #[error("Transfer already completed: {0}")]
    TransferAlreadyCompleted(String),

    #[error("Invalid proof: {0}")]
    InvalidProof(String),

    #[error("Insufficient confirmations: need {required}, have {actual}")]
    InsufficientConfirmations { required: u64, actual: u64 },

    #[error("Bridge paused")]
    BridgePaused,

    #[error("Rate limit exceeded")]
    RateLimitExceeded,

    #[error("Signature verification failed: {0}")]
    SignatureVerificationFailed(String),

    #[error("Oracle error: {0}")]
    OracleError(String),

    #[error("Relayer error: {0}")]
    RelayerError(String),

    #[error("Internal error: {0}")]
    Internal(String),
}

/// Result type for bridge operations
pub type BridgeResult<T> = std::result::Result<T, BridgeError>;

/// Bridge trait for implementing cross-chain bridges
#[async_trait::async_trait]
pub trait Bridge: Send + Sync {
    /// Get the source chain type
    fn source_chain(&self) -> ChainType;

    /// Get the destination chain type
    fn destination_chain(&self) -> ChainType;

    /// Check if an asset is supported
    fn supports_asset(&self, asset: &AssetId) -> bool;

    /// Get minimum confirmations required
    fn min_confirmations(&self) -> u64;

    /// Lock assets on the source chain
    async fn lock(&self, transfer: &BridgeTransfer) -> BridgeResult<TransferId>;

    /// Verify a lock proof
    async fn verify_lock(&self, transfer_id: &TransferId) -> BridgeResult<bool>;

    /// Mint wrapped tokens on the destination chain
    async fn mint(&self, transfer: &BridgeTransfer) -> BridgeResult<()>;

    /// Burn wrapped tokens (for redemption)
    async fn burn(&self, transfer: &BridgeTransfer) -> BridgeResult<TransferId>;

    /// Unlock original tokens
    async fn unlock(&self, transfer_id: &TransferId) -> BridgeResult<()>;

    /// Get transfer status
    async fn get_transfer_status(&self, transfer_id: &TransferId) -> BridgeResult<TransferStatus>;
}

/// Bridge event for tracking cross-chain activity
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BridgeEvent {
    /// Asset locked on source chain
    AssetLocked {
        transfer_id: TransferId,
        asset: AssetId,
        amount: u128,
        sender: BridgeAddress,
        recipient: BridgeAddress,
    },
    /// Lock verified
    LockVerified {
        transfer_id: TransferId,
        confirmations: u64,
    },
    /// Wrapped token minted
    TokenMinted {
        transfer_id: TransferId,
        asset: AssetId,
        amount: u128,
        recipient: BridgeAddress,
    },
    /// Token burned for redemption
    TokenBurned {
        transfer_id: TransferId,
        asset: AssetId,
        amount: u128,
        sender: BridgeAddress,
    },
    /// Original asset unlocked
    AssetUnlocked {
        transfer_id: TransferId,
        recipient: BridgeAddress,
    },
    /// Transfer completed
    TransferCompleted { transfer_id: TransferId },
    /// Transfer failed
    TransferFailed {
        transfer_id: TransferId,
        reason: String,
    },
}

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

    #[test]
    fn test_chain_type() {
        assert!(ChainType::Ethereum.is_evm());
        assert!(!ChainType::Bitcoin.is_evm());
        assert_eq!(ChainType::Ethereum.eth_chain_id(), Some(1));
    }

    #[test]
    fn test_asset_id() {
        let eth = AssetId::eth();
        assert_eq!(eth.symbol, "ETH");
        assert_eq!(eth.decimals, 18);

        let wrapped = AssetId::wrapped(&eth);
        assert!(wrapped.is_wrapped());
        assert_eq!(wrapped.symbol, "sETH");
    }

    #[test]
    fn test_bridge_address() {
        let eth_addr = BridgeAddress::from_eth([0xde; 20]);
        assert_eq!(eth_addr.address.len(), 20);
        assert!(eth_addr.as_eth().is_some());

        let hex_addr = BridgeAddress::from_hex(ChainType::Ethereum, "0xdeadbeef").unwrap();
        assert_eq!(hex_addr.address, vec![0xde, 0xad, 0xbe, 0xef]);
    }
}
