use crate::certificate::DatCertificate;
use crate::dat::Dat;
use crate::error::DatError;
use crate::payload::DatPayload;
use crate::signature::DatSignatureKeyExportOption;
use crate::util::{encode_base64_url_out, now_unix_timestamp};
use itertools::Itertools;
use std::sync::RwLock;

pub struct DatManager {
    issuer: RwLock<Option<DatCertificate>>,
    certificates: RwLock<Vec<DatCertificate>>,
}

impl DatManager {
    pub fn new() -> Self {
        DatManager {
            issuer: RwLock::new(None),
            certificates: RwLock::new(vec![]),
        }
    }

    pub fn issue(&self, plain: &str, secure: &str) -> Result<String, DatError> {
        if let Some(certificate) = self.issuer.read().unwrap().as_ref() {
            Self::_issue(certificate, plain, secure)
        } else {
            Err(DatError::SigningKeyNotExists)
        }
    }

    pub fn parse(&self, dat: Dat) -> Result<DatPayload, DatError> {
        let cid = dat.cid;
        if let Some(certificate) = self.certificates.read().unwrap().iter().find(|x| x.cid == cid) {
            Self::_parse(certificate, dat)
        } else {
            Err(DatError::CidNotFound)
        }
    }

    pub fn parse_without_verify(&self, dat: Dat) -> Result<DatPayload, DatError> {
        let cid = dat.cid;
        if let Some(certificate) = self.certificates.read().unwrap().iter().find(|x| x.cid == cid) {
            Self::_parse_without_verify(certificate, dat)
        } else {
            Err(DatError::CidNotFound)
        }
    }

    pub fn export_cids(&self) -> Vec<u64> {
        self.certificates.read().unwrap().iter().map(|key| key.cid).collect()
    }

    pub fn export(&self, signature_key_out_option: DatSignatureKeyExportOption) -> String {
        self.certificates.read().unwrap().iter().map(|key| key.export(signature_key_out_option).unwrap()).join("\n")
    }

    pub fn export_certificates(&self) -> Vec<DatCertificate> {
        self.certificates.read().unwrap().clone()
    }

    pub fn import(&self, format: &str, clear: bool) -> Result<(), DatError> {
        let new_certificates = format.lines()
            .filter(|s| !s.is_empty())
            .map(|s| s.parse::<DatCertificate>())
            .collect::<Result<Vec<DatCertificate>, DatError>>()?;
        self.import_certificates(new_certificates, clear)
    }

    pub fn import_certificates(&self, new_certificates: Vec<DatCertificate>, clear: bool) -> Result<(), DatError> {
        let mut ids = new_certificates.iter().map(|x| x.cid).collect::<Vec<u64>>();
        ids.sort();
        ids.dedup();
        if ids.len() != new_certificates.len() {
            return Err(DatError::DuplicatedCid);
        }

        let mut certificates = if clear {
            vec![]
        } else {
            self.certificates.read().unwrap().clone()
        };

        for certificate in new_certificates {
            if !certificates.contains(&certificate) {
                certificates.push(certificate);
            }
        }

        let certificates = certificates.into_iter()
            .filter(|certificate| !certificate.expired())
            .sorted_by(|a, b| a.dat_issue_end.cmp(&b.dat_issue_end))
            .collect::<Vec<DatCertificate>>();

        let issuer: Option<DatCertificate> = certificates.iter()
            .rev()
            .find(|x| x.issuable())
            .cloned();

        *self.issuer.write().unwrap() = issuer;
        *self.certificates.write().unwrap() = certificates;

        Ok(())
    }

    pub fn _issue<U: AsRef<[u8]>>(certificate: &DatCertificate, plain: U, secure: U) -> Result<String, DatError> {
        let mut ib = itoa::Buffer::new();
        let expire = ib.format(now_unix_timestamp() + certificate.dat_ttl);
        let plain = plain.as_ref();
        let secure = secure.as_ref();

        // (byte size + 2) * 4 / 3 = base 64 size
        // 100 is padding: expire + cid + (dot * 4) + (base64 pad 12)...
        // pad = 60(dot=4, base64_4p3=12, nonce=12, cid=16 spare), expire, cid.len + space 30
        let mut v: String = String::with_capacity(60 + expire.len() + ((plain.len() + secure.len() + certificate.signature_key.signature_size()) * 4 / 3));
        let sk = &certificate.signature_key;

        v.push_str(expire);
        v.push_str(&certificate.cid_pre_copy);

        // plain
        encode_base64_url_out(plain, &mut v);

        // secure
        v.push('.');
        encode_base64_url_out(certificate.crypto_key.encrypt(secure)?, &mut v);

        // signature
        let signature = sk.sign(v.as_bytes())?;
        v.push('.');
        encode_base64_url_out(&*signature, &mut v);
        Ok(v)
    }

    pub fn _parse(certificate: &DatCertificate, dat: Dat) -> Result<DatPayload, DatError> {
        if certificate.signature_key.verify(dat.body_bytes(), &dat.signature).is_err() {
            return Err(DatError::InvalidDat)
        }
        Self::_parse_without_verify(certificate, dat)
    }
    pub fn _parse_without_verify(certificate: &DatCertificate, dat: Dat) -> Result<DatPayload, DatError> {
        let plain = dat.plain()?;
        let secure = certificate.crypto_key.decrypt(dat.secure()?)?;

        Ok(DatPayload {
            plain_bytes: plain,
            secure_bytes: secure,
        })
    }
}
