use super::error::{Error, ErrorKind, Result};
use std::fmt;
/// A type which represents the various kinds of keys.
#[derive(Clone, Debug, PartialEq)]
pub enum KeyTypeKind {
    /// Represents an RSA key type.
    Rsa,

    /// Represents a DSA key type.
    Dsa,

    /// Represents an ED25519 key type.
    Ed25519,

    /// Represents an ECDSA key type.
    Ecdsa,

    /// Represents an RSA certificate key type.
    RsaCert,

    /// Represents a DSA certificate key type.
    DsaCert,

    /// Represents an ED25519 certificate key type.
    Ed25519Cert,

    /// Represents an ECDSA certificate key type.
    EcdsaCert,

    /// Represents an ED25519 key type, generated by a security key.
    Ed25519Sk,

    /// Represents an ECDSA key type, generated by a security key.
    EcdsaSk,
}

/// `KeyType` represents the type of an OpenSSH key.
#[derive(Clone, Debug, PartialEq)]
pub struct KeyType {
    /// Name of the key type.
    pub name: &'static str,

    /// Short name of the key type.
    pub short_name: &'static str,

    /// Indicates whether the key type represents a certificate or not.
    pub is_cert: bool,

    /// Indicates whether the key type is used with a security key or not
    pub is_sk: bool,

    /// Kind of the key type.
    pub kind: KeyTypeKind,

    /// The cert-less equivalent to a certified key type.
    pub plain: &'static str,
}

impl KeyType {
    /// Creates a new `KeyType` from a given name.
    ///
    /// # Example
    /// ```rust
    /// # use sshkeys;
    /// let kt = sshkeys::KeyType::from_name("ssh-rsa").unwrap();
    /// assert_eq!(kt.kind, sshkeys::KeyTypeKind::Rsa);
    /// ```
    pub fn from_name(name: &str) -> Result<KeyType> {
        let kt = match name {
            "ssh-rsa" => KeyType {
                name: "ssh-rsa",
                plain: "ssh-rsa",
                short_name: "RSA",
                is_cert: false,
                is_sk: false,
                kind: KeyTypeKind::Rsa,
            },
            "ssh-rsa-cert-v01@openssh.com" => KeyType {
                name: "ssh-rsa-cert-v01@openssh.com",
                plain: "ssh-rsa",
                short_name: "RSA-CERT",
                is_cert: true,
                is_sk: false,
                kind: KeyTypeKind::RsaCert,
            },
            "ssh-dss" => KeyType {
                name: "ssh-dss",
                plain: "ssh-dss",
                short_name: "DSA",
                is_cert: false,
                is_sk: false,
                kind: KeyTypeKind::Dsa,
            },
            "ssh-dss-cert-v01@openssh.com" => KeyType {
                name: "ssh-dss-cert-v01@openssh.com",
                plain: "ssh-dss",
                short_name: "DSA-CERT",
                is_cert: true,
                is_sk: false,
                kind: KeyTypeKind::DsaCert,
            },
            "ecdsa-sha2-nistp256" => KeyType {
                name: "ecdsa-sha2-nistp256",
                plain: "ecdsa-sha2-nistp256",
                short_name: "ECDSA",
                is_cert: false,
                is_sk: false,
                kind: KeyTypeKind::Ecdsa,
            },
            "ecdsa-sha2-nistp384" => KeyType {
                name: "ecdsa-sha2-nistp384",
                plain: "ecdsa-sha2-nistp384",
                short_name: "ECDSA",
                is_cert: false,
                is_sk: false,
                kind: KeyTypeKind::Ecdsa,
            },
            "ecdsa-sha2-nistp521" => KeyType {
                name: "ecdsa-sha2-nistp521",
                plain: "ecdsa-sha2-nistp521",
                short_name: "ECDSA",
                is_cert: false,
                is_sk: false,
                kind: KeyTypeKind::Ecdsa,
            },
            "ecdsa-sha2-nistp256-cert-v01@openssh.com" => KeyType {
                name: "ecdsa-sha2-nistp256-cert-v01@openssh.com",
                plain: "ecdsa-sha2-nistp256",
                short_name: "ECDSA-CERT",
                is_cert: true,
                is_sk: false,
                kind: KeyTypeKind::EcdsaCert,
            },
            "ecdsa-sha2-nistp384-cert-v01@openssh.com" => KeyType {
                name: "ecdsa-sha2-nistp384-cert-v01@openssh.com",
                plain: "ecdsa-sha2-nistp384",
                short_name: "ECDSA-CERT",
                is_cert: true,
                is_sk: false,
                kind: KeyTypeKind::EcdsaCert,
            },
            "ecdsa-sha2-nistp521-cert-v01@openssh.com" => KeyType {
                name: "ecdsa-sha2-nistp521-cert-v01@openssh.com",
                plain: "ecdsa-sha2-nistp521",
                short_name: "ECDSA-CERT",
                is_cert: true,
                is_sk: false,
                kind: KeyTypeKind::EcdsaCert,
            },
            "ssh-ed25519" => KeyType {
                name: "ssh-ed25519",
                plain: "ssh-ed25519",
                short_name: "ED25519",
                is_cert: false,
                is_sk: false,
                kind: KeyTypeKind::Ed25519,
            },
            "ssh-ed25519-cert-v01@openssh.com" => KeyType {
                name: "ssh-ed25519-cert-v01@openssh.com",
                plain: "ssh-ed25519",
                short_name: "ED25519-CERT",
                is_cert: true,
                is_sk: false,
                kind: KeyTypeKind::Ed25519Cert,
            },
            "sk-ecdsa-sha2-nistp256@openssh.com" => KeyType {
                name: "sk-ecdsa-sha2-nistp256@openssh.com",
                plain: "sk-ecdsa-sha2-nistp256@openssh.com",
                short_name: "ECDSA-SK",
                is_cert: false,
                is_sk: true,
                kind: KeyTypeKind::EcdsaSk,
            },
            "sk-ssh-ed25519@openssh.com" => KeyType {
                name: "sk-ssh-ed25519@openssh.com",
                plain: "sk-ssh-ed25519@openssh.com",
                short_name: "ED25519-SK",
                is_cert: false,
                is_sk: true,
                kind: KeyTypeKind::Ed25519Sk,
            },
            _ => {
                return Err(Error::with_kind(ErrorKind::UnknownKeyType(
                    name.to_string(),
                )))
            }
        };

        Ok(kt)
    }
}

impl fmt::Display for KeyType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.name)
    }
}
