//! Build system integration and pipeline management

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

use async_trait::async_trait;
use serde::{Deserialize, Serialize};

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

/// Build configuration for different targets and modes
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BuildConfig {
    /// Build targets configuration
    pub targets: HashMap<String, BuildTarget>,
    /// Global build hooks
    pub hooks: BuildHooks,
    /// Asset processing settings
    pub assets: AssetBuildConfig,
    /// Cache configuration
    pub cache: CacheConfig,
}

/// Build target configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BuildTarget {
    /// Target name (dev, prod, test, etc.)
    pub name: String,
    /// Output directory
    pub output_dir: PathBuf,
    /// Asset categories to include
    pub asset_categories: Vec<AssetCategory>,
    /// Build variables
    pub variables: HashMap<String, String>,
    /// Target-specific hooks
    pub hooks: Option<BuildHooks>,
    /// Whether to organize assets by category
    pub organize_by_category: bool,
    /// Cross-compilation settings
    pub cross: Option<CrossCompilationConfig>,
}

/// Build hooks for pipeline integration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BuildHooks {
    /// Commands to run before build starts
    pub pre_build: Vec<String>,
    /// Commands to run after assets are processed
    pub post_assets: Vec<String>,
    /// Commands to run after build completes
    pub post_build: Vec<String>,
    /// Commands to run on build failure
    pub on_error: Vec<String>,
}

/// Asset processing configuration for builds
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssetBuildConfig {
    /// Enable asset processing
    pub enabled: bool,
    /// Enable asset watching in development
    pub watch_enabled: bool,
    /// Asset processing concurrency
    pub concurrency: usize,
    /// Skip processing if assets haven't changed
    pub incremental: bool,
}

/// Build cache configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CacheConfig {
    /// Enable build caching
    pub enabled: bool,
    /// Cache directory
    pub cache_dir: PathBuf,
    /// Cache key strategy
    pub key_strategy: CacheKeyStrategy,
    /// Cache expiration in hours
    pub expire_hours: u64,
}

/// Cache key generation strategy
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum CacheKeyStrategy {
    /// Hash based on file content
    ContentHash,
    /// Hash based on modification time
    ModTimeHash,
    /// Combination of content and metadata
    Hybrid,
}

/// Cross-compilation configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CrossCompilationConfig {
    /// Enable cross-compilation
    pub enabled: bool,
    /// Target architecture
    pub target: String,
    /// Docker image for cross-compilation
    pub docker_image: String,
    /// Docker volumes to mount
    pub volumes: Vec<DockerVolume>,
    /// Environment variables for cross-compilation
    pub env_vars: HashMap<String, String>,
    /// Cross-compilation script path
    pub script_path: PathBuf,
}

/// Docker volume configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DockerVolume {
    /// Host path
    pub host: PathBuf,
    /// Container path
    pub container: PathBuf,
    /// Mount options (e.g., "Z" for SELinux)
    pub options: Option<String>,
}

/// Build pipeline executor
pub struct BuildPipeline {
    #[allow(dead_code)]
    config: RusteloConfig,
    build_config: BuildConfig,
    resolver: AssetResolver,
}

/// Build hook executor trait
#[async_trait]
pub trait HookExecutor: Send + Sync {
    /// Execute a build hook command
    async fn execute_hook(&self, command: &str, context: &BuildContext) -> Result<()>;

    /// Check if hook should be skipped
    fn should_skip_hook(&self, _command: &str, _context: &BuildContext) -> bool {
        false
    }
}

/// Build context passed to hooks
#[derive(Debug, Clone)]
pub struct BuildContext {
    /// Target being built
    pub target: String,
    /// Build mode (development, production)
    pub mode: String,
    /// Build variables
    pub variables: HashMap<String, String>,
    /// Output directory
    pub output_dir: PathBuf,
    /// Whether this is an incremental build
    pub incremental: bool,
}

/// Build result information
#[derive(Debug)]
pub struct BuildResult {
    /// Target name
    pub target: String,
    /// Build success status
    pub success: bool,
    /// Number of assets processed
    pub assets_processed: usize,
    /// Build duration
    pub duration: std::time::Duration,
    /// Output files created
    pub output_files: Vec<PathBuf>,
    /// Cache hits
    pub cache_hits: usize,
}

