#[allow(unused_imports)]
use serde::{Serialize, Deserialize, de::DeserializeOwned};

use crate::crypto::*;
use crate::spec::MessageFormat;
use crate::conf::ConfAte;

#[allow(dead_code)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum SessionProperty
{
    None,
    ReadKey(EncryptKey),
    PrivateReadKey(PrivateEncryptKey),
    PublicReadKey(PublicEncryptKey),
    WriteKey(PrivateSignKey),
    Identity(String),
}

impl Default for SessionProperty {
    fn default() -> Self {
        SessionProperty::None
    }
}

impl std::fmt::Display
for SessionProperty
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            SessionProperty::None => write!(f, "none"),
            SessionProperty::ReadKey(a) => write!(f, "read_key:{}", a),
            SessionProperty::PrivateReadKey(a) => write!(f, "private_read_key:{}", a),
            SessionProperty::PublicReadKey(a) => write!(f, "public_read_key:{}", a),
            SessionProperty::WriteKey(a) => write!(f, "write_key:{}", a),
            SessionProperty::Identity(a) => write!(f, "identity:{}", a),
        }
    }
}

/// Sessions hold facts about the user that give them certains
/// rights and abilities to view data within the chain-of-trust.
///
/// For instance - to see encrypted data for specific users they
/// must insert their `EncryptKey` into this session before
/// accessing the chain via a `Dio`.
///
/// Another example is the ability to write data. For certain
/// records within the tree if they have been protected with
/// write protection then you must insert your `PrivateKey`
/// into the session before you attempt to insert or modify these
/// records.
///
/// Sessions are never cached and only exist in memory for the
/// duration that you use them for security reasons.
#[allow(dead_code)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Session
where Self: Send + Sync
{
    pub properties: Vec<SessionProperty>,
    pub log_format: Option<MessageFormat>,
}

impl Default
for Session
{
    fn default() -> Session {
        Session {
            properties: Vec::new(),
            log_format: None
        }
    }
}

impl Session
{
    pub fn new(cfg: &ConfAte) -> Session {
        Session {
            properties: Vec::new(),
            log_format: Some(cfg.log_format)
        }
    }

    pub fn add_read_key(&mut self, key: &EncryptKey) {
        self.properties.push(SessionProperty::ReadKey(key.clone()));
    }

    pub fn add_private_read_key(&mut self, key: &PrivateEncryptKey) {
        self.properties.push(SessionProperty::PrivateReadKey(key.clone()));
    }

    pub fn add_write_key(&mut self, key: &PrivateSignKey) {
        self.properties.push(SessionProperty::WriteKey(key.clone()));
    }

    pub fn add_identity(&mut self, identity: String) {
        self.properties.push(SessionProperty::Identity(identity));
    }

    pub fn read_keys(&self) -> Vec<&EncryptKey> {
        self.properties
            .iter()
            .filter_map(
                |p| match p
                {
                    SessionProperty::ReadKey(k) => Some(k),
                    _ => None
                }
            ).collect::<Vec<_>>()
    }

    pub fn write_keys(&self) -> Vec<&PrivateSignKey> {
        self.properties
            .iter()
            .filter_map(
                |p| match p
                {
                    SessionProperty::WriteKey(k) => Some(k),
                    _ => None
                }
            ).collect::<Vec<_>>()
    }

    pub fn private_read_keys(&self) -> Vec<&PrivateEncryptKey> {
        self.properties
            .iter()
            .filter_map(
                |p| match p
                {
                    SessionProperty::PrivateReadKey(k) => Some(k),
                    _ => None
                }
            ).collect::<Vec<_>>()
    }
}

impl std::fmt::Display
for Session
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "[")?;
        let mut first = true;
        for prop in self.properties.iter() {
            match first {
                true => first = false,
                false => write!(f, ",")?
            };
            prop.fmt(f)?;
        }
        write!(f, "]")
    }
}