use crate::cli::{
    clap::{passthrough_subcommand, value_arg},
    commands::{COMMAND_FAMILIES, command_family},
};
use clap::{Arg, ArgAction, Command};
use std::ffi::OsString;

pub const DISPATCH_ARGS: &str = "args";
pub const INTERNAL_NETWORK_OPTION: &str = "--__icq-network";

pub fn network_arg() -> Arg {
    value_arg("network")
        .long("network")
        .value_name("name")
        .help("ICP CLI network for networked commands")
}

pub fn internal_network_arg() -> Arg {
    value_arg("network").long("__icq-network").hide(true)
}

pub fn top_level_dispatch_command() -> Command {
    let command = Command::new("icq")
        .disable_help_flag(true)
        .disable_help_subcommand(true)
        .disable_version_flag(true)
        .arg(
            Arg::new("version")
                .short('V')
                .long("version")
                .action(ArgAction::SetTrue),
        )
        .arg(network_arg().global(true));

    COMMAND_FAMILIES.iter().fold(command, |command, family| {
        command.subcommand(passthrough_subcommand(
            Command::new(family.name).about(family.about),
        ))
    })
}

pub fn command_local_global_option(args: &[OsString]) -> Option<&'static str> {
    let mut index = 0;
    while index < args.len() {
        let arg = args[index].to_str()?;
        if command_family(arg).is_some() {
            return args[index + 1..]
                .iter()
                .filter_map(|arg| arg.to_str())
                .find_map(global_option_name);
        }
        index += if arg == "--network" { 2 } else { 1 };
    }
    None
}

fn global_option_name(arg: &str) -> Option<&'static str> {
    match arg {
        "--network" => Some("--network"),
        _ if arg.starts_with("--network=") => Some("--network"),
        _ => None,
    }
}

pub fn apply_global_network(
    command: &str,
    tail: &mut Vec<OsString>,
    global_network: Option<String>,
) {
    let Some(global_network) = global_network else {
        return;
    };
    if tail_has_option(tail, INTERNAL_NETWORK_OPTION) {
        return;
    }
    if !command_accepts_global_network(command, tail) {
        return;
    }

    tail.push(OsString::from(INTERNAL_NETWORK_OPTION));
    tail.push(OsString::from(global_network));
}

fn command_accepts_global_network(command: &str, tail: &[OsString]) -> bool {
    command_family(command).is_some_and(|family| (family.accepts_global_network)(tail))
}

fn tail_has_option(tail: &[OsString], name: &str) -> bool {
    tail.iter().any(|arg| arg.to_str() == Some(name))
}
