//! [TSM.ID].[11031972] -- Platform X Ecosystem
//! xcu-ebpf -- eBPF Abstraction Layer (cross-platform)
#![deny(warnings)]
#![allow(dead_code)]

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

#[derive(Debug)]
pub enum EbpfError {
    ProgramLoadFailed(String),
    MapAccessFailed(String),
    NotSupported(String),
    AttachFailed(String),
}

impl std::fmt::Display for EbpfError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::ProgramLoadFailed(e) => write!(f, "eBPF load failed: {e}"),
            Self::MapAccessFailed(e) => write!(f, "Map access: {e}"),
            Self::NotSupported(e) => write!(f, "Not supported: {e}"),
            Self::AttachFailed(e) => write!(f, "Attach failed: {e}"),
        }
    }
}
impl std::error::Error for EbpfError {}
pub type Result<T> = std::result::Result<T, EbpfError>;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EbpfProgram {
    pub name: String,
    pub prog_type: String,
    pub bytecode: Vec<u8>,
    pub attach_point: String,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum EbpfState { Unloaded, Loaded, Attached, Detached, Error(String) }

pub struct EbpfManager {
    programs: Arc<Mutex<HashMap<String, (EbpfProgram, EbpfState)>>>,
    supported: bool,
}

impl EbpfManager {
    pub fn new() -> Self {
        let supported = cfg!(target_os = "linux");
        Self { programs: Arc::new(Mutex::new(HashMap::new())), supported }
    }

    pub fn is_supported(&self) -> bool { self.supported }

    pub fn load(&self, program: EbpfProgram) -> Result<()> {
        if !self.supported { return Err(EbpfError::NotSupported("eBPF requires Linux".into())); }
        let mut progs = self.programs.lock().map_err(|e| EbpfError::ProgramLoadFailed(e.to_string()))?;
        progs.insert(program.name.clone(), (program, EbpfState::Loaded));
        Ok(())
    }

    pub fn attach(&self, name: &str) -> Result<()> {
        let mut progs = self.programs.lock().map_err(|e| EbpfError::AttachFailed(e.to_string()))?;
        match progs.get_mut(name) {
            Some((_, state)) => { *state = EbpfState::Attached; Ok(()) }
            None => Err(EbpfError::AttachFailed(format!("Program {name} not found"))),
        }
    }

    pub fn detach(&self, name: &str) -> Result<()> {
        let mut progs = self.programs.lock().map_err(|e| EbpfError::AttachFailed(e.to_string()))?;
        match progs.get_mut(name) {
            Some((_, state)) => { *state = EbpfState::Detached; Ok(()) }
            None => Err(EbpfError::AttachFailed(format!("Program {name} not found"))),
        }
    }

    pub fn program_count(&self) -> usize { self.programs.lock().map(|p| p.len()).unwrap_or(0) }
}

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

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_ebpf_manager() {
        let mgr = EbpfManager::new();
        assert_eq!(mgr.program_count(), 0);
    }
}
