#![deny(warnings)]
#![allow(dead_code)]
//! [TSM.ID].[11031972] -- Platform X Ecosystem
//! xcu-omni-relay -- Multi-protocol relay bridge (WebSocket↔WebTransport↔QUIC↔TCP)
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

#[derive(Debug)]
pub enum RelayError { ProtocolMismatch(String), BufferFull(String), ConnectionLost(String) }
impl std::fmt::Display for RelayError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self { Self::ProtocolMismatch(e) => write!(f, "Protocol: {e}"), Self::BufferFull(e) => write!(f, "Buffer: {e}"), Self::ConnectionLost(e) => write!(f, "Lost: {e}") }
    }
}
impl std::error::Error for RelayError {}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Protocol { WebSocket, WebTransport, Quic, Tcp, Http3 }
impl Protocol {
    pub fn can_bridge_to(&self, other: &Protocol) -> bool {
        // All protocols can bridge to all others through relay
        self != other || *self == Protocol::WebTransport
    }
    pub fn header_size(&self) -> usize {
        match self { Self::WebSocket => 14, Self::WebTransport => 0, Self::Quic => 20, Self::Tcp => 20, Self::Http3 => 9 }
    }
}

#[derive(Debug, Clone)]
pub struct RelayConnection { pub id: String, pub protocol: Protocol, pub remote_addr: String, pub bytes_in: u64, pub bytes_out: u64, pub created_at: u64 }

#[derive(Debug, Clone)]
pub struct RelayFrame { pub source_conn: String, pub dest_conn: String, pub payload: Vec<u8>, pub sequence: u64 }

pub struct OmniRelay {
    connections: Arc<Mutex<HashMap<String, RelayConnection>>>,
    routes: Arc<Mutex<HashMap<String, String>>>, // source_id → dest_id
    buffer: Arc<Mutex<Vec<RelayFrame>>>,
    max_buffer: usize,
    total_relayed: Arc<Mutex<u64>>,
}

impl OmniRelay {
    pub fn new(max_buffer: usize) -> Self {
        Self { connections: Arc::new(Mutex::new(HashMap::new())), routes: Arc::new(Mutex::new(HashMap::new())),
            buffer: Arc::new(Mutex::new(Vec::new())), max_buffer, total_relayed: Arc::new(Mutex::new(0)) }
    }

    pub fn add_connection(&self, conn: RelayConnection) -> Result<(), RelayError> {
        if let Ok(mut conns) = self.connections.lock() { conns.insert(conn.id.clone(), conn); Ok(()) }
        else { Err(RelayError::ConnectionLost("Lock".into())) }
    }

    pub fn create_route(&self, source_id: &str, dest_id: &str) -> Result<(), RelayError> {
        let conns = self.connections.lock().map_err(|_| RelayError::ConnectionLost("Lock".into()))?;
        if !conns.contains_key(source_id) { return Err(RelayError::ConnectionLost(format!("Source {source_id} not found"))); }
        if !conns.contains_key(dest_id) { return Err(RelayError::ConnectionLost(format!("Dest {dest_id} not found"))); }
        drop(conns);
        if let Ok(mut routes) = self.routes.lock() { routes.insert(source_id.into(), dest_id.into()); }
        Ok(())
    }

    /// Relay data: source → translate protocol → dest
    pub fn relay(&self, source_id: &str, payload: Vec<u8>) -> Result<u64, RelayError> {
        let dest_id = {
            let routes = self.routes.lock().map_err(|_| RelayError::ConnectionLost("Lock".into()))?;
            routes.get(source_id).cloned().ok_or_else(|| RelayError::ConnectionLost(format!("No route for {source_id}")))?
        };
        // Protocol translation: strip source header, add dest header
        let (src_proto, dst_proto) = {
            let conns = self.connections.lock().map_err(|_| RelayError::ConnectionLost("Lock".into()))?;
            let src = conns.get(source_id).ok_or_else(|| RelayError::ConnectionLost(source_id.into()))?;
            let dst = conns.get(&dest_id).ok_or_else(|| RelayError::ConnectionLost(dest_id.clone()))?;
            (src.protocol, dst.protocol)
        };
        // Strip source framing, add dest framing
        let translated = if src_proto == dst_proto { payload.clone() }
        else {
            let raw = if payload.len() > src_proto.header_size() { payload[src_proto.header_size()..].to_vec() } else { payload.clone() };
            let mut framed = vec![0u8; dst_proto.header_size()];
            framed.extend_from_slice(&raw);
            framed
        };
        let seq = { let mut t = self.total_relayed.lock().map_err(|_| RelayError::BufferFull("Lock".into()))?; *t += 1; *t };
        // Update byte counters
        if let Ok(mut conns) = self.connections.lock() {
            if let Some(src) = conns.get_mut(source_id) { src.bytes_out += translated.len() as u64; }
            if let Some(dst) = conns.get_mut(&dest_id) { dst.bytes_in += translated.len() as u64; }
        }
        if let Ok(mut buf) = self.buffer.lock() {
            if buf.len() >= self.max_buffer { buf.remove(0); }
            buf.push(RelayFrame { source_conn: source_id.into(), dest_conn: dest_id, payload: translated, sequence: seq });
        }
        Ok(seq)
    }

    pub fn connection_count(&self) -> usize { self.connections.lock().map(|c| c.len()).unwrap_or(0) }
    pub fn total_relayed(&self) -> u64 { self.total_relayed.lock().map(|t| *t).unwrap_or(0) }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_relay_ws_to_wt() {
        let r = OmniRelay::new(100);
        r.add_connection(RelayConnection { id: "ws-1".into(), protocol: Protocol::WebSocket, remote_addr: "1.2.3.4".into(), bytes_in: 0, bytes_out: 0, created_at: 0 }).unwrap();
        r.add_connection(RelayConnection { id: "wt-1".into(), protocol: Protocol::WebTransport, remote_addr: "5.6.7.8".into(), bytes_in: 0, bytes_out: 0, created_at: 0 }).unwrap();
        r.create_route("ws-1", "wt-1").unwrap();
        let seq = r.relay("ws-1", vec![0; 100]).unwrap();
        assert_eq!(seq, 1);
        assert_eq!(r.total_relayed(), 1);
    }
}
