#![deny(warnings)]
#![allow(dead_code)]
//! [TSM.ID].[11031972] -- Platform X Ecosystem
//! xcu-relay -- NAT Traversal Relay Server (STUN/TURN)
pub mod turn;

#[derive(Debug)]
pub enum RelayError { AllocationFailed(String), PeerNotFound(String), QuotaExceeded(String) }
impl std::fmt::Display for RelayError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self { Self::AllocationFailed(e) => write!(f, "Alloc: {e}"), Self::PeerNotFound(e) => write!(f, "Peer: {e}"), Self::QuotaExceeded(e) => write!(f, "Quota: {e}") }
    }
}
impl std::error::Error for RelayError {}

use std::collections::HashMap;
use std::net::IpAddr;

#[derive(Debug, Clone, Copy)]
pub struct SocketAddr { pub ip: IpAddr, pub port: u16 }
impl SocketAddr {
    pub fn new(ip: IpAddr, port: u16) -> Self { Self { ip, port } }
}

/// STUN binding response: reflexive address (your public IP:port)
#[derive(Debug, Clone)]
pub struct StunResponse { pub mapped_addr: SocketAddr, pub transaction_id: [u8; 12] }

/// TURN allocation
#[derive(Debug, Clone)]
pub struct TurnAllocation {
    pub client_addr: SocketAddr,
    pub relay_addr: SocketAddr,
    pub lifetime_secs: u32,
    pub created_at: u64,
    pub bytes_relayed: u64,
    pub permissions: Vec<IpAddr>,
}

pub struct RelayServer {
    allocations: HashMap<String, TurnAllocation>,
    next_port: u16,
    relay_ip: IpAddr,
    max_allocations: usize,
    max_bytes_per_alloc: u64,
}

impl RelayServer {
    pub fn new(relay_ip: IpAddr, start_port: u16, max_alloc: usize) -> Self {
        Self { allocations: HashMap::new(), next_port: start_port, relay_ip, max_allocations: max_alloc, max_bytes_per_alloc: 100 * 1024 * 1024 }
    }

    /// STUN binding request → returns reflexive address
    pub fn handle_stun_binding(&self, source: SocketAddr, transaction_id: [u8; 12]) -> StunResponse {
        StunResponse { mapped_addr: source, transaction_id }
    }

    /// TURN allocate request
    pub fn allocate(&mut self, client: SocketAddr, lifetime: u32, now: u64) -> Result<TurnAllocation, RelayError> {
        if self.allocations.len() >= self.max_allocations {
            return Err(RelayError::AllocationFailed("Max allocations reached".into()));
        }
        let key = format!("{}:{}", client.ip, client.port);
        let relay_port = self.next_port;
        self.next_port += 1;
        let alloc = TurnAllocation {
            client_addr: client,
            relay_addr: SocketAddr::new(self.relay_ip, relay_port),
            lifetime_secs: lifetime.min(3600),
            created_at: now,
            bytes_relayed: 0,
            permissions: Vec::new(),
        };
        self.allocations.insert(key, alloc.clone());
        Ok(alloc)
    }

    /// Add permission for peer
    pub fn create_permission(&mut self, client_key: &str, peer_ip: IpAddr) -> Result<(), RelayError> {
        let alloc = self.allocations.get_mut(client_key).ok_or_else(|| RelayError::PeerNotFound(client_key.into()))?;
        if !alloc.permissions.contains(&peer_ip) { alloc.permissions.push(peer_ip); }
        Ok(())
    }

    /// Relay data from client to peer (if permitted)
    pub fn relay_data(&mut self, client_key: &str, peer_ip: IpAddr, data_len: u64) -> Result<(), RelayError> {
        let alloc = self.allocations.get_mut(client_key).ok_or_else(|| RelayError::PeerNotFound(client_key.into()))?;
        if !alloc.permissions.contains(&peer_ip) {
            return Err(RelayError::PeerNotFound(format!("{peer_ip} not permitted")));
        }
        alloc.bytes_relayed += data_len;
        if alloc.bytes_relayed > self.max_bytes_per_alloc {
            return Err(RelayError::QuotaExceeded(format!("{}B > {}B", alloc.bytes_relayed, self.max_bytes_per_alloc)));
        }
        Ok(())
    }

    /// Cleanup expired allocations
    pub fn cleanup(&mut self, now: u64) -> usize {
        let before = self.allocations.len();
        self.allocations.retain(|_, a| now - a.created_at < a.lifetime_secs as u64);
        before - self.allocations.len()
    }

    pub fn active_allocations(&self) -> usize { self.allocations.len() }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::net::Ipv4Addr;
    #[test]
    fn test_stun() {
        let s = RelayServer::new(IpAddr::V4(Ipv4Addr::new(1,2,3,4)), 50000, 100);
        let client = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10,0,0,1)), 12345);
        let resp = s.handle_stun_binding(client, [0u8; 12]);
        assert_eq!(resp.mapped_addr.port, 12345);
    }
    #[test]
    fn test_turn() {
        let mut s = RelayServer::new(IpAddr::V4(Ipv4Addr::new(1,2,3,4)), 50000, 100);
        let client = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(10,0,0,1)), 12345);
        let alloc = s.allocate(client, 600, 1000).unwrap();
        assert_eq!(alloc.relay_addr.port, 50000);
        let key = "10.0.0.1:12345";
        let peer = IpAddr::V4(Ipv4Addr::new(10,0,0,2));
        s.create_permission(key, peer).unwrap();
        s.relay_data(key, peer, 1000).unwrap();
    }
}
