use std::collections::HashMap;
use std::env;
use std::process::exit;
use std::str::FromStr;
use std::string::ToString;

pub fn parse_arguments(known_arguments: Vec<&str>, required_parameters: Vec<&str>) -> HashMap<String, String> {
    let args: Vec<String> = env::args().collect();
    let mapped_arguments: Vec<(String, String)> = args.iter().enumerate().filter(|arg| arg.0 > 0).map(|arg| {
        let split_argument = arg.1.split('=').collect::<Vec<&str>>();
        if split_argument.len() > 2 {
            return (split_argument.first().unwrap().to_string(), split_argument[1..].join(""));
        }
        return (split_argument.first().unwrap().to_string(), split_argument.last().unwrap().to_string());
    }).collect::<Vec<(String, String)>>();
    let mut map: HashMap<String, String> = HashMap::new();
    for mapped_argument in mapped_arguments {
        map.insert(mapped_argument.0, mapped_argument.1);
    }
    map.keys().for_each(|key| {
        if !known_arguments.contains(&key.as_str()) {
            panic!("Unknown argument {}", key);
        }
    });
    let keys = map.keys().map(|key| key.to_string()).collect::<Vec<String>>();
    if keys.contains(&"h".to_string()) || keys.contains(&"help".to_string()) {
        return map;
    }
    required_parameters.iter().for_each(|required_parameter| {
        if !keys.contains(&required_parameter.to_string()) {
            panic!("{} parameter is required!", required_parameter);
        }
    });
    map
}

fn print_help() {
    println!("Usage:");
    println!("\trglw_bumper <ARGS>");
    println!("Args:");
    println!("\tcommit: required parameter to specify the commit message to inspect.");
    println!("\tversion: required parameter to specify the current version to base on (should be on semantic versioning syntax like 0.0.1).");
    println!("\th | help: shows this text.");
    println!("Examples");
    println!("\trglw_bumper commit='some commit message with a flag like bump_patch' version=0.0.1");
    println!("\trglw_bumper commit='some commit message with a flag like BUMP_MAJOR' version=0.0.1");
    println!("\trglw_bumper commit='some commit message with a flag like BuMp_MiNoR' version=0.0.1");
}

fn main() {
    let args = parse_arguments(vec!["commit", "version", "h", "help"], vec!["commit", "version"]);
    if args.contains_key("h") || args.contains_key("help") {
        print_help();
        exit(0);
    }
    let commit = args.get("commit").expect("commit parameters is required!").clone().to_lowercase();
    let version = args.get("version").expect("version parameters is required!").clone();
    let version_parts = version.split('.').collect::<Vec<&str>>();
    let mut new_version: Vec<String> = Vec::new();
    for (index, part) in version_parts.iter().enumerate() {
        if commit.contains("bump_major") && index == 0 ||
            commit.contains("bump_minor") && index == 1 ||
            commit.contains("bump_patch") && index == 2 {
            new_version.push((isize::from_str(part).unwrap() + 1).to_string());
            break;
        } else {
            new_version.push(part.to_string());
        }
    }
    if new_version.len() < 3 {
        let current_len = new_version.len();
        for _ in current_len..3 {
            new_version.push(0.to_string());
        }
    }
    println!("{}", new_version.join("."));
}

#[cfg(test)]
mod tests {
    use assert_cmd::prelude::*;
    use predicates::prelude::*;
    use std::process::Command;

    #[test]
    pub fn testing_bumper() -> Result<(), Box<dyn std::error::Error>> {
        let mut cmd = Command::cargo_bin("rglw_bumper")?;
        cmd.assert().failure().to_string().contains("commit parameter is required!");

        let mut cmd = Command::cargo_bin("rglw_bumper")?;
        cmd.arg("commit='sdasdasd'");
        cmd.assert().failure().to_string().contains("version parameter is required!");

        let mut cmd = Command::cargo_bin("rglw_bumper")?;
        cmd.arg("commit='sdasdasd'").arg("version=0.0.1");
        cmd.assert().success().stdout(predicate::str::contains("0.0.1"));

        let mut cmd = Command::cargo_bin("rglw_bumper")?;
        cmd.arg("commit='sdasdasd asdasd bump_major asdasda'").arg("version=0.0.1");
        cmd.assert().success().stdout(predicate::str::contains("1.0.0"));

        let mut cmd = Command::cargo_bin("rglw_bumper")?;
        cmd.arg("commit='sdasdasd asdasd bump_minor asdasdas'").arg("version=0.0.1");
        cmd.assert().success().stdout(predicate::str::contains("0.1.0"));

        let mut cmd = Command::cargo_bin("rglw_bumper")?;
        cmd.arg("commit='sdasdasd asdasd bump_patch sdasdasd'").arg("version=0.0.1");
        cmd.assert().success().stdout(predicate::str::contains("0.0.2"));
        Ok(())
    }
}