use clap::{Arg, Command, builder::ValueParser};
use regex::Regex;

const REGEX_MD5_FINGERPRINT: &str = r"^([0-9a-f]{2}:){15}([0-9a-f]{2})$";
const REGEX_SHA256_FINERPRINT: &str = r"^SHA256:[0-9a-zA-Z+/=]{43}$";

pub fn validator_fingerprint() -> ValueParser {
    ValueParser::from(move |s: &str| -> std::result::Result<String, String> {
        let md5 = Regex::new(REGEX_MD5_FINGERPRINT).map_err(|err| err.to_string())?;
        let sha256 = Regex::new(REGEX_SHA256_FINERPRINT).map_err(|err| err.to_string())?;

        if md5.is_match(s) || sha256.is_match(s) {
            Ok(s.to_owned())
        } else {
            Err("Invalid fingerprint".into())
        }
    })
}

pub fn subcommand_create() -> Command {
    Command::new("create")
        .about("Create a new vault")
        .after_help(
            r#"Examples:

Share a secret:

    echo "secret" | ssh-vault create -u new | pbcopy

Share a secret with a known user in GitHub:

    echo "secret" | ssh-vault create -u alice

Share a secret with Alice using its second key:

    echo "secret" | ssh-vault create -u alice -k 2
"#,
        )
        .visible_alias("c")
        .arg(
            Arg::new("fingerprint")
                .short('f')
                .long("fingerprint")
                .help("Create a vault using the key matching the specified fingerprint")
                .conflicts_with("key")
                .value_parser(validator_fingerprint()),
        )
        .arg(
            Arg::new("key")
                .short('k')
                .long("key")
                .help("Path to public ssh key or index when using option -u")
                .conflicts_with("fingerprint"),
        )
        .arg(
            Arg::new("user")
                .short('u')
                .long("user")
                .help("GitHub username or URL, optional [-k N] where N is the key index"),
        )
        .arg(
            Arg::new("json")
                .short('j')
                .long("json")
                .help("When using option -u and user 'new', output the vault in JSON format")
                .number_of_values(0),
        )
        .arg(
            Arg::new("input")
                .short('i')
                .long("input")
                .help("Create a vault form an existing file")
                .value_name("FILE"),
        )
        .arg(Arg::new("vault").help("file to store the vault or writes to stdout if not specified"))
}

