//! Feature management and discovery commands

use std::collections::HashMap;
use std::fs;
use std::path::Path;

use rustelo_core::{Result, RusteloError};

use crate::commands::update::{load_deps_config, RusteloDepsConfig};

/// List enabled and available features
pub async fn list(available_only: bool, crate_filter: Option<String>) -> Result<()> {
    let current_dir = std::env::current_dir().map_err(RusteloError::Io)?;
    let config = load_deps_config(&current_dir)?;

    if available_only {
        list_available_features(&config, crate_filter).await
    } else {
        list_enabled_features(&config, crate_filter.clone()).await?;
        println!();
        list_available_features(&config, crate_filter).await
    }
}

/// List currently enabled features
async fn list_enabled_features(
    config: &RusteloDepsConfig,
    crate_filter: Option<String>,
) -> Result<()> {
    println!("✅ **Currently Enabled Features**");

    let mut found_enabled = false;

    for (crate_name, features) in &config.features {
        if let Some(filter) = &crate_filter {
            if crate_name != filter {
                continue;
            }
        }

        if !features.is_empty() {
            found_enabled = true;
            println!("\n📦 {}:", crate_name);
            for feature in features {
                println!("  ✅ {}", feature);
            }
        }
    }

    if !found_enabled {
        println!("  ℹ️  No features currently enabled");
        if let Some(filter) = &crate_filter {
            println!("     (filtered by: {})", filter);
        }
    }

    Ok(())
}

/// List available but not enabled features
async fn list_available_features(
    config: &RusteloDepsConfig,
    crate_filter: Option<String>,
) -> Result<()> {
    println!("⚪ **Available Features (Not Yet Enabled)**");

    // Get available features from framework
    let available_features = get_framework_features().await?;

    let mut found_available = false;

    for (crate_name, features) in &available_features {
        if let Some(filter) = &crate_filter {
            if crate_name != filter {
                continue;
            }
        }

        // Filter out already enabled features
        let enabled_features = config.features.get(crate_name).cloned().unwrap_or_default();
        let available_only: Vec<String> = features
            .iter()
            .filter(|f| !enabled_features.contains(f))
            .cloned()
            .collect();

        if !available_only.is_empty() {
            found_available = true;
            println!("\n📦 {}:", crate_name);
            for feature in &available_only {
                let description = get_feature_description(crate_name, feature).await?;
                println!("  ⚪ {} - {}", feature, description);
            }
        }
    }

    if !found_available {
        println!("  ✨ All available features are already enabled!");
        if let Some(filter) = &crate_filter {
            println!("     (filtered by: {})", filter);
        }
    } else {
        println!("\n💡 Use `cargo rustelo features enable <crate> <feature>` to enable features");
    }

    Ok(())
}

/// Discover new available features
pub async fn discover() -> Result<()> {
    println!("🔍 Discovering new Rustelo features...");
    let current_dir = std::env::current_dir().map_err(RusteloError::Io)?;
    let _config = load_deps_config(&current_dir)?;

    // Get latest framework features
    let latest_features = discover_latest_features().await?;
    let available_features = get_framework_features().await?;

    // Compare with current available features
    let mut new_features = HashMap::new();

    for (crate_name, features) in &latest_features {
        let current_available = available_features
            .get(crate_name)
            .cloned()
            .unwrap_or_default();
        let new_for_crate: Vec<String> = features
            .iter()
            .filter(|f| !current_available.contains(f))
            .cloned()
            .collect();

        if !new_for_crate.is_empty() {
            new_features.insert(crate_name.clone(), new_for_crate);
        }
    }
    if new_features.is_empty() {
        println!("✅ No new features discovered. You're up to date!");
        return Ok(());
    }

    println!("🆕 **New Features Discovered**:");

    for (crate_name, features) in &new_features {
        println!("\n📦 {}:", crate_name);
        for feature in features {
            let description = get_feature_description(crate_name, feature).await?;
            println!("  🆕 {} - {}", feature, description);
        }
    }

    println!("\n💡 To enable new features:");
    println!("   cargo rustelo features enable <crate> <feature>");
    println!("   cargo rustelo update  # Apply changes");

    Ok(())
}

