use std::path::PathBuf;
use std::time::Instant;

use indoc::indoc;
use once_cell::sync::Lazy;
use structopt::clap::Shell;
use structopt::StructOpt;

use crate::version::get_verbose_version;

static VERSION: Lazy<String> = Lazy::new(|| get_verbose_version().expect("missing version info"));

#[derive(Debug, StructOpt)]
#[structopt(name = "nmk", version = VERSION.as_str())]
pub struct CmdOpt {
    #[structopt(short = "2", help = "Assume the terminal supports 256 colours")]
    pub support_256_color: bool,
    #[structopt(
        short = "L",
        long = "socket",
        default_value = "nmk",
        value_name = "NAME",
        help = "Use a different tmux socket name"
    )]
    pub socket: String,
    #[structopt(
        long,
        value_name = "FILE",
        help = "Specify an alternative tmux configuration file"
    )]
    pub tmux_conf: Option<PathBuf>,
    #[structopt(short = "l", long, help = "Start zsh login shell")]
    pub login: bool,
    #[structopt(long, help = "Detach the client when the session is destroyed")]
    pub detach_on_destroy: bool,
    #[structopt(short, parse(from_occurrences), help = "Request verbose logging")]
    pub verbosity: u8,
    #[structopt(short, help = "Explicitly informs tmux that UTF-8 is supported")]
    pub unicode: bool,
    #[structopt(long, help = "Prints usage time")]
    pub usage: bool,
    #[structopt(long, help = "Display Message of The Day")]
    pub motd: bool,
    #[structopt(long, help = "Render tmux config then exit")]
    pub render: bool,
    #[structopt(subcommand)]
    pub cmd: Option<SubCommand>,
    #[structopt(skip = Instant::now())]
    pub start_time: Instant,
}

#[derive(Debug, StructOpt)]
pub enum SubCommand {
    #[structopt(about = "Backup files to do clean install")]
    Backup(Backup),
    #[structopt(
        about = "Generate tab-completion scripts for your shell",
        name = "completions"
    )]
    Completions(Completion),
    /// Integrate with ssh
    ///
    /// Add/remove configuration to `authorized_keys` so that when login with specified key,
    /// login shell will start automatically.
    Infect(Infect),
    #[structopt(about = "Display launcher information")]
    Info,
    #[structopt(about = "Setup components from files")]
    Setup(Setup),
    #[structopt(about = "Generate template files")]
    Template,
    #[structopt(about = "Run tmux command on running tmux server", long_about = TMUX_LONG_ABOUT)]
    Tmux(Tmux),
}

const TMUX_LONG_ABOUT: &str = indoc! {r#"
    Run tmux command under nmk environment.
    
    You should run tmux directly. This subcommand is added without any usecase.

    When passing tmux flag, prefix any tmux command with -- to avoid Structopt confusion.
    For example, "nmk tmux -- -V" to get tmux version.
"#};

#[derive(Debug, StructOpt)]
pub struct Backup {
    #[structopt(short, long, value_name = "FILE", help = "write to file")]
    pub output: Option<PathBuf>,
}

#[derive(Debug, StructOpt)]
pub struct Completion {
    #[structopt(
        short,
        long,
        value_name = "FILE",
        help = "write to file instead of stdout"
    )]
    pub output: Option<PathBuf>,
    #[structopt(value_name = "SHELL", possible_values = Shell::variants().as_ref())]
    pub shell: Shell,
}

#[derive(Debug, StructOpt)]
pub struct Infect {
    #[structopt(
        long,
        value_name = "FILE",
        help = "path to custom authorized_keys file"
    )]
    pub authorized_keys: Option<PathBuf>,
    #[structopt(short, long, help = "remove ssh options from all of my keys")]
    pub remove: bool,
}

#[derive(Debug, StructOpt)]
pub struct Setup {
    #[structopt(short, long, value_name = "FILE", help = "Setup dotfiles")]
    pub dotfiles: Option<PathBuf>,
    #[structopt(short, long, value_name = "FILE", help = "Setup launcher")]
    pub launcher: Option<PathBuf>,
    #[structopt(short, long, value_name = "FILE", help = "Setup vendor files")]
    pub vendor: Option<PathBuf>,
}

#[derive(Debug, StructOpt)]
pub struct Tmux {
    #[structopt(value_name = "command")]
    pub args: Vec<String>,
}

pub fn parse() -> CmdOpt {
    CmdOpt::from_args()
}