#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
    use super::*;

    #[test]
    fn test_fingerprint_regex() {
        let md5 = Regex::new(REGEX_MD5_FINGERPRINT).unwrap();
        let sha256 = Regex::new(REGEX_SHA256_FINERPRINT).unwrap();

        let tests = vec![
            "SHA256:27OFYkCe+dQ2OGAhR8rLjKONUWxPXyu5sTUftcrFAH0",
            "SHA256:manaBRGjdffWoXyg4cMwQ/+qj63c49VDk5UhwQGG1FY",
            "SHA256:hdQM6Q9D4wnXsoyB3K2RtoIPwHhtX1fJqe/ea+d3dFs",
            "SHA256:+aCsCTVZH7cp0cdFUE4QF8jv6D9v/4uax6/R/3mpvXY",
            "SHA256:l5q48gym0yQ0x0OJfgAa8GRxx1Ghc/AlnmkbsgF0qWs",
            "SHA256:glE01yuvgiVCRcnq1RUTJZT+8Z3f7XnG7F4jJtCtgZI",
            "SHA256:616UR76As6777hSo7ExLBQLtFE88LQCNegXcxynOTLs",
            "SHA256:YbZjcewAY5V78qWv/2fVEgEKTM4jWmJXVU7gonpJtLk",
            "SHA256:rCpb2a1l1vGHvEjsM3HsKm/mIpWmQy/3g0qDTLanpVA",
            "SHA256:Nh0Me49Zh9fDw/VYUfq43IJmI1T+XrjiYONPND8GzaM",
            "SHA256:O09r+CSX4Ub8S3klaRp86ahCLbBkxhbaXW7v8y/ANCI",
            "55:cd:f2:7e:4c:0b:e5:a7:6e:6c:fc:6b:8e:58:9d:13",
        ];
        for test in &tests {
            assert!(md5.is_match(test) || sha256.is_match(test));
        }
    }

    #[test]
    fn test_fingerprint_regex_invalid() {
        let md5 = Regex::new(REGEX_MD5_FINGERPRINT).unwrap();
        let sha256 = Regex::new(REGEX_SHA256_FINERPRINT).unwrap();

        let tests = vec![
            "SHA256:     27OFYkCe+dQ2OAhR8rLjKONUWxPXyu5sTUftcrFAH0",
            "SHA256: h0Me49Zh9fDw/VYUfq43IJmI1T+XrjiYONPND8GzaM",
            "SHA256:h0Me49Zh9fDw/VYUfq43IJmI1T+XrjiYONPND8GzaM",
            "SHA256:Nh0Me49Zh9fDw/VYUf]43IJmI1T+XrjiYONPND8GzaM",
            "SHA256:Nh0Me49Zh9fDw/VYUf_43IJmI1T+XrjiYONPND8GzaM",
            "SHA256:Nh0Me49Zh9fDw/VYUf 43IJmI1T+XrjiYONPND8GzaM",
            "SHA256:Nh0Me49Zh9fDw/VYUf.43IJmI1T+XrjiYONPND8GzaM",
            "SHA256:Nh0Me49Zh9fDw/VYUfq43IJmI1T+XrjiYONPND8GzaM.",
            "SHA256:Nh0Me49Zh9fDw/VYUfq43IJmI1T+XrjiYONPND8Gza?",
            "SHA256:......................Nh0Me49Zh9fDw/VYUfq43IJmI1T+XrjiYONPND8=====",
            "SHA256:aaaaaaaaaaaaaaaaaaaaaaaNh0Me49Zh9fDw/VYUfq43IJmI1T+XrjiYONPND8=====",
            "SHA256manaBRGjdffWoXyg4cMwQ/+qj63c49VDk5UhwQGG1FY",
            "HA256:hdQM6Q9D4wnXsoyB3K2RtoIPwHhtX1fJqe/ea+d3dFs",
            "SHA256::+aCsCTVZH7cp0cdFUE4QF8jv6D9v/4uax6/R/3mpvXY",
            "SHA256:Nh0Me49Zh9fDw/VYUfq43IJmI1T+XrjiYONPND8GzaM.",
            "55:cd:f2:7e:4c:0b:e5:a7:6e:6c:fc:6b:8e:58:9d:1",
            "55cd:f2:7e:4c:0b:e5:a7:6e:6c:fc:6b:8e:58:9d:1",
            "55:cdf2:7e:4c:0b:e5:a7:6e:6c:fc:6b:8e:58:9d:1",
            "55:cd:f2:7e:4c:..:e5:a7:6e:6c:fc:6b:8e:58:9d:1",
            "55-cd-f2-7e-4c-0b-e5-a7-6e-6c-fc-6b-8e-58-9d::13",
            "55-cd-f2-7e-4c-0b-e5-a7-6e-6c-fc-6b-8e-58-9d-13",
        ];
        for test in &tests {
            assert!(!(md5.is_match(test) || sha256.is_match(test)));
        }
    }

    #[test]
    fn test_subcommand_create_user_new_with_key_and_fingerprint() {
        let app = Command::new("ssh-vault").subcommand(subcommand_create());
        let matches = app.try_get_matches_from(vec![
            "ssh-vault",
            "create",
            "-u",
            "new",
            "-k",
            "3",
            "-f",
            "55:cd:f2:7e:4c:0b:e5:a7:6e:6c:fc:6b:8e:58:9d:13",
        ]);
        assert!(matches.is_err());
    }

    #[test]
    fn test_subcommand_create_with_fingerprint_1() {
        let app = Command::new("ssh-vault").subcommand(subcommand_create());
        let matches = app.try_get_matches_from(vec![
            "ssh-vault",
            "create",
            "-u",
            "new",
            "-f",
            "55:cd:f2:7e:4c:0b:e5:a7:6e:6c:fc:6b:8e:58:9d:13",
        ]);
        assert!(matches.is_ok());
    }

    #[test]
    fn test_subcommand_create_with_fingerprint_2() {
        let app = Command::new("ssh-vault").subcommand(subcommand_create());
        let matches = app.try_get_matches_from(vec![
            "ssh-vault",
            "create",
            "-u",
            "new",
            "-f",
            "SHA256:27OFYkCe+dQ2OGAhR8rLjKONUWxPXyu5sTUftcrFAH0",
        ]);
        assert!(matches.is_ok());
    }

    #[test]
    fn test_subcommand_create_with_bad_fingerprint() {
        let app = Command::new("ssh-vault").subcommand(subcommand_create());
        let matches = app.try_get_matches_from(vec![
            "ssh-vault",
            "create",
            "-u",
            "new",
            "-f",
            "55:cd:f2:7e:4c:0b:e5:a7:6e:6c:fc:6b:8e:58:9d:1",
        ]);
        assert!(matches.is_err());
    }

    #[test]
    fn test_subcommand_create_new_and_bad_fingerprint() {
        let app = Command::new("ssh-vault").subcommand(subcommand_create());
        let matches = app.try_get_matches_from(vec![
            "ssh-vault",
            "create",
            "-u",
            "new",
            "-f",
            "55:cd:f2:7e:4c:0b:e5:a7:6e:6c:fc:6b:8e:58:9d:",
        ]);
        assert!(matches.is_err());
    }

    #[test]
    fn test_subcommand_create_with_key() {
        let app = Command::new("ssh-vault").subcommand(subcommand_create());
        let matches = app.try_get_matches_from(vec!["ssh-vault", "create", "-u", "new", "-k", "0"]);
        assert!(matches.is_ok());
    }
}
