//! Asset management command implementations

use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};

use rustelo_core::{
    assets::{AssetCategory, AssetResolver},
    config::RusteloConfig,
    Result, RusteloError,
};

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

/// List asset resolution paths
pub async fn list(categories: Option<Vec<String>>, show_stats: bool) -> Result<()> {
    tracing::info!("Listing asset resolution paths");

    let config = RusteloConfig::load().unwrap_or_default();
    let resolver = AssetResolver::new(config);

    println!("📋 Asset Resolution Priority:");
    for (i, source) in resolver
        .config
        .framework
        .asset_resolution
        .priority
        .iter()
        .enumerate()
    {
        let path = match source {
            rustelo_core::config::AssetSource::Local => &resolver.config.paths.local_assets,
            rustelo_core::config::AssetSource::Framework => &resolver.config.paths.framework_assets,
            rustelo_core::config::AssetSource::Generated => &resolver.config.paths.output,
        };
        println!("  {}. {:?}: {}", i + 1, source, path.display());
    }

    if show_stats {
        // Show category statistics
        println!("\n📊 Asset Statistics by Category:");
        let stats = resolver.get_category_stats("")?;

        let mut total_assets = 0;
        for category in AssetCategory::all() {
            let count = stats.get(&category).unwrap_or(&0);
            if *count > 0 {
                println!("  {:?}: {} files", category, count);
                total_assets += count;
            }
        }
        println!("  Total: {} assets", total_assets);
        return Ok(());
    }

    // Parse category filter if provided
    let filter_categories = if let Some(category_names) = categories {
        let mut asset_categories = Vec::new();
        for name in category_names {
            match name.to_lowercase().as_str() {
                "build" => asset_categories.push(AssetCategory::Build),
                "config" => asset_categories.push(AssetCategory::Config),
                "static" => asset_categories.push(AssetCategory::Static),
                "content" => asset_categories.push(AssetCategory::Content),
                "development" | "dev" => asset_categories.push(AssetCategory::Development),
                "deployment" | "deploy" => asset_categories.push(AssetCategory::Deployment),
                _ => {
                    println!("⚠️  Unknown category: {}", name);
                    continue;
                }
            }
        }
        Some(asset_categories)
    } else {
        None
    };

    // List assets (filtered or all)
    let assets = if let Some(cats) = &filter_categories {
        println!("\n📁 Assets (filtered by categories: {:?}):", cats);
        resolver.list_assets_by_category("", cats)?
    } else {
        println!("\n📁 Available Assets:");
        resolver.list_assets("")?
    };

    let mut by_category = HashMap::new();
    for asset in assets {
        by_category
            .entry(asset.category.clone())
            .or_insert(Vec::new())
            .push(asset);
    }

    for category in AssetCategory::all() {
        if let Some(files) = by_category.get(&category) {
            if !files.is_empty() {
                println!("\n  📂 {:?} ({} files):", category, files.len());
                for file in files {
                    println!("    {} ({:?})", file.path, file.source);
                }
            }
        }
    }

    Ok(())
}

