use anyhow::Result;
use clap::Parser;

use crate::{crypto::context::PlainText, git::git_add_file_if_necessary};

use super::Handle;

/// Generates a new password of length pass-length (or PASSWORD_STORE_GENERATE_LENGTH
/// if unspecified) and insert into pass-name.
#[derive(Parser, Debug)]
pub struct Generate {
    /// If --no-symbols or -n is specified, do not use any non-aplphanumeric
    /// characers in the generated password.
    #[clap(long, short)]
    no_symbols: bool,

    /// If --clip or -c is specified, do not print the password but instead copy
    /// it to the clipboard.
    #[cfg(feature = "clipcopy")]
    #[clap(long, short)]
    clip: bool,

    /// If --force or -f is specified, silently overwrite pass-name if it exists.
    #[clap(long, short)]
    force: bool,

    #[clap(name = "pass-name")]
    name: String,

    #[clap(name = "pass-length", env = "PASSWORD_STORE_GENERATE_LENGTH")]
    length: Option<usize>,
}

impl Handle for Generate {
    fn invoke(&self, ctx: &crate::context::Context) -> Result<()> {
        let length = self.length.unwrap_or(16);
        let mut gen = passgen::Generator::default()
            .with_strict(true)
            .with_digits()
            .with_lowercase_letters()
            .with_uppercase_letters()
            .with_length(length);
        if !self.no_symbols {
            gen = gen.with_symbols();
        }

        let passwd = gen.generate();
        #[cfg(feature = "clipcopy")]
        if self.clip {
            use clipboard::{ClipboardContext, ClipboardProvider};
            ClipboardContext::new()
                .and_then(|mut cp| cp.set_contents(passwd.clone()))
                .map_err(|e| anyhow::anyhow!("{}", &e))?;
        }

        let path = ctx.encrypt_file(&self.name, PlainText::from(passwd.as_str()), self.force)?;
        git_add_file_if_necessary(
            ctx.path(),
            &path,
            format!("generate password for {}", &self.name),
        )
    }
}
