//! Synor Sharding Protocol
//!
//! This crate implements stateless sharding with beacon chain coordination
//! to achieve 100,000+ TPS throughput.
//!
//! # Architecture
//!
//! ```text
//!                     ┌─────────────────┐
//!                     │  Beacon Chain   │
//!                     │ (Coordination)  │
//!                     └────────┬────────┘
//!          ┌──────────┬────────┼────────┬──────────┐
//!          ▼          ▼        ▼        ▼          ▼
//!     ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
//!     │ Shard 0 │ │ Shard 1 │ │ Shard 2 │ │ Shard N │
//!     │ 3125TPS │ │ 3125TPS │ │ 3125TPS │ │ 3125TPS │
//!     └─────────┘ └─────────┘ └─────────┘ └─────────┘
//!
//!     Total: 32 shards × 3125 TPS = 100,000 TPS
//! ```
//!
//! # Components
//!
//! - **State**: Per-shard Merkle state trees
//! - **Leader**: VRF-based shard leader rotation
//! - **Messaging**: Cross-shard communication protocol
//! - **Routing**: Smart transaction routing by account shard
//! - **Resharding**: Dynamic node join/leave handling
//! - **Proof Aggregation**: Merkle proof batching for efficiency
//!
//! # Example
//!
//! ```rust,ignore
//! use synor_sharding::{ShardManager, ShardConfig};
//!
//! let config = ShardConfig::default(); // 32 shards
//! let manager = ShardManager::new(config);
//!
//! // Route transaction to appropriate shard
//! let shard_id = manager.route_transaction(&tx);
//!
//! // Process on shard
//! manager.submit_transaction(shard_id, tx)?;
//! ```

#![allow(dead_code)]

pub mod error;
pub mod leader;
pub mod messaging;
pub mod proof_agg;
pub mod reshard;
pub mod routing;
pub mod state;

pub use error::{ShardError, ShardResult};
pub use leader::{LeaderElection, VrfOutput};
pub use messaging::{CrossShardMessage, MessageRouter};
pub use proof_agg::{AggregatedProof, ProofAggregator};
pub use reshard::{ReshardEvent, ReshardManager};
pub use routing::{ShardRouter, RoutingTable};
pub use state::{ShardState, ShardStateManager};

use serde::{Deserialize, Serialize};
use synor_types::Hash256;

/// Shard identifier (0..NUM_SHARDS-1).
pub type ShardId = u16;

/// Epoch number for validator rotation.
pub type Epoch = u64;

/// Slot number within an epoch.
pub type Slot = u64;

/// Default number of shards (32 shards × 3125 TPS = 100,000 TPS).
pub const DEFAULT_NUM_SHARDS: u16 = 32;

/// Maximum number of shards supported.
pub const MAX_SHARDS: u16 = 1024;

/// Slots per epoch (for leader rotation).
pub const SLOTS_PER_EPOCH: u64 = 32;

/// Target TPS per shard.
pub const TARGET_TPS_PER_SHARD: u64 = 3125;

/// Cross-shard message timeout in slots.
pub const CROSS_SHARD_TIMEOUT_SLOTS: u64 = 64;

/// Minimum validators per shard for security.
pub const MIN_VALIDATORS_PER_SHARD: usize = 128;

/// Shard configuration parameters.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ShardConfig {
    /// Number of shards.
    pub num_shards: u16,
    /// Slots per epoch.
    pub slots_per_epoch: u64,
    /// Target TPS per shard.
    pub target_tps: u64,
    /// Minimum validators per shard.
    pub min_validators: usize,
    /// Cross-shard timeout in slots.
    pub cross_shard_timeout: u64,
    /// Enable dynamic resharding.
    pub dynamic_resharding: bool,
}

impl Default for ShardConfig {
    fn default() -> Self {
        Self {
            num_shards: DEFAULT_NUM_SHARDS,
            slots_per_epoch: SLOTS_PER_EPOCH,
            target_tps: TARGET_TPS_PER_SHARD,
            min_validators: MIN_VALIDATORS_PER_SHARD,
            cross_shard_timeout: CROSS_SHARD_TIMEOUT_SLOTS,
            dynamic_resharding: true,
        }
    }
}

impl ShardConfig {
    /// Creates configuration for a specific number of shards.
    pub fn with_shards(num_shards: u16) -> Self {
        Self {
            num_shards,
            ..Default::default()
        }
    }

    /// Returns the total theoretical TPS capacity.
    pub fn total_tps(&self) -> u64 {
        self.num_shards as u64 * self.target_tps
    }

    /// Calculates which shard an address belongs to.
    pub fn shard_for_address(&self, address: &Hash256) -> ShardId {
        // Use first 2 bytes of address hash for shard assignment
        let bytes = address.as_bytes();
        let shard_num = u16::from_le_bytes([bytes[0], bytes[1]]);
        shard_num % self.num_shards
    }
}

/// Shard manager coordinating all sharding operations.
pub struct ShardManager {
    /// Configuration.
    config: ShardConfig,
    /// State manager for all shards.
    state_manager: ShardStateManager,
    /// Leader election.
    leader_election: LeaderElection,
    /// Message router.
    message_router: MessageRouter,
    /// Transaction router.
    tx_router: ShardRouter,
    /// Reshard manager.
    reshard_manager: ReshardManager,
    /// Proof aggregator.
    proof_aggregator: ProofAggregator,
    /// Current epoch.
    current_epoch: Epoch,
    /// Current slot.
    current_slot: Slot,
}

