Framework for embedding localizations into Rust types
use super::macros::impl_with_validator;
use crate::{InteractionEnvironment, InteractionError};
use l10n_embed::Localize;

struct PasswordConfirmation {
    prompt: String,
    mismatch_error: String,
}

pub struct Password<'environment> {
    environment: &'environment InteractionEnvironment,
    prompt: String,
    confirmation: Option<PasswordConfirmation>,
    validator: Option<Box<dyn Fn(&String) -> Result<(), String>>>,
}

impl_with_validator!(Password);

impl<'environment> Password<'environment> {
    pub fn new<L: Localize>(environment: &'environment InteractionEnvironment, prompt: L) -> Self {
        let mut localized_prompt = String::new();
        prompt.localize(&environment.localization_context, &mut localized_prompt);

        Self {
            environment,
            prompt: localized_prompt,
            confirmation: None,
            validator: None,
        }
    }

    pub fn with_confirmation<L1: Localize, L2: Localize>(
        mut self,
        confirmation_prompt: L1,
        mismatch_error: L2,
    ) -> Self {
        let mut localized_prompt = String::new();
        confirmation_prompt.localize(
            &self.environment.localization_context,
            &mut localized_prompt,
        );

        let mut localized_mismatch_error = String::new();
        mismatch_error.localize(
            &self.environment.localization_context,
            &mut localized_mismatch_error,
        );

        self.confirmation = Some(PasswordConfirmation {
            prompt: localized_prompt,
            mismatch_error: localized_mismatch_error,
        });

        self
    }

    pub fn interact(self) -> Result<Option<String>, InteractionError> {
        match self.environment.interaction_context {
            crate::InteractionContext::Terminal => {
                let mut prompt =
                    dialoguer::Password::with_theme(&*super::THEME).with_prompt(self.prompt);

                if let Some(confirmation) = self.confirmation {
                    prompt =
                        prompt.with_confirmation(confirmation.prompt, confirmation.mismatch_error);
                }

                if let Some(validator) = self.validator {
                    prompt = prompt.validate_with(validator);
                }

                Ok(Some(prompt.interact()?))
            }
            crate::InteractionContext::NonInteractive => Ok(None),
        }
    }
}