impl Default for BuildConfig {
    fn default() -> Self {
        let mut targets = HashMap::new();

        // Development target
        targets.insert(
            "dev".to_string(),
            BuildTarget {
                name: "dev".to_string(),
                output_dir: PathBuf::from("target/dev"),
                asset_categories: vec![
                    AssetCategory::Static,
                    AssetCategory::Content,
                    AssetCategory::Config,
                    AssetCategory::Development,
                ],
                variables: {
                    let mut vars = HashMap::new();
                    vars.insert("mode".to_string(), "development".to_string());
                    vars.insert("debug".to_string(), "true".to_string());
                    vars
                },
                hooks: None,
                organize_by_category: false,
                cross: None,
            },
        );

        // Production target
        targets.insert(
            "prod".to_string(),
            BuildTarget {
                name: "prod".to_string(),
                output_dir: PathBuf::from("dist"),
                asset_categories: vec![
                    AssetCategory::Static,
                    AssetCategory::Content,
                    AssetCategory::Config,
                    AssetCategory::Deployment,
                ],
                variables: {
                    let mut vars = HashMap::new();
                    vars.insert("mode".to_string(), "production".to_string());
                    vars.insert("optimize".to_string(), "true".to_string());
                    vars
                },
                hooks: None,
                organize_by_category: true,
                cross: None,
            },
        );

        // Cross-compilation target for Linux
        targets.insert(
            "cross-linux".to_string(),
            BuildTarget {
                name: "cross-linux".to_string(),
                output_dir: PathBuf::from("target-linux"),
                asset_categories: vec![
                    AssetCategory::Static,
                    AssetCategory::Content,
                    AssetCategory::Config,
                    AssetCategory::Deployment,
                ],
                variables: {
                    let mut vars = HashMap::new();
                    vars.insert("mode".to_string(), "production".to_string());
                    vars.insert("target".to_string(), "x86_64-unknown-linux-gnu".to_string());
                    vars.insert("cross".to_string(), "true".to_string());
                    vars
                },
                hooks: Some(BuildHooks {
                    pre_build: vec!["echo 'Starting cross-compilation build...'".to_string()],
                    post_assets: vec![],
                    post_build: vec!["echo 'Cross-compilation completed!'".to_string()],
                    on_error: vec!["echo 'Cross-compilation failed!'".to_string()],
                }),
                organize_by_category: true,
                cross: Some(CrossCompilationConfig {
                    enabled: true,
                    target: "x86_64-unknown-linux-gnu".to_string(),
                    docker_image: "rustelo-cross:latest".to_string(),
                    volumes: vec![
                        DockerVolume {
                            host: PathBuf::from("target-linux"),
                            container: PathBuf::from("/workspace/target"),
                            options: Some("Z".to_string()),
                        },
                        DockerVolume {
                            host: PathBuf::from("node_modules_linux"),
                            container: PathBuf::from("/workspace/node_modules"),
                            options: Some("Z".to_string()),
                        },
                    ],
                    env_vars: {
                        let mut env = HashMap::new();
                        env.insert(
                            "CARGO_BUILD_TARGET".to_string(),
                            "x86_64-unknown-linux-gnu".to_string(),
                        );
                        env.insert("RUSTELO_MODE".to_string(), "production".to_string());
                        env.insert("NODE_ENV".to_string(), "production".to_string());
                        env
                    },
                    script_path: PathBuf::from("scripts/cross-build.sh"),
                }),
            },
        );

        Self {
            targets,
            hooks: BuildHooks {
                pre_build: vec![],
                post_assets: vec![],
                post_build: vec![],
                on_error: vec![],
            },
            assets: AssetBuildConfig {
                enabled: true,
                watch_enabled: true,
                concurrency: 4,
                incremental: true,
            },
            cache: CacheConfig {
                enabled: true,
                cache_dir: PathBuf::from("target/rustelo-cache"),
                key_strategy: CacheKeyStrategy::Hybrid,
                expire_hours: 24,
            },
        }
    }
}