/// Check for asset conflicts
pub async fn check(categories: Option<Vec<String>>) -> Result<()> {
    tracing::info!("Checking for asset conflicts");

    let config = RusteloConfig::load().unwrap_or_default();
    let resolver = AssetResolver::new(config);

    println!("🔍 Checking asset conflicts...");

    // Parse category filter if provided
    let filter_categories = if let Some(category_names) = categories {
        let mut asset_categories = Vec::new();
        for name in category_names {
            match name.to_lowercase().as_str() {
                "build" => asset_categories.push(AssetCategory::Build),
                "config" => asset_categories.push(AssetCategory::Config),
                "static" => asset_categories.push(AssetCategory::Static),
                "content" => asset_categories.push(AssetCategory::Content),
                "development" | "dev" => asset_categories.push(AssetCategory::Development),
                "deployment" | "deploy" => asset_categories.push(AssetCategory::Deployment),
                _ => {
                    println!("⚠️  Unknown category: {}", name);
                    continue;
                }
            }
        }
        println!("📁 Checking categories: {:?}", asset_categories);
        Some(asset_categories)
    } else {
        None
    };

    let mut conflicts = Vec::new();
    let mut overrides = Vec::new();
    let mut _missing: Vec<String> = Vec::new();

    // Check each asset source
    for source in &resolver.config.framework.asset_resolution.priority {
        let mut assets = resolver.list_assets_from_source("", source)?;

        // Filter by categories if specified
        if let Some(cats) = &filter_categories {
            assets.retain(|asset| cats.contains(&asset.category));
        }

        for asset in assets {
            // Check if this asset exists in other sources
            let mut found_in = Vec::new();

            for other_source in &resolver.config.framework.asset_resolution.priority {
                if other_source == source {
                    continue;
                }

                if let Ok(Some(_)) = resolver.try_resolve_from_source(&asset.path, other_source) {
                    found_in.push(other_source);
                }
            }

            if !found_in.is_empty() {
                if matches!(source, rustelo_core::config::AssetSource::Local) {
                    overrides.push((asset.path.clone(), source.clone(), found_in));
                } else {
                    conflicts.push((asset.path.clone(), source.clone(), found_in));
                }
            }
        }
    }

    // Report findings
    if !overrides.is_empty() {
        println!("\n🎯 Local Overrides (Local implementation overrides framework):");
        for (path, _source, overridden) in &overrides {
            println!("  {} overrides: {:?}", path, overridden);
        }
    }

    if !conflicts.is_empty() {
        println!("\n⚠️  Asset Conflicts:");
        for (path, source, conflicting) in &conflicts {
            println!(
                "  {} in {:?} conflicts with: {:?}",
                path, source, conflicting
            );
        }
    }

    if overrides.is_empty() && conflicts.is_empty() {
        println!("✅ No asset conflicts found");
    } else {
        println!(
            "\n📊 Summary: {} overrides, {} conflicts",
            overrides.len(),
            conflicts.len()
        );
    }

    Ok(())
}

/// Resolve a specific asset path
pub async fn resolve(path: &str) -> Result<()> {
    tracing::info!("Resolving asset path: {}", path);

    let config = RusteloConfig::load().unwrap_or_default();
    let resolver = AssetResolver::new(config);

    println!("🔍 Resolving asset: {}", path);

    // Try to resolve the asset
    match resolver.resolve(path)? {
        Some(resolved_path) => {
            println!("✅ Found at: {}", resolved_path.display());

            // Determine which source provided this asset
            for source in &resolver.config.framework.asset_resolution.priority {
                if let Ok(Some(source_path)) = resolver.try_resolve_from_source(path, source) {
                    if source_path == resolved_path {
                        println!("📁 Source: {:?}", source);
                        break;
                    }
                }
            }

            // Show file type and suggested processor
            if let Some(processor) = resolver.processors.find_processor(&resolved_path) {
                let processor_name = if processor.handles_extensions().contains(&"sh") {
                    "script"
                } else if processor.handles_extensions().contains(&"json") {
                    "config"
                } else if processor.handles_extensions().contains(&"md") {
                    "content"
                } else {
                    "static"
                };

                println!(
                    "🔧 Processor: {} (can merge: {})",
                    processor_name,
                    processor.can_merge()
                );
            }

            // Check for alternatives in other sources
            println!("\n🔄 Alternative sources:");
            for source in &resolver.config.framework.asset_resolution.priority {
                match resolver.try_resolve_from_source(path, source) {
                    Ok(Some(alt_path)) => {
                        let status = if alt_path == resolved_path {
                            "active"
                        } else {
                            "override"
                        };
                        println!("  {:?}: {} ({})", source, alt_path.display(), status);
                    }
                    Ok(None) => {
                        println!("  {:?}: not found", source);
                    }
                    Err(_) => {}
                }
            }
        }
        None => {
            println!("❌ Asset not found");
            println!("💡 Consider creating it in one of these locations:");
            for source in &resolver.config.framework.asset_resolution.priority {
                let base_path = match source {
                    rustelo_core::config::AssetSource::Local => &resolver.config.paths.local_assets,
                    rustelo_core::config::AssetSource::Framework => {
                        &resolver.config.paths.framework_assets
                    }
                    rustelo_core::config::AssetSource::Generated => &resolver.config.paths.output,
                };
                println!("  {:?}: {}", source, base_path.join(path).display());
            }
        }
    }

    Ok(())
}

