use std::env;
use crate::{config::Cfg, list, modification, update, setup, download, io, error::{MLError, ErrorType, MLE}};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Input {
    pub command: Cmd,
    pub subcommand: Option<Subcmd>,
    pub args: Option<Vec<String>>,
    pub direct_download: bool,
    pub all_lists: bool,
    pub delete_old: bool,
    pub clean: bool,
    pub disable_download: bool,
    pub version: bool,
}

impl Input {
    fn from(string: &str) -> MLE<Self> {
        let mut split: Vec<&str> = string.split(' ').collect();
        
        let mut direct_download = false;
        let mut all_lists = false;
        let mut delete_old = false;
        let mut clean = false;
        let mut disable_download = false;
        let mut version = false;
        
        let mut toremove: Vec<usize> = vec![];
        for (i, input) in split.clone().into_iter().enumerate() {
            if input.starts_with("--") {
                match input {
                    "--direct-download" => direct_download = true,
                    "--all-lists" => all_lists = true,
                    "--delete-old" => delete_old = true,
                    "--clean" => clean = true,
                    "--disable-download" => disable_download = true,
                    "--version" => version = true,
                    _ => continue,
                }
                toremove.push(i)
            }
        }

        for rem in toremove.into_iter().rev() {
            split.remove(rem);
        }
        
        if version {
            match std::env::var("DEV") {
                Ok(dev) => {
                    let devint = dev.parse::<i32>().unwrap();
                    if devint >= 1 {
                        println!("Modlist by FxQnLr v{} (DEV)", env!("CARGO_PKG_VERSION"));
                    } else {
                        println!("Modlist by FxQnLr v{}", env!("CARGO_PKG_VERSION"));
                    }
                },
                Err(..) => println!("Modlist by FxQnLr v{}", env!("CARGO_PKG_VERSION")),
            }

            std::process::exit(0);
        }

        let command = Cmd::from(split.remove(0))?;
        let subcommand = match split.is_empty() {
            false => Some(Subcmd::from(split.remove(0))?),
            true => None
        };
        
        let args = match split.is_empty() {
            true => None,
            false => {
                let mut strsplit: Vec<String> = Vec::new();
                for s in split {
                    strsplit.push(String::from(s))
                }
                Some(strsplit)
            }
        };

        Ok(Self { command, subcommand, args, direct_download, all_lists, delete_old, clean, disable_download, version })
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Cmd {
    Mod,
    List,
    Update,
    Download,
    Setup,
    Io
}

impl Cmd {
    fn from(string: &str) -> MLE<Self> {
        let cmd = match string {
            "mod" => Self::Mod,
            "list" => Self::List,
            "update" => Self::Update,
            "download" => Self::Download,
            "setup" => Self::Setup,
            "io" => Self::Io,
            _ => return Err(MLError::new(ErrorType::ArgumentError, "Unknown command"))
        };
        Ok(cmd)
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Subcmd {
    Add,
    Remove,
    Change,
    Version,
    Export,
    Import,
}

impl Subcmd {
    fn from(string: &str) -> MLE<Self> {
        let cmd = match string {
            "add" => Self::Add,
            "remove" => Self::Remove,
            "change" => Self::Change,
            "version" => Self::Version,
            "export" => Self::Export,
            "import" => Self::Import,
            _ => return Err(MLError::new(ErrorType::ArgumentError, "SUBCMD_NOT_FOUND"))
        };
        Ok(cmd)
    }
}

pub async fn get_input(config: Cfg) -> Result<(), Box<dyn std::error::Error>> {
    let mut args: Vec<String> = env::args().collect();
    args.reverse();
    args.pop();
    args.reverse();
    
    let input = Input::from(&args.join(" "))?;

    match input.command {
        Cmd::Mod => { 
            modification(config, input).await
        },
        Cmd::List => {
            list(config, input).await
        },
        Cmd::Update => {
            match update(config, input).await {
                Ok(..) => Ok(()),
                Err(..) => Err(Box::new(MLError::new(ErrorType::Other, "UPDATE_ERR")))
            }
        },
        Cmd::Setup => {
            setup(config).await
        },
        Cmd::Download => {
            download(config, input).await
        },
        Cmd::Io => {
            io(config, input).await
        }
    }
}

#[test]
fn input_from() {
    let string = "list add test 1.19.2 fabric";
    let input = Input{ command: Cmd::List, subcommand: Some(Subcmd::Add), args: Some(vec![String::from("test"), String::from("1.19.2"), String::from("fabric")]), direct_download: false, all_lists: false, clean: false, delete_old: false, disable_download: false, version: false };
    assert_eq!(Input::from(string).unwrap(), input);

    let string = "update --direct-download --delete-old";
    let input = Input{ command: Cmd::Update, subcommand: None, args: None, direct_download: true, all_lists: false, clean: false, delete_old: true, disable_download: false, version: false };
    assert_eq!(Input::from(string).unwrap(), input);
}