use clap::{Parser, ValueEnum, ValueHint};

use clap_complete::engine::{ArgValueCompleter, CompletionCandidate};

use std::ffi::OsStr;

#[derive(Parser, Debug, PartialEq)]
#[clap(author, version, about, long_about = None)]
#[command(arg_required_else_help(true))]
pub struct Args {
    #[arg(
    short = 'I', long,
    value_delimiter = ',', num_args = 1..,
    add = ArgValueCompleter::new(ignore_completer),
    value_parser = ["dupes", "same-named", "tmpfiles"],
    long_help = "\
Comma-separated list of things to ignore: dupes, same-named, tmpfiles
  dupes:      ignore duplicate files
  same-named: ignore same-named files
  tmpfiles:   ignore temporary files
"
)]
    pub ignore: Vec<String>,
    #[arg(short = 'v', long = "verbose", help = "Verbose operation?")]
    pub verbose: bool,
    #[arg(short = 'L', long = "correct-le", help = "Correct line endings")]
    pub correct_le: bool,
    #[arg(short = 'C', long = "correct-perms", help = "Correct permissions")]
    pub correct_perms: bool,
    #[arg(long = "no-colors", help = "Don't display messages in color")]
    pub no_colors: bool,
    #[arg(long = "urlcheck", help = "Check URLs found in README files")]
    pub urlcheck: bool,
    #[arg(long = "warnings-no-errors", help = "Don't treat warnings as errors")]
    pub warnings_no_errors: bool,
    #[arg(short = 'T', long = "tds-zip", help = "tds zip archive", group = "tds", value_hint = ValueHint::FilePath)]
    pub tds_zip: Option<String>,
    #[arg(
        short = 'e',
        long = "explain",
        help = "Explain error or warning message",
        group = "only_one"
    )]
    pub explain: Option<String>,
    #[arg(
        long = "explain-all",
        help = "Explains all error or warning messages",
        group = "only_one"
    )]
    pub explain_all: bool,

    /// Print completion setup instructions for the given shell.
    /// For nushell, generates a static completion script to stdout.
    ///
    /// Examples:
    ///   pkgcheck --completion fish > ~/.config/fish/completions/pkgcheck.fish
    ///   pkgcheck --completion nushell > ~/.config/nushell/completions/pkgcheck.nu
    #[arg(long = "generate-completion", value_name = "SHELL")]
    pub generate_completion: Option<Shell>,

    // #[arg(long = "generate-completion", group = "only_one", value_enum)]
    // pub generator: Option<Shell>,
    #[arg(
        long = "show-temp-endings",
        help = "Show file endings for temporary files",
        group = "only_one"
    )]
    pub show_tmp_endings: bool,
    #[arg(short = 'd', long = "package-dir", help = "Package directory", value_hint = ValueHint::DirPath)]
    pub pkg_dir: Option<String>,
    #[arg(long = "config-file", help = "Specify config file to use", value_hint = ValueHint::FilePath)]
    pub config_file: Option<String>,
}

// ── Ignore values ─────────────────────────────────────────────────────────────

const IGNORE_VALUES: &[&str] = &["dupes", "same-named", "tmpfiles"];

pub fn ignore_completer(current: &OsStr) -> Vec<CompletionCandidate> {
    let current = current.to_str().unwrap_or("");

    let (prefix, fragment) = match current.rfind(',') {
        Some(pos) => (&current[..=pos], &current[pos + 1..]),
        None => ("", current),
    };

    let already: Vec<&str> = prefix
        .split(',')
        .map(|s| s.trim_end_matches(','))
        .filter(|s| !s.is_empty())
        .collect();

    IGNORE_VALUES
        .iter()
        .filter(|&&v| v.starts_with(fragment) && !already.contains(&v))
        .map(|&v| CompletionCandidate::new(format!("{prefix}{v}")))
        .collect()
}

// ── Shell enum ────────────────────────────────────────────────────────────────

#[derive(Debug, Clone, ValueEnum, PartialEq)]
pub enum Shell {
    Fish,
    Bash,
    Zsh,
    Nushell,
}

impl Shell {
    fn setup_line(&self, bin: &str) -> Option<String> {
        match self {
            Shell::Fish => Some(format!("COMPLETE=fish {bin} | source")),
            Shell::Bash => Some(format!("source <(COMPLETE=bash {bin})")),
            Shell::Zsh => Some(format!("source <(COMPLETE=zsh {bin})")),
            Shell::Nushell => None,
        }
    }

    fn config_file(&self, bin: &str) -> String {
        match self {
            Shell::Fish => format!("~/.config/fish/completions/{bin}.fish"),
            Shell::Bash => "~/.bashrc".to_string(),
            Shell::Zsh => "~/.zshrc".to_string(),
            Shell::Nushell => format!("~/.config/nushell/completions/{bin}.nu"),
        }
    }

    pub fn usage(&self, bin: &str) -> String {
        let file = self.config_file(bin);
        match self.setup_line(bin) {
            Some(line) => format!("Add the following line to {file}:\n\n    {line}"),
            None => format!(
                "Generate static completion file:\n\n    \
                 {bin} --completion nushell > {file}\n\n\
                 Then add the following line to ~/.config/nushell/config.nu:\n\n    \
                 source {file}"
            ),
        }
    }
}
impl Args {
    pub fn ignore_dupes(&self) -> bool {
        self.ignore.iter().any(|v| v == "dupes")
    }

    pub fn ignore_same_named(&self) -> bool {
        self.ignore.iter().any(|v| v == "same-named")
    }

    pub fn ignore_tmpfiles(&self) -> bool {
        self.ignore.iter().any(|v| v == "tmpfiles")
    }
}

// ── Nushell completion ────────────────────────────────────────────────────────

/// Generate the Nushell static completion script for pkgcheck.
pub fn nushell_completion() -> String {
    let lines: &[&str] = &[
        r#"def "nu-complete pkgcheck ignore" [context: string] {"#,
        r#"    let current = ($context | split row " " | last)"#,
        r#"    let prefix = if ($current | str contains ",") {"#,
        r#"        $current | split row "," | drop 1 | str join "," | $"($in),""#,
        r#"    } else {"#,
        r#"        """#,
        r#"    }"#,
        r#"    let already = ($prefix | split row "," | each { str trim } | where { |v| $v != "" })"#,
        r#"    let options = ["dupes" "same-named" "tmpfiles"]"#,
        r#"    $options"#,
        r#"    | where { |v| not ($already | any { |a| $a == $v }) }"#,
        r#"    | each { |v| $"($prefix)($v)" }"#,
        r#"}"#,
        r#""#,
        r#"def "nu-complete pkgcheck completion" [] {"#,
        r#"    ["fish" "bash" "zsh" "nushell"]"#,
        r#"}"#,
        r#""#,
        r#"export extern main ["#,
        r#"    --ignore: string@"nu-complete pkgcheck ignore"        # Comma-separated: dupes, same-named, tmpfiles"#,
        r#"    --completion: string@"nu-complete pkgcheck completion" # Shell to generate completion for"#,
        r#"    --help(-h)                                             # Print help"#,
        r#"]"#,
    ];
    lines.join("\n") + "\n"
}
