//! Storage Node - Stores and serves data on the Synor network
//!
//! Storage nodes are the backbone of decentralized storage:
//! - Store erasure-coded chunks from deals
//! - Respond to proof challenges from L1
//! - Participate in P2P network for data distribution
//! - Earn rewards for successful storage

mod store;
mod network;
mod prover;

pub use store::ChunkStore;
pub use network::StorageNetwork;
pub use prover::ProofSubmitter;

use crate::cid::ContentId;
use crate::deal::{StorageDeal, StorageOffer};
use crate::erasure::Shard;
use crate::proof::{Challenge, StorageProof};
use crate::error::{Error, Result};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tokio::sync::RwLock;

/// Storage node configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NodeConfig {
    /// Node identity (public key)
    pub node_id: [u8; 32],
    /// Storage capacity in bytes
    pub capacity: u64,
    /// Minimum price per byte per epoch
    pub min_price: u64,
    /// Maximum price per byte per epoch
    pub max_price: u64,
    /// Data directory for chunk storage
    pub data_dir: String,
    /// Regions this node serves
    pub regions: Vec<String>,
    /// P2P listen addresses
    pub listen_addrs: Vec<String>,
    /// Bootstrap nodes for P2P discovery
    pub bootstrap_nodes: Vec<String>,
    /// L1 RPC endpoint for proof submission
    pub l1_rpc: String,
    /// Node's stake on L1 (for slashing)
    pub stake: u64,
}

impl Default for NodeConfig {
    fn default() -> Self {
        Self {
            node_id: [0u8; 32],
            capacity: 100 * 1024 * 1024 * 1024, // 100 GB default
            min_price: 1,
            max_price: 10,
            data_dir: "./synor-storage-data".to_string(),
            regions: vec!["global".to_string()],
            listen_addrs: vec!["/ip4/0.0.0.0/tcp/4001".to_string()],
            bootstrap_nodes: vec![],
            l1_rpc: "http://localhost:8545".to_string(),
            stake: 0,
        }
    }
}

/// Storage node state
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum NodeState {
    /// Starting up
    Starting,
    /// Syncing with network
    Syncing,
    /// Fully operational
    Ready,
    /// Temporarily offline
    Offline,
    /// Shutting down
    ShuttingDown,
}

/// Storage node statistics
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct NodeStats {
    /// Total bytes stored
    pub bytes_stored: u64,
    /// Number of active deals
    pub active_deals: u64,
    /// Successful proofs submitted
    pub proofs_submitted: u64,
    /// Failed proofs
    pub proofs_failed: u64,
    /// Total earnings (atomic SYNOR)
    pub total_earnings: u64,
    /// Uptime in seconds
    pub uptime_seconds: u64,
    /// Connected peers
    pub connected_peers: u32,
}

/// A running storage node
pub struct StorageNode {
    /// Node configuration
    config: NodeConfig,
    /// Current state
    state: Arc<RwLock<NodeState>>,
    /// Node statistics
    stats: Arc<RwLock<NodeStats>>,
    /// Local chunk storage
    chunk_store: Arc<ChunkStore>,
    /// Active deals this node is serving
    deals: Arc<RwLock<Vec<StorageDeal>>>,
    /// Our storage offer
    offer: Arc<RwLock<StorageOffer>>,
}

impl StorageNode {
    /// Create a new storage node
    pub async fn new(config: NodeConfig) -> Result<Self> {
        // Initialize chunk store
        let chunk_store = Arc::new(ChunkStore::new(&config.data_dir)?);

        // Create initial offer
        let used_capacity = chunk_store.used_capacity();
        let available = config.capacity.saturating_sub(used_capacity);

        let offer = StorageOffer {
            provider: config.node_id,
            available_capacity: available,
            price_per_byte_epoch: config.min_price,
            min_duration: 100,     // ~100 blocks minimum
            max_duration: 1000000, // ~1M blocks max (~1 year at 6s blocks)
            regions: config.regions.clone(),
            stake: config.stake,
            uptime: 100,
            success_rate: 100,
        };

        Ok(Self {
            config,
            state: Arc::new(RwLock::new(NodeState::Starting)),
            stats: Arc::new(RwLock::new(NodeStats::default())),
            chunk_store,
            deals: Arc::new(RwLock::new(Vec::new())),
            offer: Arc::new(RwLock::new(offer)),
        })
    }

