//! Run command handler
//!
//! Orchestrates running nodes using ProcessService.

use crate::context::CliContext;
use crate::paths;
use anyhow::Result;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::Duration;

/// Arguments for run command
#[derive(Debug, Clone)]
pub struct RunArgs {
    pub nodes: Vec<String>,
    pub release: bool,
    pub detach: bool,
}

/// Handle run command
///
/// Runs specified nodes or all enabled nodes.
///
/// # Arguments
///
/// * `ctx` - CLI execution context
/// * `args` - Run command arguments
pub async fn handle_run(ctx: &mut CliContext, args: &RunArgs) -> Result<()> {
    println!();
    println!("🚀 Running Mecha10 Nodes");
    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
    println!();

    // Check project initialization
    if !ctx.is_project_initialized() {
        return Err(anyhow::anyhow!(
            "Not in a Mecha10 project directory.\n\
             Run 'mecha10 init' to create a new project."
        ));
    }

    // Load project info and enabled nodes (complete borrow before other ctx calls)
    let (project_name, enabled_nodes) = {
        let project = ctx.project()?;
        let name = project.name()?.to_string();
        let nodes = project.list_enabled_nodes().await?;
        (name, nodes)
    };

    let _config = ctx.load_project_config().await?;

    println!("Project: {}", project_name);
    println!();

    // Check Redis connection
    println!("Checking Redis connection...");
    let redis_url = ctx.redis_url().to_string();
    let redis = ctx.redis()?;

    match redis.get_connection().await {
        Ok(_) => {
            println!("✅ Redis connected: {}", redis_url);
        }
        Err(e) => {
            println!("❌ Redis connection failed: {}", e);
            println!();
            println!("Please ensure Redis is running:");
            println!("  docker-compose up -d redis");
            println!();
            return Err(anyhow::anyhow!("Redis connection required"));
        }
    }
    println!();

    // Determine which nodes to run
    let nodes_to_run = if args.nodes.is_empty() {
        // Run all enabled nodes from config
        enabled_nodes
    } else {
        // Run specified nodes
        args.nodes.clone()
    };

    if nodes_to_run.is_empty() {
        println!("No nodes configured to run.");
        println!();
        println!("To add nodes:");
        println!("  mecha10 add <component>");
        println!();
        return Ok(());
    }

    println!("Nodes to run:");
    for node in &nodes_to_run {
        println!("  • {}", node);
    }
    println!();

    // Determine binary path (release or debug)
    let build_profile = if args.release { "release" } else { "debug" };
    println!("Build profile: {}", build_profile);
    println!();

    // Create process service
    let process_service = ctx.process();

    // Spawn node processes
    println!("Starting nodes...");
    println!();

    // Collect binary paths with owned strings to avoid lifetime issues
    let binary_paths: Vec<String> = nodes_to_run
        .iter()
        .map(|node_name| paths::target_path(build_profile, node_name))
        .collect();

    let mut node_specs = Vec::new();
    for (node_name, binary_path) in nodes_to_run.iter().zip(binary_paths.iter()) {
        node_specs.push((node_name.as_str(), binary_path.as_str(), vec![]));
    }

    let pids = process_service.spawn_nodes(node_specs)?;

    for (node, pid) in &pids {
        println!("  ✅ {} (PID: {})", node, pid);
    }
    println!();

    if args.detach {
        // Detach mode - start nodes and exit
        println!("✨ Nodes started in detached mode");
        println!();
        println!("To stop nodes:");
        for pid in pids.values() {
            println!("  kill {}", pid);
        }
        println!();

        // Don't cleanup - let processes continue running
        let _ = process_service;
    } else {
        // Foreground mode - wait for interrupt
        println!("Press Ctrl+C to stop all nodes");
        println!();

        // Set up signal handler
        let running = Arc::new(AtomicBool::new(true));
        let r = running.clone();

        ctrlc::set_handler(move || {
            r.store(false, Ordering::SeqCst);
        })?;

        // Wait for interrupt
        while running.load(Ordering::SeqCst) {
            tokio::time::sleep(Duration::from_millis(100)).await;

            // Check if any processes have exited
            let status = process_service.get_status();
            for (node, node_status) in &status {
                if !node_status.contains("running") {
                    println!("⚠️  {} stopped", node);
                }
            }
        }

        println!();
        println!("Stopping all nodes...");
        process_service.cleanup();
        println!("✅ All nodes stopped");
    }

    println!();
    Ok(())
}
