use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use strum::IntoEnumIterator;

/// Value type for a plugin option.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum OptionType {
    String,
    Integer,
    Boolean,
    Any,
}

impl OptionType {
    /// Infer the option type from a YAML value.
    pub fn from_yaml_value(value: &serde_yaml::Value) -> Self {
        match value {
            serde_yaml::Value::Bool(_) => OptionType::Boolean,
            serde_yaml::Value::Number(_) => OptionType::Integer,
            serde_yaml::Value::String(_) => OptionType::String,
            _ => OptionType::Any,
        }
    }
}

/// Schema for a single plugin option.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OptionSchema {
    pub name: String,
    pub option_type: OptionType,
    pub default: Option<serde_yaml::Value>,
    pub required: bool,
}

/// Schema for a single plugin output.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OutputSchema {
    pub name: String,
    pub description: String,
}

/// Schema describing a plugin's configuration requirements.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PluginSchema {
    pub name: String,
    pub description: String,
    pub options: Vec<OptionSchema>,
    pub outputs: Vec<OutputSchema>,
}

/// Gathers schemas from all registered meta plugins.
///
/// Iterates all `MetaPluginType` variants, attempts to create a default instance,
/// and collects their schemas. Plugins that fail to register (e.g., feature-gated)
/// are silently skipped.
pub fn gather_meta_plugin_schemas() -> Vec<PluginSchema> {
    use crate::meta_plugin::{MetaPluginType, get_meta_plugin};

    let mut schemas = Vec::new();
    let mut sorted_types: Vec<MetaPluginType> = MetaPluginType::iter().collect();
    sorted_types.sort_by_key(|t| t.to_string());

    for plugin_type in sorted_types {
        let plugin = match get_meta_plugin(plugin_type.clone(), None, None) {
            Ok(p) => p,
            Err(_) => continue,
        };

        let name = plugin.meta_type().to_string();

        let options: Vec<OptionSchema> = plugin
            .options()
            .iter()
            .map(|(key, value)| {
                let option_type = OptionType::from_yaml_value(value);
                let (default, required) = if value.is_null() {
                    (None, true)
                } else {
                    (Some(value.clone()), false)
                };
                OptionSchema {
                    name: key.clone(),
                    option_type,
                    default,
                    required,
                }
            })
            .collect();

        let mut outputs: Vec<OutputSchema> = Vec::new();
        for (key, value) in plugin.outputs() {
            if !value.is_null() {
                outputs.push(OutputSchema {
                    name: key.clone(),
                    description: key.clone(),
                });
            }
        }
        // Also include default outputs if outputs map is empty
        if outputs.is_empty() {
            for output_name in plugin.default_outputs() {
                outputs.push(OutputSchema {
                    name: output_name.clone(),
                    description: output_name,
                });
            }
        }

        schemas.push(PluginSchema {
            name,
            description: plugin.description().to_string(),
            options,
            outputs,
        });
    }

    schemas
}

/// Gathers schemas from all registered filter plugins.
///
/// Uses the global filter plugin registry to discover all registered filters,
/// creates a default instance of each, and collects their option schemas.
pub fn gather_filter_plugin_schemas() -> Vec<PluginSchema> {
    use crate::services::filter_service::get_available_filter_plugins;

    let plugins = get_available_filter_plugins().unwrap_or_default();
    let mut schemas: Vec<PluginSchema> = plugins
        .into_iter()
        .map(|(name, creator)| {
            let plugin = creator();
            let options: Vec<OptionSchema> = plugin
                .options()
                .iter()
                .map(|opt| {
                    let option_type = match &opt.default {
                        Some(serde_json::Value::Bool(_)) => OptionType::Boolean,
                        Some(serde_json::Value::Number(_)) => OptionType::Integer,
                        Some(serde_json::Value::String(_)) => OptionType::String,
                        _ => OptionType::Any,
                    };
                    OptionSchema {
                        name: opt.name.clone(),
                        option_type,
                        default: opt.default.as_ref().map(|v| {
                            // Convert serde_json::Value to serde_yaml::Value
                            serde_yaml::to_value(v).unwrap_or(serde_yaml::Value::Null)
                        }),
                        required: opt.required,
                    }
                })
                .collect();

            PluginSchema {
                name: name.clone(),
                description: plugin.description().to_string(),
                options,
                outputs: Vec::new(),
            }
        })
        .collect();

    schemas.sort_by(|a, b| a.name.cmp(&b.name));
    schemas
}