impl BuildPipeline {
    /// Create a new build pipeline
    pub fn new(config: RusteloConfig, build_config: BuildConfig) -> Self {
        let resolver = AssetResolver::new(config.clone());

        Self {
            config,
            build_config,
            resolver,
        }
    }

    /// Build a specific target
    pub async fn build_target(&self, target_name: &str) -> Result<BuildResult> {
        let start_time = std::time::Instant::now();

        let target = self.build_config.targets.get(target_name).ok_or_else(|| {
            RusteloError::generic(format!("Unknown build target: {}", target_name))
        })?;

        let context = BuildContext {
            target: target_name.to_string(),
            mode: target
                .variables
                .get("mode")
                .cloned()
                .unwrap_or_else(|| "development".to_string()),
            variables: target.variables.clone(),
            output_dir: target.output_dir.clone(),
            incremental: self.build_config.cache.enabled,
        };

        tracing::info!("Building target: {}", target_name);

        // Execute pre-build hooks
        self.execute_hooks(&self.build_config.hooks.pre_build, &context)
            .await?;
        if let Some(ref target_hooks) = target.hooks {
            self.execute_hooks(&target_hooks.pre_build, &context)
                .await?;
        }

        // Handle cross-compilation or regular build
        let assets_processed = if let Some(ref cross_config) = target.cross {
            if cross_config.enabled {
                self.execute_cross_compilation(target, &context).await?
            } else {
                // Process assets normally
                if self.build_config.assets.enabled {
                    self.process_target_assets(target, &context).await?
                } else {
                    0
                }
            }
        } else {
            // Process assets normally
            if self.build_config.assets.enabled {
                self.process_target_assets(target, &context).await?
            } else {
                0
            }
        };

        // Execute post-asset hooks
        self.execute_hooks(&self.build_config.hooks.post_assets, &context)
            .await?;
        if let Some(ref target_hooks) = target.hooks {
            self.execute_hooks(&target_hooks.post_assets, &context)
                .await?;
        }

        // Execute post-build hooks
        self.execute_hooks(&self.build_config.hooks.post_build, &context)
            .await?;
        if let Some(ref target_hooks) = target.hooks {
            self.execute_hooks(&target_hooks.post_build, &context)
                .await?;
        }

        let duration = start_time.elapsed();

        // TODO: Collect actual output files
        let output_files = vec![];

        Ok(BuildResult {
            target: target_name.to_string(),
            success: true,
            assets_processed,
            duration,
            output_files,
            cache_hits: 0, // TODO: Implement cache hit counting
        })
    }

    /// Process assets for a target
    async fn process_target_assets(
        &self,
        target: &BuildTarget,
        context: &BuildContext,
    ) -> Result<usize> {
        tracing::info!("Processing assets for target: {}", target.name);

        let mut variables = target.variables.clone();
        variables.insert("timestamp".to_string(), chrono::Utc::now().to_rfc3339());
        variables.extend(context.variables.clone());

        let processed = if target.organize_by_category {
            // Organize assets by category
            let organized = self
                .resolver
                .organize_by_category(&target.output_dir, variables)
                .await?;
            organized.values().map(|files| files.len()).sum()
        } else {
            // Process assets with category filter
            let assets = self
                .resolver
                .process_assets_by_category(&target.output_dir, &target.asset_categories, variables)
                .await?;
            assets.len()
        };

        tracing::info!("Processed {} assets for target {}", processed, target.name);
        Ok(processed)
    }

    /// Execute a list of hook commands
    async fn execute_hooks(&self, hooks: &[String], context: &BuildContext) -> Result<()> {
        for hook in hooks {
            self.execute_hook(hook, context).await?;
        }
        Ok(())
    }

