/*!
 * CoreExecutor — JSON v0 → Execute (boxed)
 *
 * Responsibility
 * - Single entry to execute a MIR(JSON) payload under Gate‑C/Core policy.
 * - Encapsulates: optional canonicalize, v1-bridge try, v0-parse fallback,
 *   OOB strict observation, and rc mapping via MIR Interpreter.
 *
 * Notes
 * - For now, execution uses the existing MIR Interpreter runner
 *   (execute_mir_module_quiet_exit). Later we can swap internals to call
 *   the Core Dispatcher directly without touching callers.
 */

use super::NyashRunner;
use std::io::Write;

pub fn run_json_v0(runner: &NyashRunner, json: &str) -> i32 {
    // Optional: direct Core Dispatcher via child nyash (boxed)
    // Toggle: HAKO_CORE_DIRECT=1 (alias: NYASH_CORE_DIRECT)
    let core_direct = std::env::var("HAKO_CORE_DIRECT").ok().as_deref() == Some("1")
        || std::env::var("NYASH_CORE_DIRECT").ok().as_deref() == Some("1");
    if core_direct {
        // Only attempt Core-Direct when payload already looks like MIR(JSON v0)
        // i.e., has functions/blocks keys. Stage‑B Program(JSON v0) must go through bridge first.
        let looks_like_mir = json.contains("\"functions\"") && json.contains("\"blocks\"");
        if looks_like_mir {
            // In-proc prototype (opt-in): HAKO_CORE_DIRECT_INPROC=1 (alias NYASH_CORE_DIRECT_INPROC)
            let core_direct_inproc = std::env::var("HAKO_CORE_DIRECT_INPROC").ok().as_deref()
                == Some("1")
                || std::env::var("NYASH_CORE_DIRECT_INPROC").ok().as_deref() == Some("1");
            if core_direct_inproc {
                if let Some(rc) = try_run_core_direct_inproc(runner, json) {
                    return rc;
                }
                eprintln!("[core-exec] direct Core (inproc) failed; trying child wrapper");
            }
            if let Some(rc) = try_run_core_direct(json) {
                return rc;
            }
            eprintln!("[core-exec] direct Core (child) failed; falling back to VM interpreter");
        }
        // else: skip direct Core and continue to bridge/VM path
    }
    let mut payload = json.to_string();

    // Optional: downconvert/canonicalize even for v1 when requested (dev diagnostics)
    if crate::config::env::nyvm_v1_downconvert() {
        if let Ok(j) =
            crate::runner::modes::common_util::core_bridge::canonicalize_module_json(&payload)
        {
            payload = j;
        }
    }

    // Prefer v1 bridge when schema_version is present (JSON v1). This must run
    // before the v0 fast-path because v1 payloads also contain `functions` and
    // `blocks`, which would otherwise be misrouted to the v0 loader.
    if payload.contains("\"schema_version\"") {
        match crate::runner::json_v1_bridge::try_parse_v1_to_module(&payload) {
            Ok(Some(module)) => {
                super::json_v0_bridge::maybe_dump_mir(&module);
                crate::runner::child_env::pre_run_reset_oob_if_strict();
                let rc = runner.execute_mir_module_quiet_exit(&module);
                if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen() {
                    eprintln!("[gate-c][oob-strict] Out-of-bounds observed → exit(1)");
                    return 1;
                }
                return rc;
            }
            Ok(None) => { /* fall through to v0 path */ }
            Err(e) => {
                eprintln!("❌ JSON v1 bridge error: {}", e);
                return 1;
            }
        }
    }

    // Fast-path: accept MIR(JSON v0) directly when it looks like a module (functions/blocks)
    if payload.contains("\"functions\"") && payload.contains("\"blocks\"") {
        match super::mir_json_v0::parse_mir_v0_to_module(&payload) {
            Ok(module) => {
                super::json_v0_bridge::maybe_dump_mir(&module);
                crate::runner::child_env::pre_run_reset_oob_if_strict();
                let rc = runner.execute_mir_module_quiet_exit(&module);
                if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen() {
                    eprintln!("[gate-c][oob-strict] Out-of-bounds observed → exit(1)");
                    return 1;
                }
                return rc;
            }
            Err(e) => {
                eprintln!("❌ MIR JSON v0 parse error: {}", e);
                return 1;
            }
        }
    }

    // For non‑v1 input, attempt canonicalization and v1 bridge (Stage‑B program → MIR).
    if let Ok(j) =
        crate::runner::modes::common_util::core_bridge::canonicalize_module_json(&payload)
    {
        payload = j;
    }
    match crate::runner::json_v1_bridge::try_parse_v1_to_module(&payload) {
        Ok(Some(module)) => {
            super::json_v0_bridge::maybe_dump_mir(&module);
            crate::runner::child_env::pre_run_reset_oob_if_strict();
            let rc = runner.execute_mir_module_quiet_exit(&module);
            if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen() {
                eprintln!("[gate-c][oob-strict] Out-of-bounds observed → exit(1)");
                return 1;
            }
            return rc;
        }
        Ok(None) => { /* fall through to v0 parse/execute */ }
        Err(e) => {
            eprintln!("❌ JSON v1 bridge error: {}", e);
            return 1;
        }
    }

    match super::json_v0_bridge::parse_json_v0_to_module(&payload) {
        Ok(module) => {
            super::json_v0_bridge::maybe_dump_mir(&module);
            crate::runner::child_env::pre_run_reset_oob_if_strict();
            let rc = runner.execute_mir_module_quiet_exit(&module);
            if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen() {
                eprintln!("[gate-c][oob-strict] Out-of-bounds observed → exit(1)");
                return 1;
            }
            rc
        }
        Err(e) => {
            eprintln!("❌ JSON v0 bridge error: {}", e);
            1
        }
    }
}

