//! [TSM.ID].[11031972] -- Platform X Ecosystem
//! xcu-ebpf-loader -- Cross-platform eBPF program loader
#![deny(warnings)]
#![allow(dead_code)]

use serde::{Serialize, Deserialize};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

#[derive(Debug)]
pub enum LoaderError {
    FileNotFound(String),
    ParseFailed(String),
    ValidationFailed(String),
    LoadFailed(String),
}

impl std::fmt::Display for LoaderError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::FileNotFound(e) => write!(f, "File not found: {e}"),
            Self::ParseFailed(e) => write!(f, "Parse failed: {e}"),
            Self::ValidationFailed(e) => write!(f, "Validation: {e}"),
            Self::LoadFailed(e) => write!(f, "Load failed: {e}"),
        }
    }
}
impl std::error::Error for LoaderError {}
pub type Result<T> = std::result::Result<T, LoaderError>;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProgramSpec {
    pub name: String,
    pub prog_type: String,
    pub bytecode_path: String,
    pub maps: Vec<String>,
}

pub struct ProgramLoader {
    loaded: Arc<Mutex<HashMap<String, ProgramSpec>>>,
    verified: Arc<Mutex<Vec<String>>>,
}

impl ProgramLoader {
    pub fn new() -> Self {
        Self { loaded: Arc::new(Mutex::new(HashMap::new())), verified: Arc::new(Mutex::new(Vec::new())) }
    }

    pub fn load_spec(&self, spec: ProgramSpec) -> Result<()> {
        let mut loaded = self.loaded.lock().map_err(|e| LoaderError::LoadFailed(e.to_string()))?;
        loaded.insert(spec.name.clone(), spec);
        Ok(())
    }

    pub fn verify(&self, name: &str) -> Result<bool> {
        let loaded = self.loaded.lock().map_err(|e| LoaderError::LoadFailed(e.to_string()))?;
        if !loaded.contains_key(name) { return Err(LoaderError::FileNotFound(name.into())); }
        let mut verified = self.verified.lock().map_err(|e| LoaderError::LoadFailed(e.to_string()))?;
        verified.push(name.to_string());
        Ok(true)
    }

    pub fn loaded_count(&self) -> usize { self.loaded.lock().map(|l| l.len()).unwrap_or(0) }
}

impl Default for ProgramLoader {
    fn default() -> Self { Self::new() }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_loader() {
        let loader = ProgramLoader::new();
        loader.load_spec(ProgramSpec {
            name: "test".into(), prog_type: "xdp".into(),
            bytecode_path: "/dev/null".into(), maps: vec![],
        }).unwrap();
        assert_eq!(loader.loaded_count(), 1);
        assert!(loader.verify("test").unwrap());
    }
}
