#![deny(warnings)]
#![allow(dead_code)]
//! [TSM.ID].[11031972] -- Platform X Ecosystem
//! xcu-labyrinth -- Multi-hop Obfuscated Routing
//! Traffic path randomization so no single node knows full route

use std::collections::HashMap;

#[derive(Debug)]
pub enum LabyrinthError {
    NoRoute(String),
    NodeFailed(String),
    EncryptionFailed(String),
}
impl std::fmt::Display for LabyrinthError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self { Self::NoRoute(e) => write!(f, "No route: {e}"),
            Self::NodeFailed(e) => write!(f, "Node: {e}"),
            Self::EncryptionFailed(e) => write!(f, "Encrypt: {e}"), }
    }
}
impl std::error::Error for LabyrinthError {}

#[derive(Debug, Clone)]
pub struct LabyrinthNode {
    pub id: String,
    pub latency_ms: u32,
    pub bandwidth_mbps: u32,
    pub trust_score: f64,
    pub country: String,
    pub is_alive: bool,
}

/// Onion-layered routing envelope
#[derive(Debug, Clone)]
pub struct OnionEnvelope {
    pub layers: Vec<EncryptedLayer>,
    pub total_hops: usize,
}

#[derive(Debug, Clone)]
pub struct EncryptedLayer {
    pub next_hop: String,
    pub encrypted_payload: Vec<u8>,
    pub layer_key_hash: u64,
}

pub struct Labyrinth {
    nodes: HashMap<String, LabyrinthNode>,
    min_hops: usize,
    max_hops: usize,
    avoid_countries: Vec<String>,
    entropy_state: u64,
}

impl Labyrinth {
    pub fn new(min_hops: usize, max_hops: usize, avoid: Vec<String>) -> Self {
        Self {
            nodes: HashMap::new(), min_hops, max_hops,
            avoid_countries: avoid,
            entropy_state: 0xa5a5a5a5deadbeef,
        }
    }

    pub fn add_node(&mut self, node: LabyrinthNode) {
        self.nodes.insert(node.id.clone(), node);
    }

    fn next_random(&mut self) -> u64 {
        self.entropy_state ^= self.entropy_state << 13;
        self.entropy_state ^= self.entropy_state >> 7;
        self.entropy_state ^= self.entropy_state << 17;
        self.entropy_state
    }

    /// Select route through the labyrinth
    pub fn build_route(&mut self, source: &str, destination: &str) -> Result<Vec<String>, LabyrinthError> {
        // Pre-compute random values before borrowing self.nodes
        let hop_rand = self.next_random();
        let node_count = self.nodes.len();
        let rand_scores: Vec<f64> = (0..node_count)
            .map(|_| (self.next_random() % 100) as f64 * 0.3)
            .collect();

        let eligible: Vec<&LabyrinthNode> = self.nodes.values()
            .filter(|n| n.is_alive)
            .filter(|n| !self.avoid_countries.contains(&n.country))
            .filter(|n| n.id != source && n.id != destination)
            .collect();

        if eligible.len() < self.min_hops {
            return Err(LabyrinthError::NoRoute(format!("Need {} hops, only {} nodes", self.min_hops, eligible.len())));
        }

        let hop_count = self.min_hops + (hop_rand as usize % (self.max_hops - self.min_hops + 1));
        let hop_count = hop_count.min(eligible.len());

        // Score nodes: prefer high trust, low latency, diverse countries
        let mut scored: Vec<(&LabyrinthNode, f64)> = eligible.iter().enumerate().map(|(i, n)| {
            let rand_component = rand_scores.get(i).copied().unwrap_or(0.0);
            let score = n.trust_score * 50.0
                + (1000.0 / (n.latency_ms as f64 + 1.0))
                + n.bandwidth_mbps as f64 * 0.1
                + rand_component;
            (*n, score)
        }).collect();

        scored.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));

        // Pick top nodes but ensure country diversity
        let mut route = vec![source.to_string()];
        let mut used_countries = std::collections::HashSet::new();

        for (node, _) in &scored {
            if route.len() - 1 >= hop_count { break; }
            if !used_countries.contains(&node.country) || route.len() > 3 {
                route.push(node.id.clone());
                used_countries.insert(node.country.clone());
            }
        }

        route.push(destination.to_string());
        Ok(route)
    }

    /// Build onion-encrypted envelope for the route
    pub fn build_onion(&mut self, route: &[String], payload: &[u8]) -> Result<OnionEnvelope, LabyrinthError> {
        let mut layers = Vec::new();
        let mut current_payload = payload.to_vec();

        // Build layers from destination back to source (onion wrapping)
        for i in (1..route.len()).rev() {
            let next_hop = &route[i];
            let layer_key = self.next_random();

            // XOR encrypt each layer
            let encrypted: Vec<u8> = current_payload.iter().enumerate()
                .map(|(j, &b)| b ^ ((layer_key >> ((j % 8) * 8)) & 0xFF) as u8)
                .collect();

            layers.push(EncryptedLayer {
                next_hop: next_hop.clone(),
                encrypted_payload: encrypted.clone(),
                layer_key_hash: layer_key & 0xFFFFFFFF,
            });

            current_payload = encrypted;
        }

        layers.reverse();
        Ok(OnionEnvelope { layers, total_hops: route.len() - 2 })
    }

    /// Peel one layer of the onion (at each relay node)
    pub fn peel_layer(&self, layer: &EncryptedLayer, key: u64) -> Vec<u8> {
        layer.encrypted_payload.iter().enumerate()
            .map(|(j, &b)| b ^ ((key >> ((j % 8) * 8)) & 0xFF) as u8)
            .collect()
    }

    pub fn node_count(&self) -> usize { self.nodes.len() }
}

#[cfg(test)]
mod tests {
    use super::*;
    fn make_nodes(lab: &mut Labyrinth) {
        for (id, country) in [("node-de","DE"),("node-jp","JP"),("node-br","BR"),("node-sg","SG"),("node-ch","CH")] {
            lab.add_node(LabyrinthNode { id: id.into(), latency_ms: 50, bandwidth_mbps: 100, trust_score: 0.9, country: country.into(), is_alive: true });
        }
    }
    #[test]
    fn test_route_building() {
        let mut lab = Labyrinth::new(2, 4, vec!["CN".into()]);
        make_nodes(&mut lab);
        let route = lab.build_route("source", "dest").unwrap();
        assert!(route.len() >= 4);
        assert_eq!(route[0], "source");
        assert_eq!(route.last().unwrap(), "dest");
    }
    #[test]
    fn test_onion_wrap() {
        let mut lab = Labyrinth::new(2, 3, vec![]);
        make_nodes(&mut lab);
        let route = lab.build_route("src", "dst").unwrap();
        let envelope = lab.build_onion(&route, b"secret").unwrap();
        assert!(envelope.total_hops >= 2);
        assert!(!envelope.layers.is_empty());
    }
}
