#![deny(warnings)]
#![allow(dead_code)]
//! [TSM.ID].[11031972] -- Platform X Ecosystem
//! xcu-orbital-router -- Mesh Routing with Latency Scoring & Circuit Breaker
use std::collections::HashMap;

#[derive(Debug)]
pub enum OrbitalError { NoRoute(String), CircuitOpen(String), AllPathsFailed(String) }
impl std::fmt::Display for OrbitalError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self { Self::NoRoute(e) => write!(f, "No route: {e}"), Self::CircuitOpen(e) => write!(f, "Circuit: {e}"), Self::AllPathsFailed(e) => write!(f, "All failed: {e}") }
    }
}
impl std::error::Error for OrbitalError {}

#[derive(Debug, Clone)]
pub struct MeshNode { pub id: String, pub region: String, pub latency_ms: u32, pub capacity: u32, pub current_load: u32 }

#[derive(Debug, Clone)]
pub struct MeshEdge { pub from: String, pub to: String, pub latency_ms: u32, pub bandwidth_mbps: u32 }

#[derive(Debug, Clone)]
pub struct CircuitBreaker { pub failures: u32, pub threshold: u32, pub is_open: bool, pub last_failure: u64 }
impl CircuitBreaker {
    pub fn new(threshold: u32) -> Self { Self { failures: 0, threshold, is_open: false, last_failure: 0 } }
    pub fn record_failure(&mut self, now: u64) { self.failures += 1; self.last_failure = now; if self.failures >= self.threshold { self.is_open = true; } }
    pub fn record_success(&mut self) { self.failures = 0; self.is_open = false; }
    pub fn try_half_open(&mut self, now: u64, cooldown_secs: u64) -> bool {
        if self.is_open && now - self.last_failure > cooldown_secs { self.is_open = false; self.failures = self.threshold - 1; return true; }
        !self.is_open
    }
}

pub struct OrbitalRouter {
    nodes: HashMap<String, MeshNode>,
    edges: Vec<MeshEdge>,
    breakers: HashMap<String, CircuitBreaker>,
    breaker_threshold: u32,
}

impl OrbitalRouter {
    pub fn new(breaker_threshold: u32) -> Self {
        Self { nodes: HashMap::new(), edges: Vec::new(), breakers: HashMap::new(), breaker_threshold }
    }
    pub fn add_node(&mut self, node: MeshNode) { self.nodes.insert(node.id.clone(), node); }
    pub fn add_edge(&mut self, edge: MeshEdge) { self.edges.push(edge); }

    /// Find best route using scoring (latency + load + circuit state)
    pub fn find_route(&mut self, from: &str, to: &str, now: u64) -> Result<Vec<String>, OrbitalError> {
        // Direct edge?
        let direct: Vec<&MeshEdge> = self.edges.iter().filter(|e| e.from == from && e.to == to).collect();
        for edge in &direct {
            let key = format!("{}->{}", edge.from, edge.to);
            let breaker = self.breakers.entry(key).or_insert_with(|| CircuitBreaker::new(self.breaker_threshold));
            if breaker.try_half_open(now, 30) {
                return Ok(vec![from.into(), to.into()]);
            }
        }
        // Multi-hop: find via intermediate nodes
        let intermediates: Vec<String> = self.edges.iter()
            .filter(|e| e.from == from)
            .flat_map(|e1| {
                self.edges.iter()
                    .filter(|e2| e2.from == e1.to && e2.to == to)
                    .map(move |e2| (e1.clone(), e2.clone()))
            })
            .map(|(e1, _)| e1.to.clone())
            .collect();

        if intermediates.is_empty() { return Err(OrbitalError::NoRoute(format!("{from} -> {to}"))); }

        // Score intermediates by latency + load
        let mut best_hop = intermediates[0].clone();
        let mut best_score = f64::MAX;
        for hop in &intermediates {
            let key = format!("{from}->{hop}");
            let breaker = self.breakers.entry(key).or_insert_with(|| CircuitBreaker::new(self.breaker_threshold));
            if !breaker.try_half_open(now, 30) { continue; }
            if let Some(node) = self.nodes.get(hop) {
                let load_ratio = if node.capacity > 0 { node.current_load as f64 / node.capacity as f64 } else { 1.0 };
                let score = node.latency_ms as f64 + load_ratio * 100.0;
                if score < best_score { best_score = score; best_hop = hop.clone(); }
            }
        }
        Ok(vec![from.into(), best_hop, to.into()])
    }

    pub fn report_failure(&mut self, from: &str, to: &str, now: u64) {
        let key = format!("{from}->{to}");
        let breaker = self.breakers.entry(key).or_insert_with(|| CircuitBreaker::new(self.breaker_threshold));
        breaker.record_failure(now);
    }

    pub fn report_success(&mut self, from: &str, to: &str) {
        let key = format!("{from}->{to}");
        if let Some(breaker) = self.breakers.get_mut(&key) { breaker.record_success(); }
    }

    pub fn node_count(&self) -> usize { self.nodes.len() }
    pub fn open_circuits(&self) -> usize { self.breakers.values().filter(|b| b.is_open).count() }
}

#[cfg(test)]
mod tests {
    use super::*;
    fn setup() -> OrbitalRouter {
        let mut r = OrbitalRouter::new(3);
        r.add_node(MeshNode { id: "a".into(), region: "sg".into(), latency_ms: 5, capacity: 100, current_load: 10 });
        r.add_node(MeshNode { id: "b".into(), region: "jp".into(), latency_ms: 20, capacity: 100, current_load: 50 });
        r.add_node(MeshNode { id: "c".into(), region: "de".into(), latency_ms: 10, capacity: 100, current_load: 20 });
        r.add_edge(MeshEdge { from: "a".into(), to: "b".into(), latency_ms: 15, bandwidth_mbps: 100 });
        r.add_edge(MeshEdge { from: "a".into(), to: "c".into(), latency_ms: 10, bandwidth_mbps: 100 });
        r.add_edge(MeshEdge { from: "c".into(), to: "b".into(), latency_ms: 20, bandwidth_mbps: 50 });
        r
    }
    #[test]
    fn test_direct_route() { let mut r = setup(); let route = r.find_route("a", "b", 1000).unwrap(); assert_eq!(route, vec!["a", "b"]); }
    #[test]
    fn test_circuit_breaker() {
        let mut r = setup();
        r.report_failure("a", "b", 100);
        r.report_failure("a", "b", 101);
        r.report_failure("a", "b", 102);
        assert_eq!(r.open_circuits(), 1);
    }
}
