//! Storage Network - P2P layer for storage nodes
//!
//! Handles peer discovery, data transfer, and network coordination.
//! Built on libp2p when the 'node' feature is enabled.

use crate::cid::ContentId;
use crate::error::{Error, Result};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Peer information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PeerInfo {
    /// Peer ID (derived from public key)
    pub peer_id: [u8; 32],
    /// Multiaddresses for this peer
    pub addresses: Vec<String>,
    /// Regions served
    pub regions: Vec<String>,
    /// Available capacity
    pub available_capacity: u64,
    /// Last seen timestamp
    pub last_seen: u64,
    /// Reputation score (0-100)
    pub reputation: u8,
}

/// Network message types
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum NetworkMessage {
    /// Request to retrieve a shard
    GetShard {
        deal_id: [u8; 32],
        shard_index: u8,
    },
    /// Response with shard data
    ShardResponse {
        deal_id: [u8; 32],
        shard_index: u8,
        #[serde(with = "serde_bytes")]
        data: Vec<u8>,
    },
    /// Announce storage availability
    AnnounceCapacity {
        available: u64,
        price_per_byte: u64,
        regions: Vec<String>,
    },
    /// Request to find providers for a CID
    FindProviders {
        cid: ContentId,
    },
    /// Response with provider list
    ProvidersFound {
        cid: ContentId,
        providers: Vec<[u8; 32]>,
    },
    /// Gossip about a new deal
    NewDeal {
        deal_id: [u8; 32],
        cid: ContentId,
        size: u64,
    },
}

/// Storage network abstraction
pub struct StorageNetwork {
    /// Our peer ID
    local_peer_id: [u8; 32],
    /// Known peers
    peers: HashMap<[u8; 32], PeerInfo>,
    /// CID to providers mapping (content routing)
    providers: HashMap<ContentId, Vec<[u8; 32]>>,
    /// Network state
    state: NetworkState,
}

/// Network state
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NetworkState {
    /// Not connected
    Disconnected,
    /// Connecting to bootstrap nodes
    Bootstrapping,
    /// Fully connected
    Connected,
}

impl StorageNetwork {
    /// Create a new storage network
    pub fn new(local_peer_id: [u8; 32]) -> Self {
        Self {
            local_peer_id,
            peers: HashMap::new(),
            providers: HashMap::new(),
            state: NetworkState::Disconnected,
        }
    }

    /// Start the network
    #[cfg(feature = "node")]
    pub async fn start(&mut self, listen_addrs: &[String], bootstrap: &[String]) -> Result<()> {
        self.state = NetworkState::Bootstrapping;

        // TODO: Initialize libp2p swarm
        // - Create noise transport for encryption
        // - Create yamux multiplexer
        // - Set up Kademlia DHT for peer discovery
        // - Set up gossipsub for deal announcements
        // - Connect to bootstrap nodes

        self.state = NetworkState::Connected;
        Ok(())
    }

    /// Start the network (stub for non-node builds)
    #[cfg(not(feature = "node"))]
    pub async fn start(&mut self, _listen_addrs: &[String], _bootstrap: &[String]) -> Result<()> {
        self.state = NetworkState::Connected;
        Ok(())
    }

    /// Stop the network
    pub async fn stop(&mut self) {
        self.state = NetworkState::Disconnected;
    }

    /// Get network state
    pub fn state(&self) -> NetworkState {
        self.state
    }

    /// Add a known peer
    pub fn add_peer(&mut self, info: PeerInfo) {
        self.peers.insert(info.peer_id, info);
    }

    /// Remove a peer
    pub fn remove_peer(&mut self, peer_id: &[u8; 32]) {
        self.peers.remove(peer_id);
    }

    /// Get peer info
    pub fn get_peer(&self, peer_id: &[u8; 32]) -> Option<&PeerInfo> {
        self.peers.get(peer_id)
    }

    /// Get all connected peers
    pub fn connected_peers(&self) -> Vec<&PeerInfo> {
        self.peers.values().collect()
    }

    /// Find providers for a CID
    pub async fn find_providers(&self, cid: &ContentId) -> Vec<[u8; 32]> {
        self.providers.get(cid).cloned().unwrap_or_default()
    }

    /// Announce that we provide a CID
    pub async fn announce_provider(&mut self, cid: ContentId) {
        let providers = self.providers.entry(cid).or_default();
        if !providers.contains(&self.local_peer_id) {
            providers.push(self.local_peer_id);
        }
    }

    /// Request a shard from a peer
    pub async fn request_shard(
        &self,
        peer_id: &[u8; 32],
        deal_id: [u8; 32],
        shard_index: u8,
    ) -> Result<Vec<u8>> {
        let _peer = self.peers.get(peer_id).ok_or_else(|| {
            Error::Network(format!("Peer not found: {:?}", hex::encode(peer_id)))
        })?;

        // TODO: Send GetShard message via libp2p
        // For now, return error as network not fully implemented
        Err(Error::Network("Network request not implemented".to_string()))
    }

    /// Broadcast a deal announcement
    pub async fn announce_deal(&self, deal_id: [u8; 32], cid: ContentId, size: u64) -> Result<()> {
        let _msg = NetworkMessage::NewDeal { deal_id, cid, size };

        // TODO: Broadcast via gossipsub

        Ok(())
    }

    /// Update reputation for a peer
    pub fn update_reputation(&mut self, peer_id: &[u8; 32], delta: i8) {
        if let Some(peer) = self.peers.get_mut(peer_id) {
            peer.reputation = peer.reputation.saturating_add_signed(delta);
        }
    }
}

/// Content routing - find where data is stored
pub struct ContentRouter {
    /// Local provider records
    records: HashMap<ContentId, Vec<[u8; 32]>>,
}

impl ContentRouter {
    /// Create a new content router
    pub fn new() -> Self {
        Self {
            records: HashMap::new(),
        }
    }

    /// Add a provider record
    pub fn add_provider(&mut self, cid: ContentId, provider: [u8; 32]) {
        let providers = self.records.entry(cid).or_default();
        if !providers.contains(&provider) {
            providers.push(provider);
        }
    }

    /// Remove a provider record
    pub fn remove_provider(&mut self, cid: &ContentId, provider: &[u8; 32]) {
        if let Some(providers) = self.records.get_mut(cid) {
            providers.retain(|p| p != provider);
        }
    }

    /// Find providers for a CID
    pub fn find_providers(&self, cid: &ContentId) -> &[[u8; 32]] {
        self.records.get(cid).map(|v| v.as_slice()).unwrap_or(&[])
    }
}

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