fn try_run_core_direct(json: &str) -> Option<i32> {
    // Generate a temporary Hako program that includes the Core dispatcher
    // and calls NyVmDispatcher.run(json), printing the numeric result.
    let tmp_dir = std::path::Path::new("tmp");
    let _ = std::fs::create_dir_all(tmp_dir);
    let script_path = tmp_dir.join("core_exec_direct.hako");
    // Escape JSON into Hako string literal (simple backslash+quote escaping)
    let mut j = String::new();
    for ch in json.chars() {
        match ch {
            '\\' => j.push_str("\\\\"),
            '"' => j.push_str("\\\""),
            _ => j.push(ch),
        }
    }
    let code = format!(
        "include \"lang/src/vm/core/dispatcher.hako\"\nstatic box Main {{ method main(args) {{ local j=\"{}\"; local r=NyVmDispatcher.run(j); return r }} }}\n",
        j
    );
    if let Ok(mut f) = std::fs::File::create(&script_path) {
        let _ = f.write_all(code.as_bytes());
    } else {
        return None;
    }
    // Determine nyash binary (current executable)
    let exe = std::env::current_exe().ok()?;
    let mut cmd = std::process::Command::new(exe);
    crate::runner::child_env::apply_core_wrapper_env(&mut cmd);
    let out = cmd
        .args(["--backend", "vm", script_path.to_string_lossy().as_ref()])
        .output()
        .ok()?;
    if !out.stdout.is_empty() {
        let _ = std::io::stdout().write_all(&out.stdout);
    }
    let rc = out.status.code().unwrap_or(1);
    Some(rc)
}

fn try_run_core_direct_inproc(runner: &NyashRunner, json: &str) -> Option<i32> {
    // Parse MIR(JSON v0) in-proc and execute via MIR Interpreter quietly.
    // This bypasses the child Hako wrapper and reduces latency/recursion risks.
    match crate::runner::json_v0_bridge::parse_json_v0_to_module(json) {
        Ok(module) => {
            crate::runner::child_env::pre_run_reset_oob_if_strict();
            let rc = runner.execute_mir_module_quiet_exit(&module);
            if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen() {
                eprintln!("[gate-c][oob-strict] Out-of-bounds observed → exit(1)");
                return Some(1);
            }
            Some(rc)
        }
        Err(_) => None,
    }
}
