//! [TSM.ID].[11031972] -- Platform X Ecosystem
//! xcu-pki-forge -- Public Key Infrastructure certificate forge
#![deny(warnings)]
#![allow(dead_code)]

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

#[derive(Debug)]
pub enum XcuError {
    InitFailed(String),
    InvalidConfig(String),
    OperationFailed(String),
    ResourceExhausted,
    NotFound(String),
    Timeout,
    IoError(String),
    SecurityViolation(String),
}

impl std::fmt::Display for XcuError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::InitFailed(e) => write!(f, "Init failed: {e}"),
            Self::InvalidConfig(e) => write!(f, "Invalid config: {e}"),
            Self::OperationFailed(e) => write!(f, "Op failed: {e}"),
            Self::ResourceExhausted => write!(f, "Resource exhausted"),
            Self::NotFound(e) => write!(f, "Not found: {e}"),
            Self::Timeout => write!(f, "Timeout"),
            Self::IoError(e) => write!(f, "IO error: {e}"),
            Self::SecurityViolation(e) => write!(f, "Security violation: {e}"),
        }
    }
}
impl std::error::Error for XcuError {}
pub type Result<T> = std::result::Result<T, XcuError>;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServiceConfig {
    pub name: String,
    pub version: String,
    pub params: HashMap<String, String>,
    pub enabled: bool,
}

impl ServiceConfig {
    pub fn new(name: &str) -> Self {
        Self { name: name.to_string(), version: "0.1.0".to_string(), params: HashMap::new(), enabled: true }
    }
    pub fn param(&mut self, k: &str, v: &str) -> &mut Self {
        self.params.insert(k.to_string(), v.to_string()); self
    }
    pub fn get_param(&self, k: &str) -> Result<&str> {
        self.params.get(k).map(|s| s.as_str()).ok_or_else(|| XcuError::NotFound(k.into()))
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ServiceState { Created, Initializing, Ready, Running, Degraded, Stopping, Stopped, Failed(String) }

pub struct Service {
    config: ServiceConfig,
    state: Arc<Mutex<ServiceState>>,
    counters: Arc<Mutex<HashMap<String, u64>>>,
}

impl Service {
    pub fn new(config: ServiceConfig) -> Result<Self> {
        if config.name.is_empty() { return Err(XcuError::InvalidConfig("empty name".into())); }
        Ok(Self {
            config, state: Arc::new(Mutex::new(ServiceState::Created)),
            counters: Arc::new(Mutex::new(HashMap::new())),
        })
    }

    pub fn init(&self) -> Result<()> {
        let mut s = self.state.lock().map_err(|e| XcuError::OperationFailed(e.to_string()))?;
        *s = ServiceState::Initializing;
        *s = ServiceState::Ready;
        Ok(())
    }

    pub fn start(&self) -> Result<()> {
        let mut s = self.state.lock().map_err(|e| XcuError::OperationFailed(e.to_string()))?;
        match &*s {
            ServiceState::Ready | ServiceState::Stopped => { *s = ServiceState::Running; Ok(()) }
            other => Err(XcuError::InvalidConfig(format!("Cannot start from {other:?}"))),
        }
    }

    pub fn stop(&self) -> Result<()> {
        let mut s = self.state.lock().map_err(|e| XcuError::OperationFailed(e.to_string()))?;
        *s = ServiceState::Stopping;
        *s = ServiceState::Stopped;
        Ok(())
    }

    pub fn state(&self) -> Result<ServiceState> {
        Ok(self.state.lock().map_err(|e| XcuError::OperationFailed(e.to_string()))?.clone())
    }

    pub fn increment(&self, key: &str) -> Result<u64> {
        let mut c = self.counters.lock().map_err(|e| XcuError::OperationFailed(e.to_string()))?;
        let v = c.entry(key.to_string()).or_insert(0);
        *v += 1;
        Ok(*v)
    }

    pub fn config(&self) -> &ServiceConfig { &self.config }
    pub fn name(&self) -> &str { &self.config.name }
    pub fn version(&self) -> &str { &self.config.version }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_service_lifecycle() {
        let s = Service::new(ServiceConfig::new("xcu-pki-forge")).unwrap();
        assert_eq!(s.state().unwrap(), ServiceState::Created);
        s.init().unwrap();
        assert_eq!(s.state().unwrap(), ServiceState::Ready);
        s.start().unwrap();
        assert_eq!(s.state().unwrap(), ServiceState::Running);
        s.stop().unwrap();
        assert_eq!(s.state().unwrap(), ServiceState::Stopped);
    }
    #[test]
    fn test_counter() {
        let s = Service::new(ServiceConfig::new("xcu-pki-forge")).unwrap();
        assert_eq!(s.increment("ops").unwrap(), 1);
        assert_eq!(s.increment("ops").unwrap(), 2);
    }
}
