use serde::{Deserialize, Serialize};
use crate::transaction::Transaction;
use blake3::Hasher;

/// Block hash
pub type BlockHash = [u8; 32];

/// A block in the OnionCoin blockchain
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Block {
    /// Block header
    pub header: BlockHeader,

    /// Transactions in this block
    pub transactions: Vec<Transaction>,

    /// Staking actions (stake/unstake)
    pub stake_actions: Vec<StakeAction>,
}

impl Block {
    /// Maximum block size (2 MB for Tor bandwidth constraints)
    pub const MAX_SIZE: usize = 2 * 1024 * 1024;

    /// Target block time (10 minutes)
    pub const TARGET_BLOCK_TIME_SECS: u64 = 10 * 60;

    /// Calculate block hash
    pub fn hash(&self) -> BlockHash {
        self.header.hash()
    }

    /// Validate block structure
    pub fn validate(&self) -> Result<(), BlockError> {
        // Check size
        let size = self.size();
        if size > Self::MAX_SIZE {
            return Err(BlockError::TooLarge(size));
        }

        // Validate all transactions
        for tx in &self.transactions {
            tx.validate().map_err(|_| BlockError::InvalidTransaction)?;
        }

        // Verify merkle root
        let calculated_merkle = Self::calculate_merkle_root(&self.transactions);
        if calculated_merkle != self.header.merkle_root {
            return Err(BlockError::InvalidMerkleRoot);
        }

        Ok(())
    }

    /// Get block size in bytes
    pub fn size(&self) -> usize {
        bincode::serialize(self)
            .map(|s| s.len())
            .unwrap_or(0)
    }

    /// Calculate merkle root of transactions
    fn calculate_merkle_root(transactions: &[Transaction]) -> [u8; 32] {
        if transactions.is_empty() {
            return [0u8; 32];
        }

        let mut hashes: Vec<[u8; 32]> = transactions
            .iter()
            .map(|tx| tx.id())
            .collect();

        // Build merkle tree bottom-up
        while hashes.len() > 1 {
            let mut next_level = Vec::new();

            for chunk in hashes.chunks(2) {
                let mut hasher = Hasher::new();
                hasher.update(&chunk[0]);
                if chunk.len() > 1 {
                    hasher.update(&chunk[1]);
                } else {
                    hasher.update(&chunk[0]); // Duplicate if odd
                }
                next_level.push(*hasher.finalize().as_bytes());
            }

            hashes = next_level;
        }

        hashes[0]
    }
}

/// Block header
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BlockHeader {
    /// Protocol version
    pub version: u8,

    /// Previous block hash
    pub previous_hash: BlockHash,

    /// Merkle root of transactions
    pub merkle_root: [u8; 32],

    /// Block timestamp (Unix seconds)
    pub timestamp: i64,

    /// Block height
    pub height: u64,

    /// Validator's public key (PoS)
    pub validator_pubkey: [u8; 32],

    /// Validator's signature
    #[serde(with = "serde_bytes")]
    pub validator_signature: [u8; 64],
}

impl BlockHeader {
    /// Calculate header hash
    pub fn hash(&self) -> BlockHash {
        let mut hasher = Hasher::new();
        hasher.update(&[self.version]);
        hasher.update(&self.previous_hash);
        hasher.update(&self.merkle_root);
        hasher.update(&self.timestamp.to_le_bytes());
        hasher.update(&self.height.to_le_bytes());
        hasher.update(&self.validator_pubkey);
        // Note: signature not included in hash
        *hasher.finalize().as_bytes()
    }
}

/// Staking action (stake or unstake coins)
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StakeAction {
    /// Action type
    pub action_type: StakeActionType,

    /// Validator public key
    pub validator_pubkey: [u8; 32],

    /// Amount to stake/unstake
    pub amount: u64,

    /// Signature
    #[serde(with = "serde_bytes")]
    pub signature: [u8; 64],
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum StakeActionType {
    Stake,
    Unstake,
}

#[derive(Debug, thiserror::Error)]
pub enum BlockError {
    #[error("Block too large: {0} bytes (max: {})", Block::MAX_SIZE)]
    TooLarge(usize),

    #[error("Invalid transaction in block")]
    InvalidTransaction,

    #[error("Invalid merkle root")]
    InvalidMerkleRoot,

    #[error("Invalid validator signature")]
    InvalidValidatorSignature,

    #[error("Invalid previous hash")]
    InvalidPreviousHash,

    #[error("Block timestamp too far in future")]
    TimestampTooFarFuture,
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::transaction::*;
    use chrono::Utc;
    use onioncoin_timing::TimingMetadata;

    fn create_test_block() -> Block {
        let seed = [42u8; 32];
        let timing = TimingMetadata::new(Utc::now(), &seed).unwrap();

        let tx = Transaction {
            version: 1,
            inputs: vec![TransactionInput {
                previous_output: OutPoint {
                    txid: [1u8; 32],
                    vout: 0,
                },
                key_image: [2u8; 32],
            }],
            outputs: vec![TransactionOutput {
                encrypted_amount: vec![0u8; 32],
                stealth_address: [3u8; 32],
                commitment: [4u8; 32],
            }],
            ring_signatures: vec![RingSignature::new(11)],
            range_proof: vec![0u8; 64],
            encrypted_fee: 1000,
            timing,
        };

        let merkle_root = Block::calculate_merkle_root(&[tx.clone()]);

        Block {
            header: BlockHeader {
                version: 1,
                previous_hash: [0u8; 32],
                merkle_root,
                timestamp: Utc::now().timestamp(),
                height: 1,
                validator_pubkey: [5u8; 32],
                validator_signature: [6u8; 64],
            },
            transactions: vec![tx],
            stake_actions: vec![],
        }
    }

    #[test]
    fn test_block_hash() {
        let block = create_test_block();
        let hash = block.hash();
        assert_ne!(hash, [0u8; 32]);
    }

    #[test]
    fn test_block_validation() {
        let block = create_test_block();
        assert!(block.validate().is_ok());
    }

    #[test]
    fn test_merkle_root() {
        let block = create_test_block();
        let calculated = Block::calculate_merkle_root(&block.transactions);
        assert_eq!(calculated, block.header.merkle_root);
    }

    #[test]
    fn test_empty_merkle_root() {
        let root = Block::calculate_merkle_root(&[]);
        assert_eq!(root, [0u8; 32]);
    }
}
