#[derive(Debug, PartialEq)]
pub enum OutputFormat {
    Render,
    JSON,
    CSV,
}
#[derive(Debug, PartialEq)]
pub struct Arguments {
    pub repos: Vec<String>,
    pub analysis: bool,
    pub pagination: bool,
    pub page_size: usize,
    pub output_format: OutputFormat,
}
impl Arguments {
    fn new() -> Arguments {
        Arguments {
            repos: vec![],
            analysis: false,
            pagination: false,
            page_size: 10,
            output_format: OutputFormat::Render,
        }
    }
}
#[derive(Debug, PartialEq)]
pub enum Command {
    ReplMode(Arguments),
    QueryMode(String, Arguments),
    Help,
    Version,
    Error(String),
}
pub fn parse_arguments(args: &[String]) -> Command {
    let args_len = args.len();
    if args.iter().any(|i| i == "--help" || i == "-h") {
        return Command::Help;
    }
    if args.iter().any(|i| i == "--version" || i == "-v") {
        return Command::Version;
    }
    let mut optional_query: Option<String> = None;
    let mut arguments = Arguments::new();
    let mut arg_index = 1;
    loop {
        if arg_index >= args_len {
            break;
        }
        let arg = &args[arg_index];
        if !arg.starts_with('-') {
            return Command::Error(format!("Unknown argument {}", arg));
        }
        match arg.as_ref() {
            "--repos" | "-r" => {
                arg_index += 1;
                if arg_index >= args_len {
                    let message = format!("Argument {} must be followed by one or more path", arg);
                    return Command::Error(message);
                }
                loop {
                    if arg_index >= args_len {
                        break;
                    }
                    let repo = &args[arg_index];
                    if !repo.starts_with('-') {
                        arguments.repos.push(repo.to_string());
                        arg_index += 1;
                        continue;
                    }
                    break;
                }
            }
            "--query" | "-q" => {
                arg_index += 1;
                if arg_index >= args_len {
                    let message = format!("Argument {} must be followed by the query", arg);
                    return Command::Error(message);
                }
                optional_query = Some(args[arg_index].to_string());
                arg_index += 1;
            }
            "--analysis" | "-a" => {
                arguments.analysis = true;
                arg_index += 1;
            }
            "--pagination" | "-p" => {
                arguments.pagination = true;
                arg_index += 1;
            }
            "--pagesize" | "-ps" => {
                arg_index += 1;
                if arg_index >= args_len {
                    let message = format!("Argument {} must be followed by the page size", arg);
                    return Command::Error(message);
                }
                let page_size_result = args[arg_index].parse::<usize>();
                if page_size_result.is_err() {
                    return Command::Error("Invalid page size".to_string());
                }
                let page_size = page_size_result.ok().unwrap();
                arguments.page_size = page_size;
                arg_index += 1;
            }
            "--output" | "-o" => {
                arg_index += 1;
                if arg_index >= args_len {
                    let message = format!("Argument {} must be followed by output format", arg);
                    return Command::Error(message);
                }
                let output_type = &args[arg_index].to_lowercase();
                if output_type == "csv" {
                    arguments.output_format = OutputFormat::CSV;
                } else if output_type == "json" {
                    arguments.output_format = OutputFormat::JSON;
                } else if output_type == "render" {
                    arguments.output_format = OutputFormat::Render;
                } else {
                    return Command::Error("Invalid output format".to_string());
                }
                arg_index += 1;
            }
            _ => return Command::Error(format!("Unknown command {}", arg)),
        }
    }
    if arguments.repos.is_empty() {
        let current_dir = std::env::current_dir();
        if current_dir.is_ok() {
            arguments.repos.push(
                current_dir
                    .ok()
                    .unwrap()
                    .as_os_str()
                    .to_str()
                    .unwrap_or(".")
                    .to_string(),
            );
        } else {
            return Command::Error("Missing repositories paths".to_string());
        }
    }
    if let Some(query) = optional_query {
        Command::QueryMode(query, arguments)
    } else {
        Command::ReplMode(arguments)
    }
}
pub fn print_help_list() {
    println!("GitQL is a SQL like query language to run on local repositories");
    println!();
    println!("Usage: gitql [OPTIONS]");
    println!();
    println!("Options:");
    println!("-r,  --repos <REPOS>        Path for local repositories to run query on");
    println!("-q,  --query <GQL Query>    GitQL query to run on selected repositories");
    println!("-p,  --pagination           Enable print result with pagination");
    println!("-ps, --pagesize             Set pagination page size [default: 10]");
    println!("-o,  --output               Set output format [render, json, csv]");
    println!("-a,  --analysis             Print Query analysis");
    println!("-h,  --help                 Print GitQL help");
    println!("-v,  --version              Print GitQL Current Version");
}
#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_empty_arguments() {
        let arguments = vec!["gitql".to_string()];
        let command = parse_arguments(&arguments);
        assert!(matches!(command, Command::ReplMode { .. }));
    }
    #[test]
    fn test_repl_arguments() {
        let arguments = vec!["gitql".to_string(), "--repos".to_string(), ".".to_string()];
        let command = parse_arguments(&arguments);
        assert!(matches!(command, Command::ReplMode { .. }));
    }
    #[test]
    fn test_query_arguments() {
        let arguments = vec![
            "gitql".to_string(),
            "-q".to_string(),
            "Select * from table".to_string(),
        ];
        let command = parse_arguments(&arguments);
        assert!(matches!(command, Command::QueryMode { .. }));
    }
    #[test]
    fn test_arguments_with_help() {
        let arguments = vec![
            "gitql".to_string(),
            "dummy".to_string(),
            "--help".to_string(),
        ];
        let command = parse_arguments(&arguments);
        assert_eq!(command, Command::Help);
    }
    #[test]
    fn test_arguments_with_version() {
        let arguments = vec![
            "gitql".to_string(),
            "dummy".to_string(),
            "--version".to_string(),
        ];
        let command = parse_arguments(&arguments);
        assert_eq!(command, Command::Version);
    }
    #[test]
    fn test_arguments_with_valid_page_size() {
        let arguments = vec![
            "gitql".to_string(),
            "--pagesize".to_string(),
            "10".to_string(),
        ];
        let command = parse_arguments(&arguments);
        assert!(!matches!(command, Command::Error { .. }));
    }
    #[test]
    fn test_arguments_with_invalid_page_size() {
        let arguments = vec![
            "gitql".to_string(),
            "--pagesize".to_string(),
            "-".to_string(),
        ];
        let command = parse_arguments(&arguments);
        assert!(matches!(command, Command::Error { .. }));
    }
    #[test]
    fn test_arguments_with_valid_output_format() {
        let arguments = vec![
            "gitql".to_string(),
            "--output".to_string(),
            "csv".to_string(),
        ];
        let command = parse_arguments(&arguments);
        assert!(!matches!(command, Command::Error { .. }));
    }
    #[test]
    fn test_arguments_with_invalid_output_format() {
        let arguments = vec![
            "gitql".to_string(),
            "--output".to_string(),
            "text".to_string(),
        ];
        let command = parse_arguments(&arguments);
        assert!(matches!(command, Command::Error { .. }));
    }
}