use super::super::NyashRunner;
use nyash_rust::mir::passes::method_id_inject::inject_method_ids;
use nyash_rust::{
    mir::{MirCompiler, MirInstruction},
    parser::NyashParser,
};
use std::{fs, process};

impl NyashRunner {
    /// Execute LLVM mode (split)
    pub(crate) fn execute_llvm_mode(&self, filename: &str) {
        // Initialize plugin host so method_id injection can resolve plugin calls
        crate::runner_plugin_init::init_bid_plugins();

        // Friendly plugin guard (non‑strict): unify diagnostics across modes
        crate::runner::modes::common_util::plugin_guard::check_and_report(
            false,
            crate::config::env::env_bool("NYASH_JSON_ONLY"),
            "llvm",
        );

        // Read the file
        let code = match fs::read_to_string(filename) {
            Ok(content) => content,
            Err(e) => {
                crate::console_println!("❌ Error reading file {}: {}", filename, e);
                process::exit(1);
            }
        };

        // Using handling (AST prelude merge like common/vm paths)
        let use_ast = crate::config::env::using_ast_enabled();
        let mut code_ref: &str = &code;
        let cleaned_code_owned;
        let mut prelude_asts: Vec<nyash_rust::ast::ASTNode> = Vec::new();
        if crate::config::env::enable_using() {
            match crate::runner::modes::common_util::resolve::resolve_prelude_paths_profiled(
                self, &code, filename,
            ) {
                Ok((clean, paths)) => {
                    cleaned_code_owned = clean;
                    code_ref = &cleaned_code_owned;
                    if !paths.is_empty() && !use_ast {
                        crate::console_println!("❌ using: AST prelude merge is disabled in this profile. Enable NYASH_USING_AST=1 or remove 'using' lines.");
                        std::process::exit(1);
                    }
                    if use_ast && !paths.is_empty() {
                        match crate::runner::modes::common_util::resolve::parse_preludes_to_asts(
                            self, &paths,
                        ) {
                            Ok(v) => prelude_asts = v,
                            Err(e) => {
                                crate::console_println!("❌ {}", e);
                                std::process::exit(1);
                            }
                        }
                    }
                }
                Err(e) => {
                    crate::console_println!("❌ {}", e);
                    process::exit(1);
                }
            }
        }
        // Pre-expand '@name[:T] = expr' sugar at line-head (same as common path)
        let preexpanded_owned =
            crate::runner::modes::common_util::resolve::preexpand_at_local(code_ref);
        code_ref = &preexpanded_owned;

        // Parse to AST (main)
        let main_ast = match NyashParser::parse_from_string(code_ref) {
            Ok(ast) => ast,
            Err(e) => {
                crate::runner::modes::common_util::diag::print_parse_error_with_context(
                    filename, code_ref, &e,
                );
                // Enhanced context: list merged prelude files if any (from text-merge path)
                let preludes =
                    crate::runner::modes::common_util::resolve::clone_last_merged_preludes();
                if !preludes.is_empty() {
                    crate::runtime::get_global_ring0().log.debug(&format!(
                        "[parse/context] merged prelude files ({}):",
                        preludes.len()
                    ));
                    let show = std::cmp::min(16, preludes.len());
                    for p in preludes.iter().take(show) {
                        crate::runtime::get_global_ring0()
                            .log
                            .debug(&format!("  - {}", p));
                    }
                    if preludes.len() > show {
                        crate::runtime::get_global_ring0()
                            .log
                            .debug(&format!("  ... ({} more)", preludes.len() - show));
                    }
                }
                process::exit(1);
            }
        };
        // Merge preludes + main when enabled
        let ast = if use_ast && !prelude_asts.is_empty() {
            crate::runner::modes::common_util::resolve::merge_prelude_asts_with_main(
                prelude_asts,
                &main_ast,
            )
        } else {
            main_ast
        };
        // Macro expansion (env-gated) after merge
        let ast = crate::r#macro::maybe_expand_and_dump(&ast, false);
        let ast = crate::runner::modes::macro_child::normalize_core_pass(&ast);

        // Compile to MIR
        let mut mir_compiler = MirCompiler::new();
        let compile_result =
            match crate::runner::modes::common_util::source_hint::compile_with_source_hint(
                &mut mir_compiler,
                ast,
                Some(filename),
            ) {
                Ok(result) => result,
                Err(e) => {
                    crate::console_println!("❌ MIR compilation error: {}", e);
                    process::exit(1);
                }
            };

        crate::console_println!("📊 MIR Module compiled successfully!");
        crate::console_println!("📊 Functions: {}", compile_result.module.functions.len());

        // Inject method_id for BoxCall/PluginInvoke where resolvable (by-id path)
        #[allow(unused_mut)]
        let mut module = compile_result.module.clone();
        let injected = inject_method_ids(&mut module);
        if injected > 0 {
            crate::cli_v!("[LLVM] method_id injected: {} places", injected);
        }

        // Phase 32 L-4.3a: JoinIR LLVM experiment hook
        // When NYASH_JOINIR_EXPERIMENT=1 and NYASH_JOINIR_LLVM_EXPERIMENT=1,
        // try to lower MIR → JoinIR → MIR' for Main.skip/1 to fix PHI issues.
        // JoinIR-converted functions are merged back into the original module.
        #[cfg(feature = "llvm-harness")]
        let module = if crate::config::env::joinir_experiment_enabled()
            && crate::config::env::joinir_llvm_experiment_enabled()
            && crate::config::env::llvm_use_harness()
        {
            use nyash_rust::mir::join_ir::lower_skip_ws_to_joinir;
            use nyash_rust::mir::join_ir_vm_bridge::bridge_joinir_to_mir;

            crate::runtime::get_global_ring0()
                .log
                .debug("[joinir/llvm] Attempting JoinIR path for LLVM execution");

            // Try to lower Main.skip/1 to JoinIR
            if module.functions.contains_key("Main.skip/1") {
                match lower_skip_ws_to_joinir(&module) {
                    Some(join_module) => {
                        crate::runtime::get_global_ring0().log.debug(&format!(
                            "[joinir/llvm] ✅ Lowered to JoinIR ({} functions)",
                            join_module.functions.len()
                        ));
                        // Convert JoinIR back to MIR' (with normalized PHI)
                        match bridge_joinir_to_mir(&join_module) {
                            Ok(mir_from_joinir) => {
                                crate::runtime::get_global_ring0().log.debug(&format!(
                                    "[joinir/llvm] ✅ Converted to MIR' ({} functions)",
                                    mir_from_joinir.functions.len()
                                ));
                                // Merge JoinIR functions into original module
                                // Strategy: Remove Main.skip/1 (PHI-problematic) and rename join_func_0 to Main.skip/1
                                let mut merged = module.clone();

                                // Remove the original PHI-problematic Main.skip/1
                                if merged.functions.remove("Main.skip/1").is_some() {
                                    crate::runtime::get_global_ring0().log.debug("[joinir/llvm] Removed original Main.skip/1 (PHI-problematic)");
                                }

                                for (name, func) in mir_from_joinir.functions {
                                    // Rename join_func_0 → Main.skip/1 to maintain call compatibility
                                    let target_name = if name == "join_func_0" {
                                        crate::runtime::get_global_ring0().log.debug(&format!(
                                            "[joinir/llvm] Renaming {} → Main.skip/1",
                                            name
                                        ));
                                        "Main.skip/1".to_string()
                                    } else {
                                        crate::runtime::get_global_ring0().log.debug(&format!(
                                            "[joinir/llvm] Adding JoinIR function: {}",
                                            name
                                        ));
                                        name
                                    };
                                    merged.functions.insert(target_name, func);
                                }
                                crate::runtime::get_global_ring0().log.debug(&format!(
                                    "[joinir/llvm] ✅ Merged module ({} functions)",
                                    merged.functions.len()
                                ));
                                merged
                            }
                            Err(e) => {
                                crate::runtime::get_global_ring0().log.debug(&format!(
                                    "[joinir/llvm] ❌ JoinIR→MIR conversion failed: {:?}",
                                    e
                                ));
                                crate::runtime::get_global_ring0()
                                    .log
                                    .debug("[joinir/llvm] Falling back to original MIR");
                                module
                            }
                        }
                    }
                    None => {
                        crate::runtime::get_global_ring0()
                            .log
                            .debug("[joinir/llvm] ❌ JoinIR lowering returned None");
                        crate::runtime::get_global_ring0()
                            .log
                            .debug("[joinir/llvm] Falling back to original MIR");
                        module
                    }
                }
            } else {
                crate::runtime::get_global_ring0()
                    .log
                    .debug("[joinir/llvm] Main.skip/1 not found, using original MIR");
                module
            }
        } else {
            module
        };

        // Dev/Test helper: allow executing via PyVM harness when requested
        if std::env::var("SMOKES_USE_PYVM").ok().as_deref() == Some("1") {
            match super::common_util::pyvm::run_pyvm_harness_lib(&module, "llvm-ast") {
                Ok(code) => {
                    std::process::exit(code);
                }
                Err(e) => {
                    crate::console_println!("❌ PyVM harness error: {}", e);
                    std::process::exit(1);
                }
            }
        }

        // If explicit object path is requested, emit object only
        if let Ok(_out_path) = std::env::var("NYASH_LLVM_OBJ_OUT") {
            #[cfg(feature = "llvm-harness")]
            {
                // Harness path (optional): if NYASH_LLVM_USE_HARNESS=1, try Python/llvmlite first.
                if crate::config::env::llvm_use_harness() {
                    if let Err(e) = crate::runner::modes::common_util::exec::llvmlite_emit_object(
                        &module, &_out_path, 20_000,
                    ) {
                        crate::console_println!("❌ {}", e);
                        process::exit(1);
                    }
                    return;
                }
                // Verify object presence and size (>0)
                match std::fs::metadata(&_out_path) {
                    Ok(meta) => {
                        if meta.len() == 0 {
                            crate::console_println!("❌ harness object is empty: {}", _out_path);
                            process::exit(1);
                        }
                        if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") {
                            crate::console_println!(
                                "[LLVM] object emitted: {} ({} bytes)",
                                _out_path,
                                meta.len()
                            );
                        }
                    }
                    Err(e) => {
                        crate::console_println!(
                            "❌ harness output not found after emit: {} ({})",
                            _out_path,
                            e
                        );
                        process::exit(1);
                    }
                }
                return;
            }
            #[cfg(all(not(feature = "llvm-harness"), feature = "llvm-inkwell-legacy"))]
            {
                use nyash_rust::backend::llvm_compile_to_object;
                // Ensure parent directory exists for the object file
                if let Some(parent) = std::path::Path::new(&_out_path).parent() {
                    let _ = std::fs::create_dir_all(parent);
                }
                crate::cli_v!(
                    "[Runner/LLVM] emitting object to {} (cwd={})",
                    _out_path,
                    std::env::current_dir()
                        .map(|p| p.display().to_string())
                        .unwrap_or_default()
                );
                if let Err(e) = llvm_compile_to_object(&module, &_out_path) {
                    crate::console_println!("❌ LLVM object emit error: {}", e);
                    process::exit(1);
                }
                match std::fs::metadata(&_out_path) {
                    Ok(meta) if meta.len() > 0 => {
                        crate::cli_v!(
                            "[LLVM] object emitted: {} ({} bytes)",
                            _out_path,
                            meta.len()
                        );
                    }
                    _ => {
                        crate::console_println!("❌ LLVM object not found or empty: {}", _out_path);
                        process::exit(1);
                    }
                }
                return;
            }
            #[cfg(all(not(feature = "llvm-harness"), not(feature = "llvm-inkwell-legacy")))]
            {
                crate::console_println!("❌ LLVM backend not available (object emit).");
                process::exit(1);
            }
        }