/// Organize assets by category
pub async fn organize(target: PathBuf, categories: Option<Vec<String>>) -> Result<()> {
    tracing::info!("Organizing assets by category to: {}", target.display());

    let config = RusteloConfig::load().unwrap_or_default();
    let resolver = AssetResolver::new(config);

    // Parse category filter if provided
    let filter_categories = if let Some(category_names) = categories {
        let mut asset_categories = Vec::new();
        for name in category_names {
            match name.to_lowercase().as_str() {
                "build" => asset_categories.push(AssetCategory::Build),
                "config" => asset_categories.push(AssetCategory::Config),
                "static" => asset_categories.push(AssetCategory::Static),
                "content" => asset_categories.push(AssetCategory::Content),
                "development" | "dev" => asset_categories.push(AssetCategory::Development),
                "deployment" | "deploy" => asset_categories.push(AssetCategory::Deployment),
                _ => {
                    println!("⚠️  Unknown category: {}", name);
                    continue;
                }
            }
        }
        println!("📁 Organizing categories: {:?}", asset_categories);
        asset_categories
    } else {
        AssetCategory::all()
    };

    // Set up organization variables
    let mut variables = HashMap::new();
    variables.insert("organized".to_string(), "true".to_string());
    variables.insert("timestamp".to_string(), chrono::Utc::now().to_rfc3339());

    println!("🗂️  Organizing assets into category-based structure...");

    let mut organized_files = HashMap::new();
    let mut total_files = 0;

    for category in &filter_categories {
        let category_assets =
            resolver.list_assets_by_category("", std::slice::from_ref(category))?;

        if !category_assets.is_empty() {
            let category_dir = target.join(category.default_directory());

            println!("  📂 {:?} -> {}/", category, category.default_directory());

            let mut processed_files = Vec::new();
            for asset in category_assets {
                let target_path = category_dir.join(&asset.path);

                match resolver
                    .process_with_fallback(&asset.path, &target_path, variables.clone())
                    .await
                {
                    Ok(result) => {
                        processed_files.push(result);
                        println!("    ✓ {}", asset.path);
                    }
                    Err(e) => {
                        println!("    ✗ {} ({})", asset.path, e);
                    }
                }
            }

            if !processed_files.is_empty() {
                total_files += processed_files.len();
                organized_files.insert(category.clone(), processed_files);
            }
        }
    }

    println!("\n✅ Asset organization completed:");
    for (category, files) in &organized_files {
        println!("  📁 {:?}: {} files", category, files.len());
    }
    println!("  Total: {} files organized", total_files);

    Ok(())
}

// =============================================================================
// LOCAL DEVELOPMENT ASSET MANAGEMENT
// =============================================================================

/// Asset synchronization result
#[derive(Debug)]
pub struct SyncResult {
    pub synced_files: Vec<String>,
    #[allow(dead_code)]
    pub skipped_files: Vec<String>,
    pub errors: Vec<String>,
}

/// Asset source abstraction for development workflow
pub trait AssetSource {
    fn sync_assets(&self, categories: &[String]) -> Result<SyncResult>;
    fn validate_source(&self) -> Result<bool>;
    fn list_available_assets(&self) -> Result<Vec<String>>;
}

/// Local asset source implementation
pub struct LocalAssetSource {
    framework_path: PathBuf,
    project_path: PathBuf,
}

impl LocalAssetSource {
    pub fn new(framework_path: PathBuf, project_path: PathBuf) -> Self {
        Self {
            framework_path,
            project_path,
        }
    }

