use std::fmt::{Display, Formatter};
use crate::format::PEMValidationError::{IncorrectSegmentLength, InvalidBase64Data, MissingStartSentinel};
use crate::util::base64decode;

pub const ED_PUBKEY_START_STR: &str = "-----BEGIN NEBULA ED25519 PUBLIC KEY-----";
pub const ED_PUBKEY_END_STR: &str = "-----END NEBULA ED25519 PUBLIC KEY-----";

pub const DH_PUBKEY_START_STR: &str = "-----BEGIN NEBULA X25519 PUBLIC KEY-----";
pub const DH_PUBKEY_END_STR: &str = "-----END NEBULA X25519 PUBLIC KEY-----";

pub enum PEMValidationError {
    MissingStartSentinel,
    InvalidBase64Data,
    MissingEndSentinel,
    IncorrectSegmentLength(usize, usize)
}
impl Display for PEMValidationError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::MissingEndSentinel => write!(f, "Missing ending sentinel"),
            Self::MissingStartSentinel => write!(f, "Missing starting sentinel"),
            Self::InvalidBase64Data => write!(f, "invalid base64 data"),
            Self::IncorrectSegmentLength(expected, got) => write!(f, "incorrect number of segments, expected {} got {}", expected, got)
        }
    }
}

pub fn validate_ed_pubkey_pem(pubkey: &str) -> Result<(), PEMValidationError> {
    let segments = pubkey.split('\n');
    let segs = segments.collect::<Vec<&str>>();
    if segs.len() < 3 {
        return Err(IncorrectSegmentLength(3, segs.len()))
    }
    if segs[0] != ED_PUBKEY_START_STR {
        return Err(MissingStartSentinel)
    }
    if base64decode(segs[1]).is_err() {
        return Err(InvalidBase64Data)
    }
    if segs[2] != ED_PUBKEY_END_STR {
        return Err(MissingStartSentinel)
    }
    Ok(())
}

pub fn validate_dh_pubkey_pem(pubkey: &str) -> Result<(), PEMValidationError> {
    let segments = pubkey.split('\n');
    let segs = segments.collect::<Vec<&str>>();
    if segs.len() < 3 {
        return Err(IncorrectSegmentLength(3, segs.len()))
    }
    if segs[0] != DH_PUBKEY_START_STR {
        return Err(MissingStartSentinel)
    }
    if base64decode(segs[1]).is_err() {
        return Err(InvalidBase64Data)
    }
    if segs[2] != DH_PUBKEY_END_STR {
        return Err(MissingStartSentinel)
    }
    Ok(())
}

pub fn validate_ed_pubkey_base64(pubkey: &str) -> Result<(), PEMValidationError> {
    match base64decode(pubkey) {
        Ok(k) => validate_ed_pubkey_pem(match std::str::from_utf8(k.as_ref()) {
            Ok(k) => k,
            Err(_) => return Err(InvalidBase64Data)
        }),
        Err(_) => Err(InvalidBase64Data)
    }
}

pub fn validate_dh_pubkey_base64(pubkey: &str) -> Result<(), PEMValidationError> {
    match base64decode(pubkey) {
        Ok(k) => validate_dh_pubkey_pem(match std::str::from_utf8(k.as_ref()) {
            Ok(k) => k,
            Err(_) => return Err(InvalidBase64Data)
        }),
        Err(_) => Err(InvalidBase64Data)
    }
}