        // Execute via LLVM backend (harness preferred)
        #[cfg(feature = "llvm-harness")]
        {
            if crate::config::env::llvm_use_harness() {
                // Prefer producing a native executable via ny-llvmc, then execute it
                let exe_out = "tmp/nyash_llvm_run";
                let libs = std::env::var("NYASH_LLVM_EXE_LIBS").ok();
                match crate::runner::modes::common_util::exec::ny_llvmc_emit_exe_lib(
                    &module,
                    exe_out,
                    None,
                    libs.as_deref(),
                ) {
                    Ok(()) => {
                        match crate::runner::modes::common_util::exec::run_executable(
                            exe_out,
                            &[],
                            20_000,
                        ) {
                            Ok((code, _timed_out, stdout_text)) => {
                                // Forward program stdout so parity tests can compare outputs
                                if !stdout_text.is_empty() {
                                    print!("{}", stdout_text);
                                }
                                crate::console_println!(
                                    "✅ LLVM (harness) execution completed (exit={})",
                                    code
                                );
                                std::process::exit(code);
                            }
                            Err(e) => {
                                crate::console_println!("❌ run executable error: {}", e);
                                std::process::exit(1);
                            }
                        }
                    }
                    Err(e) => {
                        crate::console_println!("❌ ny-llvmc emit-exe error: {}", e);
                        crate::console_println!(
                            "   Hint: build ny-llvmc: cargo build -p nyash-llvm-compiler --release"
                        );
                        std::process::exit(1);
                    }
                }
            }
        }

