use clap::*;
use log::debug;
use std::path::PathBuf;
use std::str::FromStr;

use crate::common::status::StatusInfo;
use crate::config;
use crate::modes::common::OutputFormat;
use comfy_table::{Attribute, Cell, Table};
use serde_json;
use serde_yaml;

use crate::meta_plugin::MetaPluginType;
use crate::meta_plugin::get_meta_plugin;

fn build_config_table(settings: &config::Settings) -> Table {
    let mut config_table = crate::modes::common::create_table_with_config(&settings.table_config);

    config_table.set_header(vec![
        Cell::new("Setting").add_attribute(Attribute::Bold),
        Cell::new("Value").add_attribute(Attribute::Bold),
    ]);

    // Add relevant configuration settings
    config_table.add_row(vec!["Directory", &settings.dir.to_string_lossy()]);
    config_table.add_row(vec!["Human Readable", &settings.human_readable.to_string()]);
    config_table.add_row(vec!["Quiet", &settings.quiet.to_string()]);

    if let Some(output_format) = &settings.output_format {
        config_table.add_row(vec!["Output Format", output_format]);
    }

    if let Some(compression) = settings.compression() {
        config_table.add_row(vec!["Compression", &compression]);
    }

    config_table
}

fn build_meta_plugins_configured_table(
    status_info: &StatusInfo,
    table_config: &config::TableConfig,
) -> Option<Table> {
    let meta_plugins = status_info.configured_meta_plugins.as_ref()?;
    if meta_plugins.is_empty() {
        return None;
    }

    // Sort meta plugins by name
    let mut sorted_meta_plugins = meta_plugins.clone();
    sorted_meta_plugins.sort_by(|a, b| a.name.cmp(&b.name));

    let mut table = crate::modes::common::create_table_with_config(table_config);

    table.set_header(vec![
        Cell::new("Plugin Name").add_attribute(Attribute::Bold),
        Cell::new("Options").add_attribute(Attribute::Bold),
        Cell::new("Outputs").add_attribute(Attribute::Bold),
    ]);

    for plugin_config in sorted_meta_plugins {
        // Create the plugin to get its default options
        let meta_plugin_type = match MetaPluginType::from_str(&plugin_config.name) {
            Ok(plugin_type) => plugin_type,
            Err(_) => continue,
        };

        // First, create a default plugin to get its default options
        let Ok(default_plugin) = get_meta_plugin(meta_plugin_type.clone(), None, None) else {
            continue;
        };

        // Start with the default options
        let mut effective_options = default_plugin.options().clone();

        // Merge with the configured options
        for (key, value) in &plugin_config.options {
            effective_options.insert(key.clone(), value.clone());
        }

        // Convert outputs from HashMap<String, String> to HashMap<String, serde_yaml::Value>
        let outputs_converted: std::collections::HashMap<String, serde_yaml::Value> = plugin_config
            .outputs
            .iter()
            .map(|(k, v)| (k.clone(), serde_yaml::Value::String(v.clone())))
            .collect();

        // Create the actual plugin with merged options - the constructor will handle setting up outputs
        let Ok(actual_plugin) = get_meta_plugin(
            meta_plugin_type.clone(),
            Some(effective_options.clone()),
            Some(outputs_converted),
        ) else {
            continue;
        };

        // Get the default plugin to see its default options
        let Ok(default_plugin) = get_meta_plugin(meta_plugin_type.clone(), None, None) else {
            continue;
        };

        // Start with the default options
        let mut all_options = default_plugin.options().clone();
        // Merge with the configured options
        for (key, value) in &effective_options {
            all_options.insert(key.clone(), value.clone());
        }

        // Sort options by key and convert to a YAML string
        let mut sorted_options: Vec<_> = all_options.iter().collect();
        sorted_options.sort_by(|a, b| a.0.cmp(b.0));
        let sorted_options_map: std::collections::BTreeMap<_, _> =
            sorted_options.into_iter().collect();

        let options_str = if sorted_options_map.is_empty() {
            "{}".to_string()
        } else {
            serde_yaml::to_string(&sorted_options_map)
                .unwrap_or_else(|_| "Unable to serialize options".to_string())
                .trim()
                .to_string()
        };

        // Show only non-null outputs from the plugin
        // Collect and sort outputs by their string representation
        let mut enabled_output_pairs = Vec::new();
        for (key, value) in actual_plugin.outputs() {
            // Skip null values (disabled outputs)
            if value.is_null() {
                continue;
            }

            // Convert serde_yaml::Value to a string representation
            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::Value::Null => "null".to_string(),
                serde_yaml::Value::Sequence(_) => {
                    serde_yaml::to_string(value).unwrap_or_else(|_| "[]".to_string())
                }
                serde_yaml::Value::Mapping(_) => {
                    serde_yaml::to_string(value).unwrap_or_else(|_| "{}".to_string())
                }
                serde_yaml::Value::Tagged(_) => {
                    serde_yaml::to_string(value).unwrap_or_else(|_| "tagged".to_string())
                }
            };
            // Trim any extra whitespace from the serialized values
            let value_str = value_str.trim().to_string();
            if key == &value_str {
                enabled_output_pairs.push((key.clone(), key.clone()));
            } else {
                enabled_output_pairs.push((key.clone(), format!("{key}->{value_str}")));
            }
        }

        // Sort outputs by their display value (second element of the tuple)
        enabled_output_pairs.sort_by(|a, b| a.1.cmp(&b.1));

        // Join each output on a new line
        let outputs_str = if enabled_output_pairs.is_empty() {
            "{}".to_string()
        } else {
            enabled_output_pairs
                .into_iter()
                .map(|(_, display)| display)
                .collect::<Vec<_>>()
                .join("\n")
        };

        table.add_row(vec![plugin_config.name.clone(), options_str, outputs_str]);
    }

    Some(table)
}

pub fn mode_status(
    cmd: &mut Command,
    settings: &config::Settings,
    data_path: PathBuf,
    db_path: PathBuf,
) -> Result<(), anyhow::Error> {
    debug!("STATUS: Starting mode_status function");

    let status_service = crate::services::status_service::StatusService::new();
    let output_format = crate::modes::common::settings_output_format(settings);
    debug!("STATUS: About to generate status info");
    let status_info = status_service.generate_status(cmd, settings, data_path, db_path)?;
    debug!("STATUS: Status info generated successfully");

    match output_format {
        OutputFormat::Table => {
            println!("CONFIG:");
            let config_table = build_config_table(settings);
            println!(
                "{}",
                crate::modes::common::trim_lines_end(&config_table.trim_fmt())
            );
            println!();

            println!("PATHS:");
            let path_table =
                crate::modes::common::build_path_table(&status_info.paths, &settings.table_config);
            println!(
                "{}",
                crate::modes::common::trim_lines_end(&path_table.trim_fmt())
            );
            println!();

            // Always try to print META PLUGINS CONFIGURED section using status_info
            if let Some(meta_plugins_table) =
                build_meta_plugins_configured_table(&status_info, &settings.table_config)
            {
                println!("META PLUGINS CONFIGURED:");
                println!(
                    "{}",
                    crate::modes::common::trim_lines_end(&meta_plugins_table.trim_fmt())
                );
                println!();
            } else {
                println!("META PLUGINS CONFIGURED:");
                println!("No plugins configured");
                println!();
            }
            Ok(())
        }
        OutputFormat::Json => {
            crate::modes::common::print_serialized(&status_info, &output_format)?;
            Ok(())
        }
        OutputFormat::Yaml => {
            crate::modes::common::print_serialized(&status_info, &output_format)?;
            Ok(())
        }
    }
}
