use crate::key::{KeyPair, PrivateKey, PublicKey};

use serde::{Deserialize, Serialize};
use std::collections::HashSet;
use std::iter::FromIterator;

pub trait Info {
    fn name(&self) -> &str;
}

#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct CA {
    name: String,
    keypair: KeyPair,
}

impl CA {
    pub fn new(name: &str, keypair: KeyPair) -> Self {
        Self {
            name: name.to_string(),
            keypair,
        }
    }

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

    pub fn rename(&mut self, name: &str) {
        self.name = name.into();
    }

    pub fn keypair(&self) -> &KeyPair {
        &self.keypair
    }
}

#[derive(Debug, Clone, Serialize)]
pub struct SafeCA {
    name: String,
    public_key: PublicKey,
}

impl From<&CA> for SafeCA {
    fn from(ca: &CA) -> Self {
        Self {
            name: ca.name().into(),
            public_key: ca.keypair.public().clone(),
        }
    }
}

impl Info for SafeCA {
    fn name(&self) -> &str {
        &self.name
    }
}

#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Host {
    public: PublicKey,
    private: Option<PrivateKey>,
    valid_until: Option<String>,
    name: String,
    #[serde(default)]
    principals: Vec<String>,
}

impl Host {
    pub fn new(public: PublicKey, private: Option<PrivateKey>, name: String) -> Self {
        Self {
            public,
            private,
            valid_until: None,
            name,
            principals: vec![],
        }
    }

    pub fn with_principals(mut self, principals: Vec<String>) -> Self {
        self.principals = principals;
        self
    }

    pub fn set_valid_until(&mut self, timestamp: String) {
        self.valid_until = Some(timestamp);
    }

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

    pub fn valid_until(&self) -> Option<&str> {
        self.valid_until.as_deref()
    }

    pub fn rename(&mut self, name: &str) {
        self.name = name.into();
    }

    pub fn public(&self) -> &PublicKey {
        &self.public
    }

    pub fn private(&self) -> Option<&PrivateKey> {
        if let Some(key) = &self.private {
            Some(key)
        } else {
            None
        }
    }

    pub fn set_keys(&mut self, public: PublicKey, private: PrivateKey) {
        assert!(self.private.is_some());
        self.public = public;
        self.private = Some(private);
    }

    pub fn principals(&self) -> &[String] {
        &self.principals
    }

    pub fn set_principals(&mut self, principals: &[String]) {
        self.principals = principals.to_vec();
    }

    pub fn add_principals(&mut self, principals: &[String]) {
        self.principals.append(&mut principals.to_vec())
    }

    pub fn remove_principals(&mut self, unwanted: &[String]) {
        let old: HashSet<&String> = HashSet::from_iter(&self.principals);
        let unwanted: HashSet<&String> = HashSet::from_iter(unwanted);
        self.principals = old.difference(&unwanted).map(|x| x.to_string()).collect();
    }
}

#[derive(Debug, Clone, Serialize)]
pub struct SafeHost {
    name: String,
    public_key: PublicKey,
}

impl From<&Host> for SafeHost {
    fn from(host: &Host) -> Self {
        Self {
            name: host.name().into(),
            public_key: host.public.clone(),
        }
    }
}

impl Info for SafeHost {
    fn name(&self) -> &str {
        &self.name
    }
}

#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct User {
    key: PublicKey,
    name: String,
    #[serde(default)]
    principals: Vec<String>,
}

impl User {
    pub fn new(key: PublicKey, name: String, principals: Vec<String>) -> Self {
        Self {
            key,
            name,
            principals,
        }
    }

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

    pub fn rename(&mut self, name: &str) {
        self.name = name.into();
    }

    pub fn public(&self) -> &PublicKey {
        &self.key
    }

    pub fn principals(&self) -> &[String] {
        &self.principals
    }

    pub fn set_principals(&mut self, principals: &[String]) {
        self.principals = principals.to_vec();
    }

    pub fn add_principals(&mut self, principals: &[String]) {
        self.principals.append(&mut principals.to_vec())
    }

    pub fn remove_principals(&mut self, unwanted: &[String]) {
        let old: HashSet<&String> = HashSet::from_iter(&self.principals);
        let unwanted: HashSet<&String> = HashSet::from_iter(unwanted);
        self.principals = old.difference(&unwanted).map(|x| x.to_string()).collect();
    }
}

#[derive(Debug, Clone, Serialize)]
pub struct SafeUser {
    name: String,
    public_key: PublicKey,
    principals: Vec<String>,
}

impl From<&User> for SafeUser {
    fn from(user: &User) -> Self {
        Self {
            name: user.name().into(),
            public_key: user.key.clone(),
            principals: user.principals.clone(),
        }
    }
}

impl Info for SafeUser {
    fn name(&self) -> &str {
        &self.name
    }
}

#[derive(Serialize)]
pub struct List<T: Info> {
    infos: Vec<T>,
}

impl<T: Info> List<T> {
    pub fn new() -> Self {
        Self { infos: vec![] }
    }

    pub fn push(&mut self, info: T) {
        self.infos.push(info);
    }

    pub fn sort(&mut self) {
        self.infos
            .sort_by_cached_key(|info| info.name().to_string());
    }

    pub fn iter(&self) -> impl Iterator<Item = &T> {
        self.infos.iter()
    }
}
