//! P2P networking layer for Synor blockchain.
//!
//! This crate provides the networking infrastructure for Synor nodes:
//! - Peer discovery and management
//! - Block and transaction gossip
//! - Block synchronization
//! - RPC request/response protocol
//!
//! # Architecture
//!
//! The network layer is built on libp2p and consists of:
//! - **GossipSub**: For broadcasting blocks and transactions
//! - **Kademlia DHT**: For peer discovery
//! - **Request-Response**: For direct block/header requests
//! - **Identify**: For peer identification
//! - **Ping**: For connection health checks
//!
//! # Topics
//!
//! GossipSub topics:
//! - `synor/blocks/1.0.0`: New block announcements
//! - `synor/txs/1.0.0`: Transaction announcements
//! - `synor/headers/1.0.0`: Header announcements

#![allow(dead_code)]

pub mod behaviour;
pub mod config;
pub mod eclipse;
pub mod message;
pub mod partition;
pub mod peer;
pub mod protocol;
pub mod rate_limit;
pub mod ratelimit;
pub mod reputation;
pub mod service;
pub mod sync;

pub use behaviour::SynorBehaviour;
pub use config::NetworkConfig;
pub use eclipse::{EclipseConfig, EclipseProtection, EclipseStats, PeerType};
pub use message::{BlockAnnouncement, Message, TransactionAnnouncement};
pub use partition::{
    PartitionConfig, PartitionDetector, PartitionReason, PartitionStats, PartitionStatus,
};
pub use peer::{PeerInfo, PeerManager, PeerState};
pub use protocol::{SynorProtocol, SynorRequest, SynorResponse};
pub use rate_limit::{
    PerPeerLimiter, RateLimitConfig as TokenBucketConfig, RateLimitError,
    RateLimiter as TokenBucketLimiter,
};
pub use ratelimit::{RateLimitConfig, RateLimitResult, RateLimiter, RateLimiters};
pub use reputation::{
    PeerReputation, ReputationConfig, ReputationManager, ReputationStats, ViolationType,
};
pub use service::{NetworkEvent, NetworkHandle, NetworkService};
pub use sync::{SyncManager, SyncState, SyncStatus};

/// Network protocol version.
pub const PROTOCOL_VERSION: &str = "1.0.0";

/// Maximum message size (10 MB).
pub const MAX_MESSAGE_SIZE: usize = 10 * 1024 * 1024;

/// Maximum number of peers.
pub const MAX_PEERS: usize = 50;

/// Default port for P2P connections.
pub const DEFAULT_PORT: u16 = 16511;

/// GossipSub topic names.
pub mod topics {
    /// Topic for block announcements.
    pub const BLOCKS: &str = "synor/blocks/1.0.0";
    /// Topic for transaction announcements.
    pub const TRANSACTIONS: &str = "synor/txs/1.0.0";
    /// Topic for header announcements.
    pub const HEADERS: &str = "synor/headers/1.0.0";
}

/// User agent string.
pub fn user_agent() -> String {
    format!("synor/{}", env!("CARGO_PKG_VERSION"))
}

/// Network chain identifier.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum ChainId {
    /// Main network.
    Mainnet,
    /// Test network.
    Testnet,
    /// Development network.
    Devnet,
    /// Custom network with ID.
    Custom(u32),
}

impl ChainId {
    /// Returns the chain ID as bytes.
    pub fn as_bytes(&self) -> [u8; 4] {
        match self {
            ChainId::Mainnet => [0x53, 0x59, 0x4E, 0x4F], // "SYNO"
            ChainId::Testnet => [0x54, 0x53, 0x59, 0x4E], // "TSYN"
            ChainId::Devnet => [0x44, 0x53, 0x59, 0x4E],  // "DSYN"
            ChainId::Custom(id) => id.to_be_bytes(),
        }
    }

    /// Returns the network magic bytes (for message framing).
    pub fn magic(&self) -> [u8; 4] {
        self.as_bytes()
    }
}

#[allow(clippy::derivable_impls)]
impl Default for ChainId {
    fn default() -> Self {
        ChainId::Mainnet
    }
}

impl std::fmt::Display for ChainId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ChainId::Mainnet => write!(f, "mainnet"),
            ChainId::Testnet => write!(f, "testnet"),
            ChainId::Devnet => write!(f, "devnet"),
            ChainId::Custom(id) => write!(f, "custom-{}", id),
        }
    }
}

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

    #[test]
    fn test_chain_id() {
        assert_eq!(ChainId::Mainnet.to_string(), "mainnet");
        assert_eq!(ChainId::Testnet.as_bytes(), [0x54, 0x53, 0x59, 0x4E]);
    }

    #[test]
    fn test_user_agent() {
        let ua = user_agent();
        assert!(ua.starts_with("synor/"));
    }
}