    /// Start the storage node
    pub async fn start(&self) -> Result<()> {
        *self.state.write().await = NodeState::Syncing;

        // TODO: Start P2P network
        // TODO: Connect to L1 for challenge monitoring
        // TODO: Start proof submission loop

        *self.state.write().await = NodeState::Ready;
        Ok(())
    }

    /// Stop the storage node gracefully
    pub async fn stop(&self) -> Result<()> {
        *self.state.write().await = NodeState::ShuttingDown;

        // TODO: Stop accepting new deals
        // TODO: Finish pending proof submissions
        // TODO: Disconnect from P2P network

        *self.state.write().await = NodeState::Offline;
        Ok(())
    }

    /// Get current node state
    pub async fn state(&self) -> NodeState {
        *self.state.read().await
    }

    /// Get node statistics
    pub async fn stats(&self) -> NodeStats {
        self.stats.read().await.clone()
    }

    /// Store a shard from an accepted deal
    pub async fn store_shard(&self, deal_id: [u8; 32], shard: Shard) -> Result<()> {
        // Verify we have accepted this deal
        let deals = self.deals.read().await;
        if !deals.iter().any(|d| d.deal_id == deal_id) {
            return Err(Error::InvalidDeal("Deal not found".to_string()));
        }
        drop(deals);

        // Store the shard
        self.chunk_store.store_shard(&deal_id, &shard).await?;

        // Update stats
        let mut stats = self.stats.write().await;
        stats.bytes_stored += shard.data.len() as u64;

        // Update available capacity in offer
        let mut offer = self.offer.write().await;
        offer.available_capacity = offer.available_capacity.saturating_sub(shard.data.len() as u64);

        Ok(())
    }

    /// Retrieve a shard
    pub async fn get_shard(&self, deal_id: &[u8; 32], shard_index: u8) -> Result<Shard> {
        self.chunk_store.get_shard(deal_id, shard_index).await
    }

    /// Accept a storage deal
    pub async fn accept_deal(&self, deal: StorageDeal) -> Result<()> {
        // Verify we have capacity
        let offer = self.offer.read().await;
        if offer.available_capacity < deal.size {
            return Err(Error::InsufficientCapacity);
        }
        drop(offer);

        // Add deal to active deals
        let mut deals = self.deals.write().await;
        deals.push(deal);

        // Update stats
        let mut stats = self.stats.write().await;
        stats.active_deals += 1;

        Ok(())
    }

    /// Generate proof for a challenge
    pub async fn generate_proof(&self, challenge: Challenge) -> Result<StorageProof> {
        // Find the deal for this CID
        let deals = self.deals.read().await;
        let deal = deals
            .iter()
            .find(|d| d.cid == challenge.cid)
            .ok_or_else(|| Error::NotFound("CID not stored".to_string()))?;

        // Get the challenged shard/chunk
        let shard = self.chunk_store.get_shard(&deal.deal_id, challenge.chunk_index as u8).await?;

        // Build Merkle proof for the requested byte range
        let proof_data = shard.data
            .get(challenge.byte_offset as usize..(challenge.byte_offset + challenge.byte_length) as usize)
            .ok_or_else(|| Error::ProofVerificationFailed("Byte range out of bounds".to_string()))?
            .to_vec();

        // Build Merkle tree for proof
        let tree = crate::proof::build_merkle_tree(&shard.data, 1024);
        let leaf_index = challenge.byte_offset as usize / 1024;
        let merkle_proof = tree.proof(leaf_index);

        Ok(StorageProof {
            node_id: self.config.node_id,
            challenge: challenge.clone(),
            chunk_merkle_root: tree.root(),
            proof_data,
            merkle_proof,
            timestamp: std::time::SystemTime::now()
                .duration_since(std::time::UNIX_EPOCH)
                .unwrap()
                .as_secs(),
            signature: vec![], // TODO: Sign with node key
        })
    }

    /// Get current storage offer
    pub async fn offer(&self) -> StorageOffer {
        self.offer.read().await.clone()
    }

    /// Update storage offer pricing
    pub async fn update_pricing(&self, min_price: u64, max_price: u64) {
        let mut offer = self.offer.write().await;
        offer.price_per_byte_epoch = min_price;
    }
}