        // Execute via LLVM backend (mock or real)
        #[cfg(feature = "llvm-inkwell-legacy")]
        {
            use nyash_rust::backend::llvm_compile_and_execute;
            let temp_path = "nyash_llvm_temp";
            match llvm_compile_and_execute(&module, temp_path) {
                Ok(result) => {
                    if let Some(int_result) = result.as_any().downcast_ref::<IntegerBox>() {
                        let exit_code = int_result.value;
                        crate::console_println!("✅ LLVM execution completed!");
                        crate::console_println!("📊 Exit code: {}", exit_code);
                        process::exit(exit_code as i32);
                    } else {
                        crate::console_println!(
                            "✅ LLVM execution completed (non-integer result)!"
                        );
                        crate::console_println!("📊 Result: {}", result.to_string_box().value);
                    }
                }
                Err(e) => {
                    crate::console_println!("❌ LLVM execution error: {}", e);
                    process::exit(1);
                }
            }
        }
        #[cfg(all(not(feature = "llvm-inkwell-legacy")))]
        {
            crate::console_println!("🔧 Mock LLVM Backend Execution:");
            crate::console_println!("   Build with --features llvm-inkwell-legacy for Rust/inkwell backend, or set NYASH_LLVM_OBJ_OUT and NYASH_LLVM_USE_HARNESS=1 for harness.");
            // NamingBox SSOT: Select entry (arity-aware, Main.main → main fallback)
            let entry =
                crate::runner::modes::common_util::entry_selection::select_entry_function(&module);
            if let Some(main_func) = module.functions.get(&entry) {
                for (_bid, block) in &main_func.blocks {
                    for inst in &block.instructions {
                        match inst {
                            MirInstruction::Return { value: Some(_) } => {
                                crate::console_println!("✅ Mock exit code: 42");
                                process::exit(42);
                            }
                            MirInstruction::Return { value: None } => {
                                crate::console_println!("✅ Mock exit code: 0");
                                process::exit(0);
                            }
                            _ => {}
                        }
                    }
                }
            }
            crate::console_println!("✅ Mock exit code: 0");
            process::exit(0);
        }
    }
}

// emit_mir_json_for_harness moved to crate::runner::mir_json_emit
