use anyhow::Result;
use clap::Command;
use std::collections::HashMap;
use strum::IntoEnumIterator;

use crate::common::schema::{gather_filter_plugin_schemas, gather_meta_plugin_schemas};
use crate::compression_engine::CompressionType;
use crate::config;

/// Generates and prints a default commented YAML configuration template.
///
/// Discovers all registered meta plugins, filter plugins, and compression engines
/// at runtime via the plugin schema system. Outputs a commented YAML template
/// with all available plugins and their default options/outputs.
///
/// # Arguments
///
/// * `_cmd` - Unused Clap command reference.
/// * `_settings` - Unused settings reference.
///
/// # Returns
///
/// `Ok(())` on success.
pub fn mode_generate_config(_cmd: &mut Command, _settings: &crate::config::Settings) -> Result<()> {
    let meta_schemas = gather_meta_plugin_schemas();
    let filter_schemas = gather_filter_plugin_schemas();

    // Build list_format defaults matching config.rs
    let list_format = default_list_format();

    // Build meta_plugins with env as the default (active), rest commented
    let meta_plugins = build_meta_plugins_section(&meta_schemas);

    // Build the full YAML
    let mut lines = Vec::with_capacity(128);

    lines.push("# Keep configuration file".to_string());
    lines.push("# Uncomment and modify the settings you need.".to_string());
    lines.push(String::new());

    // Core settings
    lines.push("# Data directory for storing items".to_string());
    lines.push("dir: ~/.local/share/keep".to_string());
    lines.push(String::new());

    // List format
    lines.push("# Column configuration for --list output".to_string());
    lines.push("list_format:".to_string());
    for col in &list_format {
        lines.push(format!("  - name: {}", col.name));
        lines.push(format!("    label: {}", col.label));
        lines.push(format!("    align: {}", col.align));
    }
    lines.push(String::new());

    // Table config
    lines.push("# Table display configuration".to_string());
    lines.push("#table_config:".to_string());
    lines.push("#  style: nothing".to_string());
    lines.push("#  modifiers: []".to_string());
    lines.push("#  content_arrangement: dynamic".to_string());
    lines.push("#  truncination_indicator: \"\"".to_string());
    lines.push(String::new());

    // Other settings
    lines.push("human_readable: false".to_string());
    lines.push("output_format: table".to_string());
    lines.push("quiet: false".to_string());
    lines.push("force: false".to_string());
    lines.push(String::new());

    // Server config
    lines.push("# Server configuration (only used with --server)".to_string());
    lines.push("server:".to_string());
    lines.push("  address: 127.0.0.1".to_string());
    lines.push("  port: 8080".to_string());
    lines.push("#  username: keep".to_string());
    lines.push("#  password: null".to_string());
    lines.push("#  password_file: null".to_string());
    lines.push("#  password_hash: null".to_string());
    lines.push("#  jwt_secret: null".to_string());
    lines.push("#  jwt_secret_file: null".to_string());
    lines.push("#  cert_file: null".to_string());
    lines.push("#  key_file: null".to_string());
    lines.push("#  cors_origin: null".to_string());
    lines.push(String::new());

    // Compression plugin
    lines.push("# Compression plugin to use".to_string());
    lines.push("#compression_plugin:".to_string());
    let mut comp_types: Vec<String> = CompressionType::iter().map(|ct| ct.to_string()).collect();
    comp_types.sort();
    for ct in &comp_types {
        lines.push(format!("#  name: {ct}  # {}", compression_description(ct)));
    }
    lines.push(String::new());

    // Meta plugins
    lines.push("# Meta plugins to run when saving items".to_string());
    lines.push("meta_plugins:".to_string());
    for line in &meta_plugins {
        lines.push(line.clone());
    }
    lines.push(String::new());

    // Filter plugins reference
    if !filter_schemas.is_empty() {
        lines.push("# Available filter plugins (use with --filter)".to_string());
        for schema in &filter_schemas {
            lines.push(format!("#   {}", schema.name));
            if !schema.description.is_empty() {
                lines.push(format!("#     {}", schema.description));
            }
            for opt in &schema.options {
                let req = if opt.required { "required" } else { "optional" };
                lines.push(format!(
                    "#       {} ({:?}, {})",
                    opt.name, opt.option_type, req
                ));
            }
        }
        lines.push(String::new());
    }

    // Client config
    lines.push("# Client configuration (requires client feature)".to_string());
    lines.push("#client:".to_string());
    lines.push("#  url: null".to_string());
    lines.push("#  username: null".to_string());
    lines.push("#  password: null".to_string());
    lines.push("#  jwt: null".to_string());

    // Print
    for line in &lines {
        println!("{line}");
    }

    Ok(())
}

