use std::{
    fmt::{Arguments, Debug},
    sync::OnceLock,
};

mod cli_prompter;
pub use cli_prompter::*;

static PROMPTER: OnceLock<&'static dyn Prompter> = OnceLock::new();
pub fn set_prompter(prompter: &'static dyn Prompter) -> Result<(), &'static dyn Prompter> {
    PROMPTER.set(prompter)
}
pub fn get_prompter() -> Option<&'static dyn Prompter> {
    PROMPTER.get().copied()
}

pub trait Prompter: Sync + Debug {
    /// Tells the prompter to start an "Okay" prompt
    ///
    /// This prompt just requires user input for a confirmation something happend.
    /// An example usage is "Press any key to continue..."
    fn okay(&self, msg: Arguments);

    /// Tells the prompter to start a "Yes/No" prompt
    ///
    /// This prompt just requires user input for a confirmation if they want to do something.
    /// An example usage is "Are you sure you want to do this? \[Y/n\]"
    ///
    /// This function should return [`None`] if the result couldn't be parsed
    fn ask(&self, msg: Arguments, default: bool) -> Option<bool>;
}

/// Requests an "Okay" prompt to the current prompter lazily using format args
///
/// If no prompter is set, the args will not be parsed
#[macro_export]
macro_rules! okay {
    ($($arg:tt)*) => {{
        if let Some(prompter) = $crate::prompts::get_prompter() {
            prompter.okay(format_args!($($arg)*));
        }
    }};
}

/// Requests an "Ask" or "Yes/No" prompt to the current prompter lazily using format args
///
/// If no prompter is set, the args will not be parsed
#[macro_export]
macro_rules! ask {
    ($default:expr, $($arg:tt)*) => {{
        if let Some(prompter) = $crate::prompts::get_prompter() {
            prompter.ask(format_args!($($arg)*), $default)
        } else {
            None
        }
    }};
}