/// Get detailed information about a specific feature
pub async fn explain(feature_name: &str) -> Result<()> {
    println!("📖 **Feature Information: {}**", feature_name);

    // Parse feature name (format: "crate-feature" or just "feature")
    let (crate_name, feature) = if feature_name.contains('-') {
        let parts: Vec<&str> = feature_name.splitn(2, '-').collect();
        if parts.len() == 2 {
            (format!("rustelo-{}", parts[0]), parts[1].to_string())
        } else {
            return Err(RusteloError::generic(format!(
                "Invalid feature format: {}",
                feature_name
            )));
        }
    } else {
        // Try to find which crate contains this feature
        let framework_features = get_framework_features().await?;
        let mut found_crate = None;

        for (crate_name, features) in &framework_features {
            if features.contains(&feature_name.to_string()) {
                found_crate = Some(crate_name.clone());
                break;
            }
        }

        match found_crate {
            Some(crate_name) => (crate_name, feature_name.to_string()),
            None => {
                return Err(RusteloError::generic(format!(
                    "Feature '{}' not found",
                    feature_name
                )))
            }
        }
    };

    // Get feature details
    let feature_info = get_detailed_feature_info(&crate_name, &feature).await?;

    println!("\n**Crate**: {}", crate_name);
    println!("**Feature**: {}", feature);
    println!("**Description**: {}", feature_info.description);

    if !feature_info.dependencies.is_empty() {
        println!("**Dependencies**: {}", feature_info.dependencies.join(", "));
    }

    if !feature_info.enables.is_empty() {
        println!("**Enables**: {}", feature_info.enables.join(", "));
    }

    println!("**Documentation**: {}", feature_info.docs_url);

    if !feature_info.examples.is_empty() {
        println!("\n**Examples**:");
        for example in &feature_info.examples {
            println!("  • {}", example);
        }
    }

    println!("\n**Usage**:");
    println!(
        "  cargo rustelo features enable {} {}",
        crate_name.strip_prefix("rustelo-").unwrap_or(&crate_name),
        feature
    );

    Ok(())
}

/// Enable a feature
pub async fn enable(crate_name: &str, feature: &str) -> Result<()> {
    let current_dir = std::env::current_dir().map_err(RusteloError::Io)?;
    let full_crate_name = if crate_name.starts_with("rustelo-") {
        crate_name.to_string()
    } else {
        format!("rustelo-{}", crate_name)
    };

    println!(
        "✅ Enabling feature '{}' for crate '{}'",
        feature, full_crate_name
    );

    // Load current config
    let mut config = load_deps_config(&current_dir)?;

    // Check if feature is available
    let available_features = get_framework_features().await?;
    let crate_features = available_features
        .get(&full_crate_name)
        .ok_or_else(|| RusteloError::generic(format!("Crate '{}' not found", full_crate_name)))?;

    if !crate_features.contains(&feature.to_string()) {
        return Err(RusteloError::generic(format!(
            "Feature '{}' not available for crate '{}'",
            feature, full_crate_name
        )));
    }

    // Add feature to config
    let crate_enabled_features = config
        .features
        .entry(full_crate_name.clone())
        .or_insert_with(Vec::new);

    if crate_enabled_features.contains(&feature.to_string()) {
        println!("ℹ️  Feature '{}' is already enabled", feature);
        return Ok(());
    }

    crate_enabled_features.push(feature.to_string());

    // Write updated config
    save_deps_config(&current_dir, &config)?;

    println!("✅ Feature '{}' enabled for '{}'", feature, full_crate_name);
    println!("💡 Run 'cargo rustelo update' to apply changes");

    Ok(())
}

/// Disable a feature
pub async fn disable(crate_name: &str, feature: &str) -> Result<()> {
    let current_dir = std::env::current_dir().map_err(RusteloError::Io)?;
    let full_crate_name = if crate_name.starts_with("rustelo-") {
        crate_name.to_string()
    } else {
        format!("rustelo-{}", crate_name)
    };

    println!(
        "❌ Disabling feature '{}' for crate '{}'",
        feature, full_crate_name
    );

    // Load current config
    let mut config = load_deps_config(&current_dir)?;

    // Remove feature from config
    if let Some(crate_features) = config.features.get_mut(&full_crate_name) {
        let original_len = crate_features.len();
        crate_features.retain(|f| f != feature);

        if crate_features.len() == original_len {
            println!("ℹ️  Feature '{}' was not enabled", feature);
            return Ok(());
        }
    } else {
        println!("ℹ️  No features enabled for crate '{}'", full_crate_name);
        return Ok(());
    }

    // Write updated config
    save_deps_config(&current_dir, &config)?;

    println!(
        "❌ Feature '{}' disabled for '{}'",
        feature, full_crate_name
    );
    println!("💡 Run 'cargo rustelo update' to apply changes");

    Ok(())
}

// =============================================================================
// HELPER FUNCTIONS
// =============================================================================