struct ListColumn {
    name: String,
    label: String,
    align: String,
}

fn default_list_format() -> Vec<ListColumn> {
    vec![
        ListColumn {
            name: "id".into(),
            label: "Item".into(),
            align: "right".into(),
        },
        ListColumn {
            name: "time".into(),
            label: "Time".into(),
            align: "right".into(),
        },
        ListColumn {
            name: "size".into(),
            label: "Size".into(),
            align: "right".into(),
        },
        ListColumn {
            name: "meta:text_line_count".into(),
            label: "Lines".into(),
            align: "right".into(),
        },
        ListColumn {
            name: "tags".into(),
            label: "Tags".into(),
            align: "left".into(),
        },
        ListColumn {
            name: "meta:hostname_short".into(),
            label: "Host".into(),
            align: "left".into(),
        },
        ListColumn {
            name: "meta:command".into(),
            label: "Command".into(),
            align: "left".into(),
        },
    ]
}

fn build_meta_plugins_section(schemas: &[crate::common::schema::PluginSchema]) -> Vec<String> {
    let mut lines = Vec::new();

    for (i, schema) in schemas.iter().enumerate() {
        let is_default = schema.name == "env";
        let prefix = if is_default { "" } else { "# " };

        if i > 0 {
            lines.push(format!("{prefix}# --- {name} ---", name = schema.name));
        }

        lines.push(format!("{prefix}- name: {}", schema.name));

        // Options
        if !schema.options.is_empty() {
            lines.push(format!("{prefix}  options:"));
            for opt in &schema.options {
                if let Some(ref default) = opt.default {
                    let default_str = format_yaml_value(default);
                    lines.push(format!("{prefix}    {}: {}", opt.name, default_str));
                } else if opt.required {
                    lines.push(format!("{prefix}    {}: null  # required", opt.name));
                }
            }
        } else {
            lines.push(format!("{prefix}  options: {{}}"));
        }

        // Outputs
        if !schema.outputs.is_empty() {
            lines.push(format!("{prefix}  outputs:"));
            for output in &schema.outputs {
                lines.push(format!("{prefix}    {}: {}", output.name, output.name));
            }
        } else {
            lines.push(format!("{prefix}  outputs: {{}}"));
        }
    }

    lines
}

fn format_yaml_value(value: &serde_yaml::Value) -> String {
    match value {
        serde_yaml::Value::Null => "null".into(),
        serde_yaml::Value::Bool(b) => b.to_string(),
        serde_yaml::Value::Number(n) => n.to_string(),
        serde_yaml::Value::String(s) => {
            if s.contains(' ') || s.contains(':') || s.contains('#') {
                format!("\"{s}\"")
            } else {
                s.clone()
            }
        }
        serde_yaml::Value::Sequence(_) | serde_yaml::Value::Mapping(_) => {
            serde_yaml::to_string(value)
                .unwrap_or_default()
                .trim()
                .to_string()
        }
        serde_yaml::Value::Tagged(_) => serde_yaml::to_string(value)
            .unwrap_or_default()
            .trim()
            .to_string(),
    }
}

fn compression_description(name: &str) -> &str {
    match name {
        "lz4" => "Fast compression (native)",
        "gzip" => "Good compression ratio (native)",
        "bzip2" => "High compression (requires bzip2 binary)",
        "xz" => "Very high compression (requires xz binary)",
        "zstd" => "Modern fast compression (requires zstd binary)",
        "none" => "No compression",
        _ => "",
    }
}