impl ShardManager {
    /// Creates a new shard manager with the given configuration.
    pub fn new(config: ShardConfig) -> Self {
        let state_manager = ShardStateManager::new(config.num_shards);
        let leader_election = LeaderElection::new(config.num_shards, config.slots_per_epoch);
        let message_router = MessageRouter::new(config.num_shards, config.cross_shard_timeout);
        let tx_router = ShardRouter::new(config.clone());
        let reshard_manager = ReshardManager::new(config.clone());
        let proof_aggregator = ProofAggregator::new(config.num_shards);

        Self {
            config,
            state_manager,
            leader_election,
            message_router,
            tx_router,
            reshard_manager,
            proof_aggregator,
            current_epoch: 0,
            current_slot: 0,
        }
    }

    /// Returns the shard configuration.
    pub fn config(&self) -> &ShardConfig {
        &self.config
    }

    /// Returns the current epoch.
    pub fn current_epoch(&self) -> Epoch {
        self.current_epoch
    }

    /// Returns the current slot.
    pub fn current_slot(&self) -> Slot {
        self.current_slot
    }

    /// Routes a transaction to the appropriate shard based on sender address.
    pub fn route_transaction(&self, sender: &Hash256) -> ShardId {
        self.tx_router.route(sender)
    }

    /// Gets the current leader for a shard.
    pub fn get_shard_leader(&self, shard_id: ShardId) -> Option<Hash256> {
        self.leader_election.get_leader(shard_id, self.current_slot)
    }

    /// Advances to the next slot.
    pub fn advance_slot(&mut self) {
        self.current_slot += 1;
        if self.current_slot % self.config.slots_per_epoch == 0 {
            self.advance_epoch();
        }
    }

    /// Advances to the next epoch.
    fn advance_epoch(&mut self) {
        self.current_epoch += 1;
        self.leader_election.rotate_leaders(self.current_epoch);

        // Check if resharding is needed
        if self.config.dynamic_resharding {
            if let Some(event) = self.reshard_manager.check_reshard_needed() {
                self.apply_reshard(event);
            }
        }
    }

    /// Applies a resharding event.
    fn apply_reshard(&mut self, event: ReshardEvent) {
        match event {
            ReshardEvent::Split { shard_id, new_shards } => {
                tracing::info!("Splitting shard {} into {:?}", shard_id, new_shards);
                self.state_manager.split_shard(shard_id, &new_shards);
            }
            ReshardEvent::Merge { shards, into } => {
                tracing::info!("Merging shards {:?} into {}", shards, into);
                self.state_manager.merge_shards(&shards, into);
            }
        }
    }

    /// Submits a cross-shard message.
    pub fn send_cross_shard_message(
        &mut self,
        from: ShardId,
        to: ShardId,
        payload: Vec<u8>,
    ) -> ShardResult<Hash256> {
        self.message_router.send(from, to, payload, self.current_slot)
    }

    /// Processes pending cross-shard messages for a shard.
    pub fn process_cross_shard_messages(&mut self, shard_id: ShardId) -> Vec<CrossShardMessage> {
        self.message_router.receive(shard_id, self.current_slot)
    }

    /// Gets the state root for a shard.
    pub fn get_shard_state_root(&self, shard_id: ShardId) -> Option<Hash256> {
        self.state_manager.get_state_root(shard_id)
    }

    /// Aggregates proofs from multiple shards.
    pub fn aggregate_proofs(&self, shard_ids: &[ShardId]) -> ShardResult<AggregatedProof> {
        self.proof_aggregator.aggregate(shard_ids, &self.state_manager)
    }

    /// Returns the total number of active shards.
    pub fn num_shards(&self) -> u16 {
        self.config.num_shards
    }

    /// Returns the theoretical maximum TPS.
    pub fn max_tps(&self) -> u64 {
        self.config.total_tps()
    }
}

impl std::fmt::Debug for ShardManager {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("ShardManager")
            .field("num_shards", &self.config.num_shards)
            .field("current_epoch", &self.current_epoch)
            .field("current_slot", &self.current_slot)
            .field("max_tps", &self.max_tps())
            .finish()
    }
}

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

    #[test]
    fn test_default_config() {
        let config = ShardConfig::default();
        assert_eq!(config.num_shards, 32);
        assert_eq!(config.total_tps(), 100_000);
    }

    #[test]
    fn test_shard_for_address() {
        let config = ShardConfig::with_shards(32);

        // Test deterministic shard assignment
        let addr1 = Hash256::from_bytes([1u8; 32]);
        let addr2 = Hash256::from_bytes([1u8; 32]);

        assert_eq!(
            config.shard_for_address(&addr1),
            config.shard_for_address(&addr2)
        );
    }

    #[test]
    fn test_shard_distribution() {
        let config = ShardConfig::with_shards(32);
        let mut counts = vec![0u32; 32];

        // Generate random addresses and check distribution
        for i in 0..10000u32 {
            let mut bytes = [0u8; 32];
            bytes[0..4].copy_from_slice(&i.to_le_bytes());
            let addr = Hash256::from_bytes(bytes);
            let shard = config.shard_for_address(&addr) as usize;
            counts[shard] += 1;
        }

        // Check that all shards have some assignments
        for (i, count) in counts.iter().enumerate() {
            assert!(*count > 0, "Shard {} has no assignments", i);
        }
    }

    #[test]
    fn test_shard_manager_creation() {
        let manager = ShardManager::new(ShardConfig::default());
        assert_eq!(manager.num_shards(), 32);
        assert_eq!(manager.max_tps(), 100_000);
        assert_eq!(manager.current_epoch(), 0);
        assert_eq!(manager.current_slot(), 0);
    }

    #[test]
    fn test_slot_advancement() {
        let mut manager = ShardManager::new(ShardConfig::default());

        // Advance through an epoch
        for i in 1..=32 {
            manager.advance_slot();
            assert_eq!(manager.current_slot(), i);
        }

        // Should have advanced to epoch 1
        assert_eq!(manager.current_epoch(), 1);
    }
}
