//! [TSM.ID].[11031972] -- Platform X Ecosystem
//! xcu-iam-gatekeeper -- Identity Access Management gatekeeper engine
#![deny(warnings)]

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

#[derive(Debug)]
pub enum BridgeError {
    ConnectionFailed(String),
    ConfigError(String),
    OperationFailed(String),
    NotReady,
}

impl std::fmt::Display for BridgeError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::ConnectionFailed(e) => write!(f, "Connection failed: {e}"),
            Self::ConfigError(e) => write!(f, "Config error: {e}"),
            Self::OperationFailed(e) => write!(f, "Op failed: {e}"),
            Self::NotReady => write!(f, "Not ready"),
        }
    }
}
impl std::error::Error for BridgeError {}
pub type Result<T> = std::result::Result<T, BridgeError>;

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

impl BridgeConfig {
    pub fn new(name: &str, endpoint: &str) -> Self {
        Self { name: name.to_string(), endpoint: endpoint.to_string(), params: HashMap::new(), enabled: true }
    }
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum BridgeState { Disconnected, Connecting, Connected, Error(String) }

pub struct Bridge {
    config: BridgeConfig,
    state: Arc<Mutex<BridgeState>>,
    message_count: Arc<Mutex<u64>>,
}

impl Bridge {
    pub fn new(config: BridgeConfig) -> Result<Self> {
        Ok(Self {
            config, state: Arc::new(Mutex::new(BridgeState::Disconnected)),
            message_count: Arc::new(Mutex::new(0)),
        })
    }

    pub fn connect(&self) -> Result<()> {
        let mut s = self.state.lock().map_err(|e| BridgeError::OperationFailed(e.to_string()))?;
        *s = BridgeState::Connecting;
        *s = BridgeState::Connected;
        Ok(())
    }

    pub fn disconnect(&self) -> Result<()> {
        let mut s = self.state.lock().map_err(|e| BridgeError::OperationFailed(e.to_string()))?;
        *s = BridgeState::Disconnected;
        Ok(())
    }

    pub fn send(&self, _payload: &[u8]) -> Result<u64> {
        let s = self.state.lock().map_err(|e| BridgeError::OperationFailed(e.to_string()))?;
        if *s != BridgeState::Connected { return Err(BridgeError::NotReady); }
        drop(s);
        let mut c = self.message_count.lock().map_err(|e| BridgeError::OperationFailed(e.to_string()))?;
        *c += 1;
        Ok(*c)
    }

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

    pub fn config(&self) -> &BridgeConfig { &self.config }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_bridge() {
        let b = Bridge::new(BridgeConfig::new("xcu-iam-gatekeeper", "localhost:3000")).unwrap();
        assert_eq!(b.state().unwrap(), BridgeState::Disconnected);
        b.connect().unwrap();
        assert_eq!(b.state().unwrap(), BridgeState::Connected);
        b.send(b"hello").unwrap();
        b.disconnect().unwrap();
        assert_eq!(b.state().unwrap(), BridgeState::Disconnected);
    }
}
