use super::{BaseMetaPlugin, MetaPlugin, MetaPluginType, process_metadata_outputs};

#[derive(Debug, Clone)]
/// Meta plugin that extracts environment variables prefixed with KEEP_META_ as metadata.
pub struct EnvMetaPlugin {
    is_finalized: bool,
    base: BaseMetaPlugin,
    env_vars: Vec<(String, String)>,
}

impl EnvMetaPlugin {
    /// Creates a new `EnvMetaPlugin` instance.
    ///
    /// Collects environment variables starting with KEEP_META_ and sets up default output mappings.
    ///
    /// # Arguments
    ///
    /// * `_options` - Optional configuration options for the plugin (unused in this implementation).
    /// * `outputs` - Optional output mappings for metadata (overrides defaults).
    ///
    /// # Returns
    ///
    /// A new instance of `EnvMetaPlugin`.
    pub fn new(
        options: Option<std::collections::HashMap<String, serde_yaml::Value>>,
        outputs: Option<std::collections::HashMap<String, serde_yaml::Value>>,
    ) -> Self {
        let mut env_vars = Vec::new();
        let mut outputs_map = std::collections::HashMap::new();

        // Use options from --meta-plugin JSON if provided and non-empty,
        // otherwise fall back to KEEP_META_* environment variables.
        let use_options = options.as_ref().map(|o| !o.is_empty()).unwrap_or(false);

        if use_options {
            let opts = options.as_ref().unwrap();
            for (key, value) in opts {
                let value_str = match value {
                    serde_yaml::Value::String(s) => s.clone(),
                    serde_yaml::Value::Number(n) => n.to_string(),
                    serde_yaml::Value::Bool(b) => b.to_string(),
                    _ => serde_yaml::to_string(value).unwrap_or_default(),
                };
                env_vars.push((key.clone(), value_str));
                outputs_map.insert(key.clone(), serde_yaml::Value::String(key.clone()));
            }
        } else {
            // Fall back to KEEP_META_* environment variables
            for (key, value) in std::env::vars() {
                if let Some(stripped_key) = key.strip_prefix("KEEP_META_") {
                    env_vars.push((stripped_key.to_string(), value));
                    outputs_map.insert(
                        stripped_key.to_string(),
                        serde_yaml::Value::String(stripped_key.to_string()),
                    );
                }
            }
        }

        // Override with provided outputs
        if let Some(provided_outputs) = outputs {
            for (key, value) in provided_outputs {
                outputs_map.insert(key, value);
            }
        }

        let mut base = BaseMetaPlugin::new();
        base.outputs = outputs_map;

        EnvMetaPlugin {
            is_finalized: false,
            base,
            env_vars,
        }
    }
}

impl MetaPlugin for EnvMetaPlugin {
    /// Returns the type of this meta plugin.
    ///
    /// # Returns
    ///
    /// `MetaPluginType::Env`.
    fn meta_type(&self) -> MetaPluginType {
        MetaPluginType::Env
    }

    /// Checks if the plugin has been finalized.
    ///
    /// # Returns
    ///
    /// `true` if finalized, `false` otherwise.
    fn is_finalized(&self) -> bool {
        self.is_finalized
    }

    /// Sets the finalized state of the plugin.
    ///
    /// # Arguments
    ///
    /// * `finalized` - The new finalized state.
    fn set_finalized(&mut self, finalized: bool) {
        self.is_finalized = finalized;
    }

    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);
    }

    /// Initializes the plugin, processing environment variables.
    ///
    /// Processes all KEEP_META_* variables and generates metadata using output mappings.
    ///
    /// # Returns
    ///
    /// A `MetaPluginResponse` with environment metadata and finalized state set to `true`.
    fn initialize(&mut self) -> crate::meta_plugin::MetaPluginResponse {
        // If already finalized, don't process again
        if self.is_finalized {
            return crate::meta_plugin::MetaPluginResponse {
                metadata: Vec::new(),
                is_finalized: true,
            };
        }

        // Process all collected environment variables
        let mut metadata = Vec::new();
        for (name, value) in &self.env_vars {
            if let Some(meta_data) = process_metadata_outputs(
                name,
                serde_yaml::Value::String(value.clone()),
                self.base.outputs(),
            ) {
                metadata.push(meta_data);
            }
        }

        // Mark as finalized since this plugin only needs to run once
        self.is_finalized = true;

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

    /// Updates the plugin with new data (unused in this implementation).
    ///
    /// This plugin does not process streaming data; returns empty response.
    ///
    /// # Arguments
    ///
    /// * `_data` - The data chunk (unused).
    ///
    /// # Returns
    ///
    /// A `MetaPluginResponse` with empty metadata and current finalized state.
    fn update(&mut self, _data: &[u8]) -> crate::meta_plugin::MetaPluginResponse {
        // If already finalized, don't process more data
        if self.is_finalized {
            return crate::meta_plugin::MetaPluginResponse {
                metadata: Vec::new(),
                is_finalized: true,
            };
        }

        crate::meta_plugin::MetaPluginResponse {
            metadata: Vec::new(),
            is_finalized: false,
        }
    }

    /// Finalizes the plugin, calling initialize if not already done.
    ///
    /// Ensures environment metadata is processed if not previously initialized.
    ///
    /// # Returns
    ///
    /// A `MetaPluginResponse` with environment metadata if not finalized, or empty if already done.
    fn finalize(&mut self) -> crate::meta_plugin::MetaPluginResponse {
        // If not already finalized, we can call initialize
        if !self.is_finalized {
            return self.initialize();
        }

        crate::meta_plugin::MetaPluginResponse {
            metadata: Vec::new(),
            is_finalized: true,
        }
    }

    /// 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 based on collected env vars.
    ///
    /// # Returns
    ///
    /// A vector of environment variable names (stripped of KEEP_META_ prefix).
    fn default_outputs(&self) -> Vec<String> {
        self.env_vars.iter().map(|(name, _)| name.clone()).collect()
    }

    /// Returns a reference to the options mapping (empty for this plugin).
    ///
    /// This plugin has no configurable options.
    ///
    /// # Returns
    ///
    /// An empty `HashMap`.
    fn options(&self) -> &std::collections::HashMap<String, serde_yaml::Value> {
        self.base.options()
    }

    /// Returns a mutable reference to the options mapping.
    ///
    /// # Panics
    ///
    /// Panics with "options_mut() not implemented for EnvMetaPlugin".
    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;

/// Registers the EnvMetaPlugin with the global registry at module initialization.
#[ctor::ctor]
fn register_env_plugin() {
    register_meta_plugin(MetaPluginType::Env, |options, outputs| {
        Box::new(EnvMetaPlugin::new(options, outputs))
    })
    .expect("Failed to register EnvMetaPlugin");
}
