use crate::crypt::{CryptAlgorithm, CryptKey};
use crate::error::DatError;
use crate::sign::{SignAlgorithm, SignKey, VerifyKey};
use crate::util::{decode_base64_url_no_pad, encode_base64_url_no_pad, to_kid};
use std::fmt::{Debug, Display};
use std::str::FromStr;
use crate::dat_key_active::DatKeyActive;

pub type DatSplit<'a> = [&'a str; 5];
pub trait Kid: PartialEq + Display + FromStr + Clone {}

impl<T> Kid for T where T: PartialEq + Display + FromStr + Clone {}

pub const CONV_VERSION: &str = "1";

#[derive(Debug, Clone)]
pub struct DatKey<T: Kid> {
    pub(crate) kid: T,
    pub(crate) sign_alg: SignAlgorithm,
    pub(crate) sign_key_vec: Vec<u8>,
    pub(crate) verify_key_vec: Vec<u8>,
    pub(crate) crypt_alg: CryptAlgorithm,
    pub(crate) crypt_key_vec: Vec<u8>,
    pub(crate) issue_begin: i64,
    pub(crate) issue_end: i64,
    pub(crate) token_ttl: i64,
}

impl <T: Kid> DatKey<T> {
    pub fn generate(kid: T, sign_alg: SignAlgorithm, crypt_alg: CryptAlgorithm, issue_begin: i64, issue_end: i64, token_ttl: i64) -> Result<Self, DatError> {
        let sign_key = SignKey::generate(sign_alg);
        let sign_key_v = sign_key.to_bytes().to_vec();
        let verify_key_v = sign_key.to_verify_key().to_bytes().to_vec();
        let crypt_key_v = CryptKey::generate(crypt_alg).to_bytes().to_vec();
        Self::from(kid, sign_alg, sign_key_v, verify_key_v, crypt_alg, crypt_key_v, issue_begin, issue_end, token_ttl)
    }

    pub fn from(kid: T, sign_alg: SignAlgorithm, sign_key_vec: Vec<u8>, verify_key_vec: Vec<u8>, crypt_alg: CryptAlgorithm, crypt_key_vec: Vec<u8>, issue_begin: i64, issue_end: i64, token_ttl: i64) -> Result<Self, DatError> {
        if kid.to_string().contains(['.', '\r', '\n']) {
            return Err(DatError::InvalidDatKidFormat);
        }
        Ok(DatKey { kid, sign_alg, sign_key_vec, verify_key_vec, crypt_alg, crypt_key_vec, issue_begin, issue_end, token_ttl, })
    }

    pub fn kid(&self) -> T { self.kid.clone() }
    pub fn sign_alg(&self) -> SignAlgorithm { self.sign_alg }
    pub fn sign_key_vec(&self) -> Vec<u8> { self.sign_key_vec.to_vec() }
    pub fn verify_key_vec(&self) -> Vec<u8> { self.verify_key_vec.to_vec() }
    pub fn crypt_alg(&self) -> CryptAlgorithm { self.crypt_alg }
    pub fn crypt_key_vec(&self) -> Vec<u8> { self.crypt_key_vec.to_vec() }
    pub fn issue_begin(&self) -> i64 { self.issue_begin }
    pub fn issue_end(&self) -> i64 { self.issue_end }
    pub fn token_ttl(&self) -> i64 { self.token_ttl }
    pub fn key_expire(&self) -> i64 { self.issue_end + self.token_ttl }
    pub fn sign_key(&self) -> Option<SignKey> {
        if self.sign_key_vec.len() > 0 {
            SignKey::from_bytes(self.sign_alg, self.sign_key_vec.as_ref()).ok()
        } else {
            None
        }
    }
    pub fn sign_key_base64(&self) -> Result<String, DatError> {
        if self.sign_key_vec.len() > 0 {
            Ok(encode_base64_url_no_pad(&*self.sign_key_vec))
        } else {
            Err(DatError::UnSupportSignKeyIsVerifyOnly)
        }
    }
    pub fn verify_key(&self) -> Result<VerifyKey, DatError> {
        if self.verify_key_vec.len() > 0 {
            VerifyKey::from_bytes(self.sign_alg, self.verify_key_vec.as_ref())
        } else {
            Ok(SignKey::from_bytes(self.sign_alg, self.sign_key_vec.as_ref())?.to_verify_key())
        }
    }
    pub fn verify_key_base64(&self) -> Result<String, DatError> {
        self.verify_key().map(|v| encode_base64_url_no_pad(&*v.to_bytes()))
    }

    pub fn format(&self, verify_only: bool) -> Result<String, DatError> {
        let kid = self.kid.to_string();
        let sign_alg = self.sign_alg.to_str();
        let sign_key = if verify_only {
            format!("~{}", self.verify_key_base64()?)
        } else {
            self.sign_key_base64()?
        };
        let crypt_alg = self.crypt_alg.to_str();
        let crypt_key = encode_base64_url_no_pad(&*self.crypt_key_vec);
        let issue_begin = self.issue_begin;
        let issue_end = self.issue_end;
        let token_ttl = self.token_ttl;
        Ok(format!("{CONV_VERSION}.{kid}.{sign_alg}.{sign_key}.{crypt_alg}.{crypt_key}.{issue_begin}.{issue_end}.{token_ttl}"))
    }

    pub fn active(&self) -> Result<DatKeyActive<T>, DatError> {
        let crypt_key = CryptKey::from_bytes(self.crypt_alg, &*self.crypt_key_vec)?;

        Ok(DatKeyActive {
            kid: self.kid.clone(),
            sign_alg: self.sign_alg,
            sign_key: self.sign_key(),
            crypt_alg: self.crypt_alg,
            verify_key: self.verify_key()?,
            cipher: crypt_key.to_cipher(),
            token_ttl: self.token_ttl,
        })
    }
}

impl <T: Kid> FromStr for DatKey<T> {
    type Err = DatError;
    fn from_str(format: &str) -> Result<Self, Self::Err> {
        let split = format.split(".").collect::<Vec<&str>>();
        match split[0] {
            "1" => {
                return if split.len() == 9 {
                    let kid = to_kid(split[1])?;
                    let sign_alg = SignAlgorithm::from_str(split[2])?;
                    let sign_or_verify_key = split[3];
                    let (sign_key, verify_key) = if sign_or_verify_key.starts_with('~') {
                        (vec![], decode_base64_url_no_pad(sign_or_verify_key[1..].as_bytes())?)
                    } else {
                        (decode_base64_url_no_pad(sign_or_verify_key)?, vec![])
                    };
                    let crypt_alg = CryptAlgorithm::from_str(split[4])?;
                    let crypt_key = decode_base64_url_no_pad(split[5])?;
                    let issue_begin = split[6].parse::<i64>().map_err(|_| DatError::InvalidDatKeyFormat)?;
                    let issue_end = split[7].parse::<i64>().map_err(|_| DatError::InvalidDatKeyFormat)?;
                    let token_ttl = split[8].parse::<i64>().map_err(|_| DatError::InvalidDatKeyFormat)?;
                    DatKey::from(kid, sign_alg, sign_key, verify_key, crypt_alg, crypt_key, issue_begin, issue_end, token_ttl)
                } else {
                    Err(DatError::InvalidDatKeyFormat)
                }
            },
            _ => {}
        }
        Err(DatError::UnSupportDatKeyVersion)
    }
}

impl <T: Kid> PartialEq for DatKey<T> {
    fn eq(&self, other: &Self) -> bool {
        self.kid.eq(&other.kid)
    }
}