    fn sync_category(&self, category: &str) -> Result<Vec<String>> {
        let mut synced_files = Vec::new();

        match category {
            "justfiles" => {
                let source_dir = self.framework_path.join("justfiles");
                let target_dir = self.project_path.join("framework-assets/justfiles");
                synced_files.extend(self.sync_directory(&source_dir, &target_dir)?);
            }
            "scripts" => {
                let source_dir = self.framework_path.join("scripts");
                let target_dir = self.project_path.join("framework-assets/scripts");
                synced_files.extend(self.sync_directory(&source_dir, &target_dir)?);
            }
            "content" => {
                let source_dir = self.framework_path.join("content");
                let target_dir = self.project_path.join("content-templates");
                synced_files.extend(self.sync_directory(&source_dir, &target_dir)?);
            }
            "docker" => {
                // Sync Docker-related files
                let files_to_sync = [
                    ("Dockerfile", "framework-assets/docker/Dockerfile"),
                    ("Dockerfile.dev", "framework-assets/docker/Dockerfile.dev"),
                    (
                        "assets/deploy/Dockerfile.cross",
                        "framework-assets/docker/Dockerfile.cross",
                    ),
                    (
                        "assets/configs/Cross.toml",
                        "framework-assets/docker/Cross.toml",
                    ),
                ];

                for (source_file, target_file) in &files_to_sync {
                    let source_path = self.framework_path.join(source_file);
                    let target_path = self.project_path.join(target_file);

                    if source_path.exists() {
                        if let Some(parent) = target_path.parent() {
                            fs::create_dir_all(parent).map_err(RusteloError::Io)?;
                        }
                        fs::copy(&source_path, &target_path).map_err(RusteloError::Io)?;
                        synced_files.push(target_file.to_string());
                    }
                }
            }
            "public" => {
                let source_dir = self.framework_path.join("public");
                let target_dir = self.project_path.join("public-templates");
                synced_files.extend(self.sync_directory(&source_dir, &target_dir)?);
            }
            _ => {
                tracing::warn!("Unknown asset category: {}", category);
            }
        }

        Ok(synced_files)
    }

    fn sync_directory(&self, source: &Path, target: &Path) -> Result<Vec<String>> {
        let mut synced_files = Vec::new();

        if !source.exists() {
            tracing::warn!("Source directory does not exist: {}", source.display());
            return Ok(synced_files);
        }

        // Create target directory
        fs::create_dir_all(target).map_err(RusteloError::Io)?;

        // Sync files recursively
        for entry in walkdir::WalkDir::new(source) {
            let entry = entry.map_err(|e| RusteloError::generic(format!("Walk error: {}", e)))?;

            if entry.file_type().is_file() {
                let source_file = entry.path();
                let relative_path = source_file
                    .strip_prefix(source)
                    .map_err(|e| RusteloError::generic(format!("Path error: {}", e)))?;
                let target_file = target.join(relative_path);

                // Create parent directories
                if let Some(parent) = target_file.parent() {
                    fs::create_dir_all(parent).map_err(RusteloError::Io)?;
                }

                // Copy file
                fs::copy(source_file, &target_file).map_err(RusteloError::Io)?;
                synced_files.push(relative_path.to_string_lossy().to_string());
            }
        }

        Ok(synced_files)
    }
}

impl AssetSource for LocalAssetSource {
    fn sync_assets(&self, categories: &[String]) -> Result<SyncResult> {
        let mut result = SyncResult {
            synced_files: Vec::new(),
            skipped_files: Vec::new(),
            errors: Vec::new(),
        };

        for category in categories {
            match self.sync_category(category) {
                Ok(mut files) => result.synced_files.append(&mut files),
                Err(e) => result
                    .errors
                    .push(format!("Error syncing {}: {}", category, e)),
            }
        }

        Ok(result)
    }

    fn validate_source(&self) -> Result<bool> {
        // Check if framework path exists and has expected structure
        if !self.framework_path.exists() {
            return Ok(false);
        }

        let required_paths = [
            "justfiles/build.just",
            "scripts/cross-build.sh",
            "crates/rustelo-core/Cargo.toml",
        ];

        for path in &required_paths {
            if !self.framework_path.join(path).exists() {
                return Ok(false);
            }
        }

        Ok(true)
    }

    fn list_available_assets(&self) -> Result<Vec<String>> {
        let mut assets = Vec::new();

        let asset_dirs = [
            ("justfiles", self.framework_path.join("justfiles")),
            ("scripts", self.framework_path.join("scripts")),
            ("content", self.framework_path.join("content")),
            ("public", self.framework_path.join("public")),
        ];

        for (category, dir) in &asset_dirs {
            if dir.exists() {
                assets.push(category.to_string());
            }
        }

        Ok(assets)
    }
}

