//! Init command handler
//!
//! Orchestrates project initialization using InitService and ProjectTemplateService.

use crate::commands::InitArgs;
use crate::context::CliContext;
use crate::init::validation;
use crate::types::DashboardConfig;
use crate::ui::init_wizard::run_wizard_tui;
use anyhow::Result;
use std::path::{Path, PathBuf};

/// Result type for init processing: (project_name, project_path, template, nodes, dev)
type InitProcessResult = (String, PathBuf, Option<String>, Vec<String>, bool);

/// Cleanup partially created project directory on error
async fn cleanup_on_error(project_path: &Path) {
    if project_path.exists() {
        if let Err(e) = tokio::fs::remove_dir_all(project_path).await {
            eprintln!("⚠️  Failed to cleanup directory: {}", e);
        } else {
            println!("🧹 Cleaned up incomplete project directory");
        }
    }
}

/// Process init command arguments in non-interactive mode
///
/// Validates and processes command-line arguments for project initialization.
/// This is used when:
/// - Running in a non-TTY environment
/// - User specifies --non-interactive flag
/// - Interactive wizard fails
///
/// # Arguments
///
/// * `args` - Init command arguments
/// * `working_dir` - Working directory for resolving relative paths
/// * `catalog` - Component catalog service for loading nodes
///
/// # Returns
///
/// A tuple of (project_name, project_path, template, nodes, dev_mode)
fn process_non_interactive_args(
    args: &InitArgs,
    working_dir: &Path,
    catalog: &crate::services::ComponentCatalogService,
) -> Result<InitProcessResult> {
    use crate::init::get_template_defaults;

    // Require project name in non-interactive mode
    let name = args.name.clone().ok_or_else(|| {
        anyhow::anyhow!(
            "Project name required in non-interactive mode.\n\
             Usage: mecha10 init <PROJECT_NAME> [OPTIONS]\n\
             Example: mecha10 init my-robot"
        )
    })?;

    // Check if it's a path or just a name
    let path_buf = PathBuf::from(&name);
    let (project_name, project_path) = if path_buf.is_absolute() || name.contains('/') || name.contains('\\') {
        // It's a path - extract the final component as the name
        let final_name = path_buf
            .file_name()
            .and_then(|n| n.to_str())
            .unwrap_or(&name)
            .to_string();
        (final_name, path_buf)
    } else {
        // It's just a name - create path relative to working directory
        let final_path = working_dir.join(&name);
        (name, final_path)
    };

    let template = args.template.clone();

    // Apply template defaults for nodes unless --no-examples is specified
    let nodes = if args.no_examples {
        Vec::new()
    } else {
        let mut node_components = catalog.get_by_category("nodes");
        node_components.sort_by(|a, b| a.name.cmp(&b.name));
        let node_ids: Vec<String> = node_components.iter().map(|c| c.id.clone()).collect();

        let defaults = get_template_defaults(&template, &node_ids);

        node_ids
            .iter()
            .enumerate()
            .filter(|(i, _)| defaults[*i])
            .map(|(_, id)| id.clone())
            .collect()
    };

    let dev = args.dev;

    Ok((project_name, project_path, template, nodes, dev))
}

/// Handle init command
///
/// Creates a new Mecha10 project with interactive wizard or command-line arguments.
///
/// # Arguments
///
/// * `ctx` - CLI execution context
/// * `args` - Init command arguments
pub async fn handle_init(ctx: &mut CliContext, args: &InitArgs) -> Result<()> {
    // Clear terminal before starting to avoid menu display issues
    print!("\x1B[2J\x1B[1;1H");

    println!();
    println!("🤖 Initialize Mecha10 Project");
    println!();

    // Capture working_dir before borrowing catalog
    let working_dir = ctx.working_dir.clone();

    // Load component catalog for example selection
    let catalog = ctx.component_catalog();

    // Determine if we should use interactive wizard
    let is_tty = atty::is(atty::Stream::Stdin) && atty::is(atty::Stream::Stdout);
    let use_wizard = is_tty && !args.non_interactive;

    let (project_name, project_path, template, nodes, dev) = if use_wizard {
        // Try interactive wizard with TUI
        let default_name = args.name.clone();
        match run_wizard_tui(default_name.as_deref(), catalog) {
            Ok((name, template, selected_nodes)) => {
                // Check if it's a path or just a name
                let path_buf = std::path::PathBuf::from(&name);
                let (final_name, path) = if path_buf.is_absolute() || name.contains('/') || name.contains('\\') {
                    // It's a path - extract the final component as the name
                    let final_name = path_buf
                        .file_name()
                        .and_then(|n| n.to_str())
                        .unwrap_or(&name)
                        .to_string();
                    (final_name, path_buf)
                } else {
                    // It's just a name - create path relative to working directory
                    let final_path = ctx.working_dir.join(&name);
                    (name, final_path)
                };

                // Use --dev flag from args even in interactive mode
                (final_name, path, template, selected_nodes, args.dev)
            }
            Err(e) => {
                println!("⚠️  Wizard failed: {}", e);
                println!("Using command-line arguments...\n");

                // Fall back to non-interactive mode
                process_non_interactive_args(args, &working_dir, catalog)?
            }
        }
    } else {
        if !is_tty {
            println!("ℹ️  Non-interactive mode\n");
        }

        // Non-interactive mode - use provided arguments
        process_non_interactive_args(args, &working_dir, catalog)?
    };

    // Check if directory already exists BEFORE any processing
    validation::check_directory_not_exists(&project_path)?;

    // Wrap the entire project creation in cleanup logic
    let result = perform_init(ctx, &project_path, &project_name, &template, &nodes, dev).await;

    // If init failed, cleanup and propagate error
    if let Err(e) = result {
        cleanup_on_error(&project_path).await;
        return Err(e);
    }

    // Success message
    print_success_message(&project_name);

    Ok(())
}