    /// Execute a single hook command
    async fn execute_hook(&self, hook: &str, context: &BuildContext) -> Result<()> {
        if hook.trim().is_empty() {
            return Ok(());
        }

        tracing::info!("Executing hook: {}", hook);

        // Simple command execution (could be enhanced with shell detection)
        let mut cmd = if cfg!(target_os = "windows") {
            let mut cmd = Command::new("cmd");
            cmd.args(["/C", hook]);
            cmd
        } else {
            let mut cmd = Command::new("sh");
            cmd.args(["-c", hook]);
            cmd
        };

        // Set environment variables from build context
        cmd.env("RUSTELO_TARGET", &context.target);
        cmd.env("RUSTELO_MODE", &context.mode);
        cmd.env("RUSTELO_OUTPUT_DIR", &context.output_dir);

        for (key, value) in &context.variables {
            cmd.env(format!("RUSTELO_{}", key.to_uppercase()), value);
        }

        let output = cmd.output().map_err(|e| {
            RusteloError::generic(format!("Failed to execute hook '{}': {}", hook, e))
        })?;

        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(RusteloError::generic(format!(
                "Hook '{}' failed: {}",
                hook, stderr
            )));
        }

        let stdout = String::from_utf8_lossy(&output.stdout);
        if !stdout.trim().is_empty() {
            tracing::info!("Hook output: {}", stdout.trim());
        }

        Ok(())
    }

    /// Execute cross-compilation build
    async fn execute_cross_compilation(
        &self,
        target: &BuildTarget,
        context: &BuildContext,
    ) -> Result<usize> {
        let cross_config = target
            .cross
            .as_ref()
            .ok_or_else(|| RusteloError::generic("Cross-compilation config missing"))?;

        tracing::info!(
            "Executing cross-compilation for target: {}",
            cross_config.target
        );

        // Check if cross-compilation script exists
        if !cross_config.script_path.exists() {
            tracing::warn!(
                "Cross-compilation script not found: {}",
                cross_config.script_path.display()
            );
            tracing::info!("Falling back to regular asset processing");
            return self.process_target_assets(target, context).await;
        }

        // Prepare cross-compilation environment
        let mut cmd = std::process::Command::new(&cross_config.script_path);

        // Set environment variables
        for (key, value) in &cross_config.env_vars {
            cmd.env(key, value);
        }

        // Add build context variables
        cmd.env("RUSTELO_TARGET", &context.target);
        cmd.env("RUSTELO_MODE", &context.mode);
        cmd.env("RUSTELO_OUTPUT_DIR", &context.output_dir);

        for (key, value) in &context.variables {
            cmd.env(format!("RUSTELO_{}", key.to_uppercase()), value);
        }

        // Execute cross-compilation
        tracing::info!(
            "Running cross-compilation script: {}",
            cross_config.script_path.display()
        );
        let output = cmd.output().map_err(|e| {
            RusteloError::generic(format!("Failed to execute cross-compilation script: {}", e))
        })?;

        if !output.status.success() {
            let stderr = String::from_utf8_lossy(&output.stderr);
            return Err(RusteloError::generic(format!(
                "Cross-compilation failed: {}",
                stderr
            )));
        }

        let stdout = String::from_utf8_lossy(&output.stdout);
        if !stdout.trim().is_empty() {
            tracing::info!("Cross-compilation output: {}", stdout.trim());
        }

        // Count processed files in output directory
        let mut file_count = 0;
        if target.output_dir.exists() {
            for entry in std::fs::read_dir(&target.output_dir).map_err(RusteloError::Io)? {
                let entry = entry.map_err(RusteloError::Io)?;
                if entry.file_type().map_err(RusteloError::Io)?.is_file() {
                    file_count += 1;
                }
            }
        }

        tracing::info!(
            "Cross-compilation completed for target: {}",
            cross_config.target
        );
        Ok(file_count)
    }

    /// Load build configuration from file
    pub fn load_build_config<P: AsRef<Path>>(path: P) -> Result<BuildConfig> {
        if !path.as_ref().exists() {
            return Ok(BuildConfig::default());
        }

        let content = std::fs::read_to_string(path).map_err(RusteloError::Io)?;

        let config: BuildConfig = toml::from_str(&content)
            .map_err(|e| RusteloError::generic(format!("Failed to parse build config: {}", e)))?;

        Ok(config)
    }

    /// Save build configuration to file
    pub fn save_build_config<P: AsRef<Path>>(config: &BuildConfig, path: P) -> Result<()> {
        let content = toml::to_string_pretty(config).map_err(|e| {
            RusteloError::generic(format!("Failed to serialize build config: {}", e))
        })?;

        std::fs::write(path, content).map_err(RusteloError::Io)?;

        Ok(())
    }
}
