//! Define the [`TrustHandler`] triat which is used to manage trust of devices.
//! Check out its documentation for more details.
#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, vec::Vec, string::String};

/// A trait that defines how the current device manages trust of other devices.
///
/// When two devices trust each other (usually when they was paired the last time they were
/// connected), they don't need to be paired again, but instead the certificate of the other peer
/// must be compared against the last known certificate of the peer to avoid blindly pairing with
/// an unknown device.
///
/// When implementing this trait, you must prepend the `impl` statement with `#[kdeconnect_proto::async_trait]`.
///
/// ### Example
///
/// This implementation uses two mechanisms:
///
/// - it stores the certificates in a map with the device ID as key to easily retrieves them and
///     check if a device is trusted
/// - it stores the certificates in a dedicated directory with the device ID as file name and reads
///     them at startup to avoid losing the certificates when the program stops
///
/// ```no_run
/// use std::{fs, collections::HashMap, path::{Path, PathBuf}};
/// use kdeconnect_proto::trust::TrustHandler;
///
/// struct TrustHandlerImpl {
///    path: PathBuf,
///    trusted_devices: HashMap<String, Vec<u8>>,
/// }
///
/// impl TrustHandlerImpl {
///    pub fn new<P: AsRef<Path>>(path: P) -> Self {
///        let path = path.as_ref();
///
///        let trusted_devices = if path.exists() {
///             HashMap::from_iter(fs::read_dir(path).unwrap().filter_map(Result::ok).map(|f| {
///                 let device_id = f.path().file_stem().unwrap().to_string_lossy().to_string();
///                 let cert = fs::read(f.path()).expect("failed to read certificate");
///                 (device_id, cert)
///             }))
///         } else {
///             fs::create_dir_all(path).expect("failed to create directory for trusted devices");
///             HashMap::new()
///         };
///
///        Self {
///            path: path.to_path_buf(),
///            trusted_devices,
///        }
///    }
/// }
///
/// #[kdeconnect_proto::async_trait]
/// impl TrustHandler for TrustHandlerImpl {
///     async fn trust_device(&mut self, device_id: String, cert: Vec<u8>) {
///        fs::write(self.path.join(device_id.clone() + ".pem"), &cert).unwrap();
///        self.trusted_devices.insert(device_id, cert);
///    }
///
///    async fn untrust_device(&mut self, device_id: &str) {
///        fs::remove_file(self.path.join(device_id.to_string() + ".pem")).unwrap();
///        self.trusted_devices.remove(device_id);
///    }
///
///    async fn get_certificate(&mut self, device_id: &str) -> Option<&[u8]> {
///        self.trusted_devices.get(device_id).map(|v| &**v)
///    }
/// }
/// ```
#[async_trait::async_trait]
pub trait TrustHandler {
    /// Make a device trusted by storing its certificate somewhere.
    async fn trust_device(&mut self, device_id: String, cert: Vec<u8>);

    /// Forget a device as trusted.
    async fn untrust_device(&mut self, device_id: &str);

    /// Returns the certificate of a trusted device if it exists or `None` otherwise.
    async fn get_certificate(&mut self, device_id: &str) -> Option<&[u8]>;
}
