use crate::dat::DatPayload;
use crate::dat_key::{DatKey, Kid};
use crate::error::DatError;
use crate::util::{get_dat_dot_indices, now_unix_timestamp, to_kid};
use itertools::Itertools;
use std::sync::RwLock;
use crate::dat_key_active::DatKeyActive;

pub struct DatKeyStore<T: Kid> {
    padding_keys: RwLock<Vec<DatKey<T>>>,
    issue_key: RwLock<Option<DatKeyActive<T>>>,
    verify_keys: RwLock<Vec<DatKeyActive<T>>>,
}

impl <T: Kid> DatKeyStore<T> {
    pub fn new() -> Self {
        DatKeyStore {
            padding_keys: RwLock::new(vec![]),
            issue_key: RwLock::new(None),
            verify_keys: RwLock::new(vec![]),
        }
    }

    pub fn to_dat(&self, plain: &str, secure: &str) -> Result<String, DatError> {
        if let Some(key) = self.issue_key.read().unwrap().as_ref() {
            key.to_dat(plain, secure)
        } else {
            Err(DatError::NoAvailableKey)
        }
    }

    pub fn to_payload_verify(&self, dat: &str) -> Result<DatPayload, DatError> {
        let di = get_dat_dot_indices(&dat)?;
        let kid = to_kid::<T>(&dat[di[0] + 1 .. di[1]])?;
        if let Some(key) = self.verify_keys.read().unwrap().iter().find(|e| e.kid == kid) {
            key.to_payload_with_indices(dat, di)
        } else {
            Err(DatError::InvalidBase64Format)
        }
    }

    pub fn to_payload_without_verify(&self, dat: &str) -> Result<DatPayload, DatError> {
        let di = get_dat_dot_indices(&dat)?;
        let kid = to_kid::<T>(&dat[di[0] + 1 .. di[1]])?;
        if let Some(key) = self.verify_keys.read().unwrap().iter().find(|e| e.kid == kid) {
            let plain_base64 = &dat[di[1] + 1 .. di[2]];
            let secure_base64 = &dat[di[2] + 1 .. di[3]];
            key.decode_payload_base64(plain_base64, secure_base64)
        } else {
            Err(DatError::InvalidBase64Format)
        }
    }

    pub fn export_kids(&self) -> Vec<T> {
        self.verify_keys.read().unwrap().iter().map(|key| key.kid.clone()).collect()
    }

    pub fn export_keys_format(&self, verify_only: bool) -> String {
        self.padding_keys.read().unwrap().iter().map(|key| key.format(verify_only).unwrap()).join("\n")
    }

    pub fn export_keys(&self) -> Vec<DatKey<T>> {
        self.padding_keys.read().unwrap().iter().cloned().collect()
    }

    pub fn import_keys_format(&self, format: String, clear: bool) -> Result<(), DatError> {
        let new_keys = format.lines()
            .filter(|e| !e.is_empty())
            .map(|e| e.parse::<DatKey<T>>())
            .collect::<Result<Vec<DatKey<T>>, DatError>>()?;
        self.import_keys(new_keys, clear)
    }

    pub fn import_keys(&self, new_keys: Vec<DatKey<T>>, clear: bool) -> Result<(), DatError> {
        let now = now_unix_timestamp();
        let mut keys = if clear { vec![] } else { self.padding_keys.read().unwrap().clone() };

        for key in new_keys {
            if !keys.contains(&key) {
                keys.push(key);
            }
        }

        let keys = keys.into_iter()
            .filter(|key| key.key_expire() >= now)
            .sorted_by(|a, b| a.issue_begin.cmp(&b.issue_begin))
            .collect::<Vec<DatKey<T>>>();

        let issue_key_set: Option<DatKeyActive<T>> = keys.iter()
            .rev()
            .find(|e| e.issue_begin() < now)
            .map(|e| e.clone().active())
            .transpose()?;

        let verify_key_set_list: Vec<DatKeyActive<T>> = keys.iter()
            .map(|e| e.active())
            .collect::<Result<Vec<DatKeyActive<T>>, DatError>>()?;

        *self.padding_keys.write().unwrap() = keys;
        *self.issue_key.write().unwrap() = issue_key_set;
        *self.verify_keys.write().unwrap() = verify_key_set_list;

        Ok(())
    }
}