/// Get framework features (simulated - in real implementation would query
/// framework)
async fn get_framework_features() -> Result<HashMap<String, Vec<String>>> {
    let mut features = HashMap::new();

    features.insert(
        "rustelo-core".to_string(),
        vec![
            "tracing".to_string(),
            "metrics".to_string(),
            "caching".to_string(),
            "async-runtime".to_string(),
        ],
    );

    features.insert(
        "rustelo-web".to_string(),
        vec![
            "ssr".to_string(),
            "islands".to_string(),
            "streaming".to_string(),
            "hydration".to_string(),
            "prerendering".to_string(),
        ],
    );

    features.insert(
        "rustelo-auth".to_string(),
        vec![
            "oauth2".to_string(),
            "jwt".to_string(),
            "session".to_string(),
            "2fa".to_string(),
            "social-login".to_string(),
        ],
    );

    features.insert(
        "rustelo-content".to_string(),
        vec![
            "markdown".to_string(),
            "search".to_string(),
            "cms".to_string(),
            "multilang".to_string(),
            "versioning".to_string(),
        ],
    );

    features.insert(
        "rustelo-analytics".to_string(),
        vec![
            "tracking".to_string(),
            "events".to_string(),
            "reporting".to_string(),
            "dashboards".to_string(),
        ],
    );

    features.insert(
        "rustelo-realtime".to_string(),
        vec![
            "websockets".to_string(),
            "sse".to_string(),
            "notifications".to_string(),
            "collaboration".to_string(),
        ],
    );

    Ok(features)
}

/// Discover latest features from framework repository
async fn discover_latest_features() -> Result<HashMap<String, Vec<String>>> {
    // In real implementation, this would query the framework repository
    // or package registry for the latest feature set
    let mut features = get_framework_features().await?;

    // Simulate discovering new features
    features
        .get_mut("rustelo-web")
        .unwrap()
        .push("pwa".to_string());
    features
        .get_mut("rustelo-auth")
        .unwrap()
        .push("passkeys".to_string());
    features.insert(
        "rustelo-ai".to_string(),
        vec![
            "llm-integration".to_string(),
            "embeddings".to_string(),
            "semantic-search".to_string(),
        ],
    );

    Ok(features)
}

/// Get feature description
async fn get_feature_description(crate_name: &str, feature: &str) -> Result<String> {
    // In real implementation, would fetch from framework documentation
    let description = match (crate_name, feature) {
        ("rustelo-core", "tracing") => "Distributed tracing and observability",
        ("rustelo-core", "metrics") => "Application metrics and monitoring",
        ("rustelo-core", "caching") => "In-memory and distributed caching",
        ("rustelo-web", "islands") => "Islands architecture for partial hydration",
        ("rustelo-web", "streaming") => "Streaming server-side rendering",
        ("rustelo-auth", "oauth2") => "OAuth 2.0 authentication provider support",
        ("rustelo-auth", "jwt") => "JSON Web Token authentication",
        ("rustelo-content", "markdown") => "Markdown processing and rendering",
        ("rustelo-content", "search") => "Full-text search capabilities",
        _ => "Feature description not available",
    };

    Ok(description.to_string())
}

/// Feature information struct
struct FeatureInfo {
    description: String,
    dependencies: Vec<String>,
    enables: Vec<String>,
    docs_url: String,
    examples: Vec<String>,
}

/// Get detailed feature information
async fn get_detailed_feature_info(crate_name: &str, feature: &str) -> Result<FeatureInfo> {
    // In real implementation, would fetch from framework documentation/metadata
    let info = match (crate_name, feature) {
        ("rustelo-auth", "oauth2") => FeatureInfo {
            description: "OAuth 2.0 authentication provider support with Google, GitHub, Discord, \
                          etc."
                .to_string(),
            dependencies: vec!["oauth2".to_string(), "reqwest".to_string()],
            enables: vec![
                "OAuth provider configuration".to_string(),
                "Token management".to_string(),
            ],
            docs_url: "https://docs.rustelo.dev/auth/oauth2".to_string(),
            examples: vec![
                "Google OAuth integration".to_string(),
                "GitHub login flow".to_string(),
                "Multi-provider setup".to_string(),
            ],
        },
        _ => FeatureInfo {
            description: get_feature_description(crate_name, feature).await?,
            dependencies: Vec::new(),
            enables: Vec::new(),
            docs_url: format!(
                "https://docs.rustelo.dev/{}",
                crate_name.strip_prefix("rustelo-").unwrap_or(crate_name)
            ),
            examples: Vec::new(),
        },
    };

    Ok(info)
}

/// Save updated dependencies configuration
fn save_deps_config(project_dir: &Path, config: &RusteloDepsConfig) -> Result<()> {
    let config_path = project_dir.join("rustelo-deps.toml");
    let toml_content = toml::to_string_pretty(config)
        .map_err(|e| RusteloError::generic(format!("Failed to serialize config: {}", e)))?;

    fs::write(&config_path, toml_content).map_err(RusteloError::Io)?;

    Ok(())
}
