//! Truncated mesh addresses for bandwidth-efficient routing.
//!
//! A [`MeshAddress`] is derived from an Ed25519 public key by taking the first
//! 16 bytes of its SHA-256 hash. This provides globally unique addressing
//! (birthday collision at ~2^64) while saving 16 bytes per packet compared to
//! full 32-byte public keys.

use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::fmt;

/// 16-byte truncated mesh address.
#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct MeshAddress([u8; 16]);

impl MeshAddress {
    /// Derive from a 32-byte Ed25519 public key.
    pub fn from_public_key(key: &[u8; 32]) -> Self {
        let hash = Sha256::digest(key);
        let mut addr = [0u8; 16];
        addr.copy_from_slice(&hash[..16]);
        Self(addr)
    }

    /// Create from raw 16-byte array.
    pub fn from_bytes(bytes: [u8; 16]) -> Self {
        Self(bytes)
    }

    /// Get the raw 16-byte address.
    pub fn as_bytes(&self) -> &[u8; 16] {
        &self.0
    }

    /// Check if a 32-byte public key matches this address.
    pub fn matches_key(&self, key: &[u8; 32]) -> bool {
        Self::from_public_key(key) == *self
    }

    /// The broadcast address (all zeros).
    pub const BROADCAST: Self = Self([0u8; 16]);

    /// Check if this is the broadcast address.
    pub fn is_broadcast(&self) -> bool {
        self.0 == [0u8; 16]
    }
}

impl fmt::Debug for MeshAddress {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "MeshAddress({})", hex::encode(self.0))
    }
}

impl fmt::Display for MeshAddress {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", hex::encode(&self.0[..8]))
    }
}

impl From<[u8; 16]> for MeshAddress {
    fn from(bytes: [u8; 16]) -> Self {
        Self(bytes)
    }
}

impl AsRef<[u8; 16]> for MeshAddress {
    fn as_ref(&self) -> &[u8; 16] {
        &self.0
    }
}

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

    #[test]
    fn from_key_deterministic() {
        let key = [42u8; 32];
        let addr1 = MeshAddress::from_public_key(&key);
        let addr2 = MeshAddress::from_public_key(&key);
        assert_eq!(addr1, addr2, "same key must produce same address");
    }

    #[test]
    fn different_keys_different_addresses() {
        let key_a = [1u8; 32];
        let key_b = [2u8; 32];
        let addr_a = MeshAddress::from_public_key(&key_a);
        let addr_b = MeshAddress::from_public_key(&key_b);
        assert_ne!(addr_a, addr_b, "different keys must produce different addresses");
    }

    #[test]
    fn matches_key_works() {
        let key = [99u8; 32];
        let addr = MeshAddress::from_public_key(&key);
        assert!(addr.matches_key(&key), "correct key must match");

        let wrong_key = [100u8; 32];
        assert!(!addr.matches_key(&wrong_key), "wrong key must not match");
    }

    #[test]
    fn broadcast_address() {
        assert_eq!(*MeshAddress::BROADCAST.as_bytes(), [0u8; 16]);
        assert!(MeshAddress::BROADCAST.is_broadcast());

        let non_broadcast = MeshAddress::from_bytes([1u8; 16]);
        assert!(!non_broadcast.is_broadcast());
    }

    #[test]
    fn display_formatting() {
        let key = [0xAB; 32];
        let addr = MeshAddress::from_public_key(&key);
        let display = format!("{addr}");
        // Display shows first 8 bytes as hex = 16 hex chars.
        assert_eq!(display.len(), 16, "display should show 8 bytes = 16 hex chars");

        let debug = format!("{addr:?}");
        // Debug shows all 16 bytes as hex = 32 hex chars, plus wrapper.
        assert!(debug.starts_with("MeshAddress("));
        assert!(debug.ends_with(')'));
    }

    #[test]
    fn serde_roundtrip() {
        let key = [77u8; 32];
        let addr = MeshAddress::from_public_key(&key);
        let json = serde_json::to_string(&addr).expect("serialize");
        let restored: MeshAddress = serde_json::from_str(&json).expect("deserialize");
        assert_eq!(addr, restored);
    }
}