/// Perform the actual project initialization
async fn perform_init(
    ctx: &mut CliContext,
    project_path: &Path,
    project_name: &str,
    template: &Option<String>,
    nodes: &[String],
    dev: bool,
) -> Result<()> {
    // Create project directory structure
    ctx.init_service().create_project_directories(project_path).await?;
    println!("✅ Directories");

    // Copy simulation configs from framework (skips in standalone mode)
    ctx.init_service().copy_simulation_configs(project_path).await?;

    // Create simulation configs from embedded templates (standalone mode fallback)
    ctx.project_template_service()
        .create_simulation_configs(project_path)
        .await?;

    // Copy simulation assets (images) from framework (skips in standalone mode)
    ctx.init_service().copy_simulation_assets(project_path).await?;

    // Create simulation assets from embedded templates (standalone mode fallback)
    ctx.project_template_service()
        .create_simulation_assets(project_path)
        .await?;

    // Copy behavior tree templates from framework (skips in standalone mode)
    ctx.init_service().copy_behavior_templates(project_path).await?;

    // Create behavior templates from embedded files (standalone mode fallback)
    ctx.project_template_service()
        .create_behavior_templates(project_path)
        .await?;

    // Generate mecha10.json configuration
    ctx.project_template_service()
        .create_mecha10_json(project_path, project_name, template)
        .await?;
    println!("✅ Configuration (mecha10.json)");

    // Generate simulation/models/model.json
    ctx.project_template_service()
        .create_simulation_model_json(project_path)
        .await?;

    // Generate simulation/environments/basic_arena/environment.json
    ctx.project_template_service()
        .create_simulation_environment_json(project_path)
        .await?;
    println!("✅ Simulation assets");

    // Create node configuration files from embedded templates
    // Configs use ${CONTROL_PLANE_URL} which resolves from mecha10.json environments
    ctx.project_template_service().create_node_configs(project_path).await?;
    println!("✅ Node configurations");

    // Create project files
    ctx.project_template_service()
        .create_readme(project_path, project_name)
        .await?;

    ctx.project_template_service().create_gitignore(project_path).await?;

    ctx.project_template_service()
        .create_package_json(project_path, project_name)
        .await?;

    ctx.project_template_service()
        .create_requirements_txt(project_path)
        .await?;

    // Create .cargo/config.toml with patch directives (only in framework dev mode)
    if dev {
        let framework_path = ctx.init_service().detect_framework_path()?;
        ctx.project_template_service()
            .create_cargo_config(project_path, &framework_path)
            .await?;
        println!("🔧 Framework dev mode: Created .cargo/config.toml with source patches");
    }

    // Create Rust project files
    ctx.project_template_service()
        .create_cargo_toml(project_path, project_name, dev)
        .await?;

    ctx.project_template_service()
        .create_main_rs(project_path, project_name)
        .await?;

    ctx.project_template_service().create_build_rs(project_path).await?;

    ctx.project_template_service()
        .create_embedded_structure(project_path)
        .await?;

    // Detect framework path for development mode
    let framework_path = if dev {
        ctx.init_service().detect_framework_path().ok()
    } else {
        None
    };

    ctx.project_template_service()
        .create_env_example(project_path, project_name, framework_path)
        .await?;

    ctx.project_template_service().create_rustfmt_toml(project_path).await?;

    ctx.project_template_service()
        .create_docker_compose(project_path, project_name)
        .await?;

    // Copy simulation Docker files from framework (skips in standalone mode)
    ctx.init_service().copy_simulation_docker_files(project_path).await?;

    // Create simulation Docker files from embedded templates (standalone mode fallback)
    ctx.project_template_service()
        .create_simulation_docker_files(project_path)
        .await?;
    println!("✅ Project files");
    println!();

    // Add nodes if any were selected
    if !nodes.is_empty() {
        for node_name in nodes {
            if let Err(e) = ctx.init_service().add_example_node(project_path, node_name).await {
                println!("⚠️  Failed to add {}: {}", node_name, e);
            } else {
                println!("✅ {}", node_name);
            }
        }
        println!();
    }

    Ok(())
}

/// Print success message after initialization
fn print_success_message(project_name: &str) {
    println!("✨ Project created successfully!");
    println!();
    println!("📋 Next steps:");
    println!("  1. cd {}", project_name);
    println!("  2. Set OpenAI API key in .env (for AI features):");
    println!("     OPENAI_API_KEY=your-key-here");
    println!("  3. Run setup to install dependencies:");
    println!("     mecha10 setup");
    println!("  4. Start development:");
    println!("     mecha10 dev");
    println!();
    println!("Useful commands:");
    println!("  mecha10 topics list            # View message topics");
    println!("  mecha10 logs [node]            # Monitor node logs");
    println!("  mecha10 topology               # Visualize system topology");
    println!("  mecha10 diagnostics watch      # Monitor diagnostics in real-time");
    println!("  mecha10 models                 # Manage AI models");
    println!();
    println!("In dev mode:");
    println!("  's' → Start simulation         'wasd' → Teleop control");
    println!("  Dashboard: {}", DashboardConfig::default().effective_url());
    println!();
}
