use crate::util::Threading;
use thiserror::Error;
#[derive(Error, Debug)]
#[error("{0}")]
pub struct ConfigError(pub String);
#[derive(Default, Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct NoiseConfigData {
    pub app_static_privkey: Option<[u8; 32]>,
    pub device_static_pubkeys: Vec<Vec<u8>>,
}
impl NoiseConfigData {
    pub(crate) fn contains_device_static_pubkey(&self, pubkey: &[u8]) -> bool {
        self.device_static_pubkeys
            .iter()
            .any(|config_pubkey| config_pubkey.as_slice() == pubkey)
    }
    pub(crate) fn add_device_static_pubkey(&mut self, pubkey: &[u8]) {
        if !self.contains_device_static_pubkey(pubkey) {
            self.device_static_pubkeys.push(pubkey.to_vec());
        }
    }
    pub(crate) fn get_app_static_privkey(&self) -> Option<zeroize::Zeroizing<[u8; 32]>> {
        self.app_static_privkey.map(zeroize::Zeroizing::new)
    }
    pub(crate) fn set_app_static_privkey(&mut self, privkey: &[u8]) -> Result<(), ConfigError> {
        self.app_static_privkey = Some(
            privkey
                .try_into()
                .map_err(|e: std::array::TryFromSliceError| ConfigError(e.to_string()))?,
        );
        Ok(())
    }
}
pub trait NoiseConfig: Threading {
    fn read_config(&self) -> Result<NoiseConfigData, ConfigError> {
        Ok(NoiseConfigData::default())
    }
    fn store_config(&self, _conf: &NoiseConfigData) -> Result<(), ConfigError> {
        Ok(())
    }
}
pub struct NoiseConfigNoCache;
impl NoiseConfig for NoiseConfigNoCache {}
impl Threading for NoiseConfigNoCache {}
pub struct PersistedNoiseConfig {
    config_dir: String,
}
impl Threading for PersistedNoiseConfig {}
impl PersistedNoiseConfig {
    pub fn new(config_dir: &str) -> PersistedNoiseConfig {
        PersistedNoiseConfig {
            config_dir: config_dir.into(),
        }
    }
}
impl NoiseConfig for PersistedNoiseConfig {
    fn read_config(&self) -> Result<NoiseConfigData, ConfigError> {
        use std::io::Read;
        let config_path = std::path::Path::new(&self.config_dir).join("bitbox.json");
        if !config_path.exists() {
            return Ok(NoiseConfigData::default());
        }
        let mut file = std::fs::File::open(config_path).map_err(|e| ConfigError(e.to_string()))?;
        let mut contents = String::new();
        file.read_to_string(&mut contents)
            .map_err(|e| ConfigError(e.to_string()))?;
        serde_json::from_str::<NoiseConfigData>(&contents).map_err(|e| ConfigError(e.to_string()))
    }
    fn store_config(&self, conf: &NoiseConfigData) -> Result<(), ConfigError> {
        use std::io::Write;
        let config_path = std::path::Path::new(&self.config_dir).join("bitbox.json");
        let mut file =
            std::fs::File::create(config_path).map_err(|e| ConfigError(e.to_string()))?;
        let data = serde_json::to_string(conf).map_err(|e| ConfigError(e.to_string()))?;
        file.write_all(data.as_bytes())
            .map_err(|e| ConfigError(e.to_string()))
    }
}