use clap::{Arg, Command};
use clap_complete::generate;
use clap_complete::Shell::{Bash, Elvish, Fish, PowerShell, Zsh};
use std::ffi::OsString;
use std::path::{Path, PathBuf};
use std::{env, fs};
use toml::de::from_str;

use crate::base::MmhConfig;
use crate::cmd::{add, connect, list, ping, remove, sftp};

mod base;
mod cmd;

fn get_base_path() -> PathBuf {
    let config_path =
        Path::new(&env::var("HOME").expect("Failed to get HOME environment variable")).join(".mmh");
    if !config_path.exists() {
        fs::create_dir_all(&config_path).expect("create base dir error!");
    }
    config_path
}

fn get_config_path() -> PathBuf {
    get_base_path().join("config.toml")
}

fn get_config() -> MmhConfig {
    let config_str = fs::read_to_string(get_config_path()).unwrap_or_default();
    from_str(&config_str).unwrap()
}

fn cli() -> Command {
    Command::new("mmh")
        .version("0.1.21")
        .about("my manage ssh tools!")
        .subcommand_required(true)
        .arg_required_else_help(true)
        .allow_external_subcommands(true)
        .subcommand(Command::new("add").short_flag('a').about("add ssh config"))
        .subcommand(
            Command::new("list")
                .short_flag('l')
                .about("list ssh config"),
        )
        .subcommand(
            Command::new("remove")
                .short_flag('r')
                .about("remove ssh config")
                .arg(Arg::new("name").help("server config name ").required(false)),
        )
        .subcommand(
            Command::new("connect")
                .short_flag('c')
                .about("connect ssh config")
                .arg(Arg::new("name").help("server config name ").required(false)),
        )
        .subcommand(
            Command::new("sftp")
                .short_flag('s')
                .about("sftp connect ssh config")
                .arg(Arg::new("name").help("server config name ").required(false))
                .arg(Arg::new("remoteDir").help("remote dir").required(false)),
        )
        .subcommand(
            Command::new("ping")
                .short_flag('p')
                .about("ping ssh config"),
        )
        .subcommand(
            Command::new("copy")
                .arg(Arg::new("name").help("server config name "))
                .about("copy server config"),
        )
        .subcommand(Command::new("check").about("check tools!"))
        .subcommand(Command::new("init").about("init tools!"))
}

fn main() {
    let matches = cli().get_matches();

    match matches.subcommand() {
        Some(("add", _sub_matches)) => add(),
        Some(("list", _sub_matches)) => list(),
        Some(("remove", sub_matches)) => {
            let name = sub_matches.get_one::<String>("name");
            remove(name)
        }
        Some(("connect", sub_matches)) => {
            let name = sub_matches.get_one::<String>("name");
            connect(name)
        }
        Some(("sftp", sub_matches)) => {
            let name = sub_matches.get_one::<String>("name");
            let remote_dir = sub_matches.get_one::<String>("remoteDir");
            sftp(name, remote_dir)
        }
        Some(("ping", _sub_matches)) => ping(),
        Some(("copy", sub_matches)) => {
            let name = sub_matches.get_one::<String>("name");
            cmd::copy_server_config(name)
        }
        Some(("init", _sub_matches)) => init(),
        Some(("check",_sub_matches)) => check(),
        Some((ext, sub_matches)) => {
            let args = sub_matches
                .get_many::<OsString>("")
                .into_iter()
                .flatten()
                .collect::<Vec<_>>();
            println!("Calling out to {ext:?} with {args:?}");
        }
        _ => unreachable!(),
    }
}

fn init() {
    let mut cmd = cli();
    print_completions(&mut cmd);
    let path_str = get_base_path()
        .canonicalize()
        .unwrap()
        .to_string_lossy()
        .to_string();
    println!(
        r#"init success!
            auto prompt:
                    zsh: source {path_str}/complete/zsh.complete
                    bash: source {path_str}/complete/bash.complete
                    fish: source {path_str}/complete/fish.complete
                    powerShell: source {path_str}/complete/powerShell.complete
                    elvish: source {path_str}/complete/elvish.complete
            "#
    );
}

//根据path下的 文件列表 判断是否满足当前工具依赖的 工具
fn check (){
    let require_tools = vec!["git", "ssh","sftp","sshpass"];
    //获取当前系统 PATH变量
    let path_str = env::var("PATH").expect("Failed to get PATH environment variable");
    // 遍历PATH变量中的路径
    let mut tools = vec![];
    for path in path_str.split(':') {
        // 遍历当前路径下的文件
        for entry in fs::read_dir(path).expect("Failed to read directory") {
            let entry = entry.expect("Failed to read directory entry");
            let file_name = entry.file_name();
            tools.push(file_name.to_string_lossy().to_string())
        }
    }
    //判断 require_tools 那些不再tools中
    for tool in require_tools {
        if tools.contains(&tool.to_string()) {
            println!("{} found! \u{2705}   ", tool);
        }else{
            //输出红色 提示
            println!("{} not found! \u{2717}    ", tool);
        }
    }
}

//生成常见的shell 提示脚本
fn print_completions(cmd: &mut Command) {
    let path = get_base_path().join("complete");
    if !path.exists() {
        fs::create_dir_all(&path).expect("create base dir error!");
    }
    generate(
        Zsh,
        cmd,
        cmd.get_name().to_string(),
        &mut fs::File::create(path.join("zsh.complete")).unwrap(),
    );
    generate(
        Bash,
        cmd,
        cmd.get_name().to_string(),
        &mut fs::File::create(path.join("bash.complete")).unwrap(),
    );
    generate(
        Fish,
        cmd,
        cmd.get_name().to_string(),
        &mut fs::File::create(path.join("fish.complete")).unwrap(),
    );
    generate(
        PowerShell,
        cmd,
        cmd.get_name().to_string(),
        &mut fs::File::create(path.join("powerShell.complete")).unwrap(),
    );
    generate(
        Elvish,
        cmd,
        cmd.get_name().to_string(),
        &mut fs::File::create(path.join("elvish.complete")).unwrap(),
    );
}