use crate::meta_plugin::{MetaPlugin, MetaPluginType};

#[derive(Debug, Clone, Default)]
/// Meta plugin for capturing current user and group information.
///
/// This plugin collects user ID, group ID, username, and group name for the process
/// running the keep application, providing context about the creator of items.
pub struct UserMetaPlugin {
    base: crate::meta_plugin::BaseMetaPlugin,
}

impl UserMetaPlugin {
    /// Creates a new `UserMetaPlugin` instance.
    ///
    /// # Arguments
    ///
    /// * `options` - Optional configuration options for the plugin.
    /// * `outputs` - Optional output mappings for metadata.
    ///
    /// # Returns
    ///
    /// A new instance of `UserMetaPlugin`.
    pub fn new(
        options: Option<std::collections::HashMap<String, serde_yaml::Value>>,
        outputs: Option<std::collections::HashMap<String, serde_yaml::Value>>,
    ) -> UserMetaPlugin {
        let mut base = crate::meta_plugin::BaseMetaPlugin::new();

        // Initialize with helper function
        base.initialize_plugin(
            &["user_uid", "user_gid", "user_name", "user_group"],
            &options,
            &outputs,
        );

        UserMetaPlugin { base }
    }

    /// Gets the current username.
    ///
    /// # Returns
    ///
    /// An `Option<String>` with the username, or `None` if unavailable.
    fn get_current_username() -> Option<String> {
        uzers::get_user_by_uid(uzers::get_current_uid())
            .map(|user| user.name().to_string_lossy().to_string())
    }

    /// Gets the current group name.
    ///
    /// # Returns
    ///
    /// An `Option<String>` with the group name, or `None` if unavailable.
    fn get_current_groupname() -> Option<String> {
        uzers::get_group_by_gid(uzers::get_current_gid())
            .map(|group| group.name().to_string_lossy().to_string())
    }
}

impl MetaPlugin for UserMetaPlugin {
    /// Initializes the plugin, capturing user information.
    ///
    /// # Returns
    ///
    /// A `MetaPluginResponse` with user metadata and `is_finalized` set to `true`.
    fn initialize(&mut self) -> crate::meta_plugin::MetaPluginResponse {
        let mut metadata = Vec::new();

        // Get user info
        let uid = uzers::get_current_uid().to_string();
        let gid = uzers::get_current_gid().to_string();
        let username = Self::get_current_username().unwrap_or_else(|| "unknown".to_string());
        let groupname = Self::get_current_groupname().unwrap_or_else(|| "unknown".to_string());

        // Process each output
        let values = [
            ("user_uid", uid),
            ("user_gid", gid),
            ("user_name", username),
            ("user_group", groupname),
        ];

        for (name, value) in values {
            if let Some(meta_data) = crate::meta_plugin::process_metadata_outputs(
                name,
                serde_yaml::Value::String(value),
                self.base.outputs(),
            ) {
                metadata.push(meta_data);
            }
        }

        crate::meta_plugin::MetaPluginResponse {
            metadata,
            is_finalized: true,
        }
    }

    /// Returns the type of this meta plugin.
    ///
    /// # Returns
    ///
    /// `MetaPluginType::User`.
    fn meta_type(&self) -> MetaPluginType {
        MetaPluginType::User
    }

    fn set_save_meta(&mut self, save_meta: crate::meta_plugin::SaveMetaFn) {
        self.base.set_save_meta(save_meta);
    }

    fn save_meta(&self, name: &str, value: &str) {
        self.base.save_meta(name, value);
    }

    /// Returns a reference to the outputs mapping.
    ///
    /// # Returns
    ///
    /// A reference to the `HashMap` of outputs.
    fn outputs(&self) -> &std::collections::HashMap<String, serde_yaml::Value> {
        self.base.outputs()
    }

    /// Returns a mutable reference to the outputs mapping.
    ///
    /// # Returns
    ///
    /// A mutable reference to the `HashMap` of outputs.
    fn outputs_mut(
        &mut self,
    ) -> anyhow::Result<&mut std::collections::HashMap<String, serde_yaml::Value>> {
        Ok(self.base.outputs_mut())
    }

    /// Returns the default output names.
    ///
    /// # Returns
    ///
    /// A vector of default output names.
    fn default_outputs(&self) -> Vec<String> {
        vec![
            "user_uid".to_string(),
            "user_gid".to_string(),
            "user_name".to_string(),
            "user_group".to_string(),
        ]
    }

    /// Returns a reference to the options mapping.
    ///
    /// # Returns
    ///
    /// A reference to the `HashMap` of options.
    fn options(&self) -> &std::collections::HashMap<String, serde_yaml::Value> {
        self.base.options()
    }

    /// Returns a mutable reference to the options mapping.
    ///
    /// # Returns
    ///
    /// A mutable reference to the `HashMap` of options.
    fn options_mut(
        &mut self,
    ) -> anyhow::Result<&mut std::collections::HashMap<String, serde_yaml::Value>> {
        Ok(self.base.options_mut())
    }
}
use crate::meta_plugin::register_meta_plugin;

// Register the plugin at module initialization time
#[ctor::ctor]
fn register_user_plugin() {
    register_meta_plugin(MetaPluginType::User, |options, outputs| {
        Box::new(UserMetaPlugin::new(options, outputs))
    })
    .expect("Failed to register UserMetaPlugin");
}