/// Sync assets from configured source
pub async fn sync_framework_assets(
    categories: Option<Vec<String>>,
    _source_override: Option<String>,
) -> Result<()> {
    let current_dir = std::env::current_dir().map_err(RusteloError::Io)?;
    let config = load_assets_config(&current_dir)?;

    let asset_source = create_asset_source(&config, &current_dir)?;

    let categories_to_sync = categories.unwrap_or_else(|| {
        vec![
            "justfiles".to_string(),
            "scripts".to_string(),
            "content".to_string(),
            "docker".to_string(),
        ]
    });

    println!("🔄 Syncing assets from: {}", config.source);

    let result = asset_source.sync_assets(&categories_to_sync)?;

    if !result.synced_files.is_empty() {
        println!("✅ Synced {} files:", result.synced_files.len());
        for file in &result.synced_files {
            println!("  📄 {}", file);
        }
    }

    if !result.errors.is_empty() {
        println!("❌ Errors:");
        for error in &result.errors {
            println!("  {}", error);
        }
    }

    Ok(())
}

/// Validate configured asset source
pub async fn validate_framework_assets() -> Result<()> {
    let current_dir = std::env::current_dir().map_err(RusteloError::Io)?;
    let config = load_assets_config(&current_dir)?;

    let asset_source = create_asset_source(&config, &current_dir)?;

    println!("🔍 Validating asset source: {}", config.source);

    if asset_source.validate_source()? {
        println!("✅ Asset source is valid");
    } else {
        println!("❌ Asset source validation failed");
        return Err(RusteloError::generic("Asset source validation failed"));
    }

    Ok(())
}

/// List available framework assets
pub async fn list_framework_assets() -> Result<()> {
    let current_dir = std::env::current_dir().map_err(RusteloError::Io)?;
    let config = load_assets_config(&current_dir)?;

    let asset_source = create_asset_source(&config, &current_dir)?;

    let assets = asset_source.list_available_assets()?;

    println!("📦 Available asset categories:");
    for asset in &assets {
        println!("  🗂️  {}", asset);
    }

    Ok(())
}

/// Load assets configuration from rustelo-deps.toml
fn load_assets_config(project_dir: &Path) -> Result<AssetsConfig> {
    let config_path = project_dir.join("rustelo-deps.toml");

    if !config_path.exists() {
        // Default configuration if no config file
        return Ok(AssetsConfig {
            source: "embedded".to_string(),
            local: None,
            download: None,
            embedded: Some(crate::commands::update::EmbeddedAssetsConfig {
                fallback_enabled: true,
            }),
        });
    }

    let content = fs::read_to_string(&config_path).map_err(RusteloError::Io)?;
    let full_config: RusteloDepsConfig = toml::from_str(&content)
        .map_err(|e| RusteloError::generic(format!("Invalid rustelo-deps.toml: {}", e)))?;

    full_config
        .assets
        .ok_or_else(|| RusteloError::generic("No [assets] section in rustelo-deps.toml"))
}

/// Create appropriate asset source based on configuration
fn create_asset_source(config: &AssetsConfig, project_dir: &Path) -> Result<Box<dyn AssetSource>> {
    match config.source.as_str() {
        "local" => {
            if let Some(local_config) = &config.local {
                let framework_path = if local_config.framework_path.starts_with('/') {
                    // Absolute path
                    PathBuf::from(&local_config.framework_path)
                } else {
                    // Relative path
                    project_dir.join(&local_config.framework_path)
                };

                let framework_path = framework_path
                    .canonicalize()
                    .map_err(|e| RusteloError::generic(format!("Invalid framework path: {}", e)))?;

                Ok(Box::new(LocalAssetSource::new(
                    framework_path,
                    project_dir.to_path_buf(),
                )))
            } else {
                Err(RusteloError::generic(
                    "Local asset source selected but no [assets.local] configuration found",
                ))
            }
        }
        "embedded" => {
            // For now, return a simple implementation
            // TODO: Implement embedded asset source
            Err(RusteloError::generic(
                "Embedded asset source not yet implemented",
            ))
        }
        "download" => {
            // TODO: Implement download asset source
            Err(RusteloError::generic(
                "Download asset source not yet implemented",
            ))
        }
        _ => Err(RusteloError::generic(format!(
            "Unknown asset source: {}",
            config.source
        ))),
    }
}
