use super::*;
use crate::box_trait::NyashBox;

impl MirInterpreter {
    pub(super) fn handle_new_box(
        &mut self,
        dst: ValueId,
        box_type: &str,
        args: &[ValueId],
    ) -> Result<(), VMError> {
        // Provider Lock guard (受け口・既定は挙動不変)
        if let Err(e) = crate::runtime::provider_lock::guard_before_new_box(box_type) {
            return Err(self.err_invalid(e));
        }

        // Fast path (bench/profile-only): new StringBox("const") without registry roundtrip
        if std::env::var("NYASH_VM_FAST").ok().as_deref() == Some("1") && box_type == "StringBox" {
            if args.len() == 1 {
                let v0 = self.reg_load(args[0])?;
                let s_opt: Option<String> = match v0.clone() {
                    VMValue::String(s) => Some(s),
                    VMValue::BoxRef(b) => {
                        if let Some(sb) =
                            b.as_any().downcast_ref::<crate::boxes::basic::StringBox>()
                        {
                            Some(sb.value.clone())
                        } else {
                            None
                        }
                    }
                    _ => None,
                };
                if let Some(s) = s_opt {
                    let boxed: Box<dyn crate::box_trait::NyashBox> =
                        Box::new(crate::boxes::basic::StringBox::new(s));
                    let created_vm = VMValue::from_nyash_box(boxed);
                    self.regs.insert(dst, created_vm);
                    if Self::box_trace_enabled() {
                        self.box_trace_emit_new(box_type, args.len());
                    }
                    return Ok(());
                }
            }
        }
        let converted = self.load_args_as_boxes(args)?;
        let reg = crate::runtime::unified_registry::get_global_unified_registry();
        let created = reg
            .lock()
            .unwrap()
            .create_box(box_type, &converted)
            .map_err(|e| self.err_with_context(&format!("NewBox {}", box_type), &e.to_string()))?;
        // Store created instance first so 'me' can be passed to birth
        let created_vm = VMValue::from_nyash_box(created);
        self.regs.insert(dst, created_vm.clone());

        // Trace: new box event (dev-only)
        if Self::box_trace_enabled() {
            self.box_trace_emit_new(box_type, args.len());
        }

        // Note: birth の自動呼び出しは削除。
        // 正しい設計は Builder が NewBox 後に明示的に birth 呼び出しを生成すること。
        Ok(())
    }

    pub(super) fn handle_plugin_invoke(
        &mut self,
        dst: Option<ValueId>,
        box_val: ValueId,
        method: &str,
        args: &[ValueId],
    ) -> Result<(), VMError> {
        let recv = self.reg_load(box_val)?;
        let recv_box: Box<dyn NyashBox> = match recv.clone() {
            VMValue::BoxRef(b) => b.share_box(),
            other => other.to_nyash_box(),
        };

        if let Some(p) = recv_box
            .as_any()
            .downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
        {
            let host = crate::runtime::plugin_loader_unified::get_global_plugin_host();
            let host = host.read().unwrap();
            let argv = self.load_args_as_boxes(args)?;
            match host.invoke_instance_method(&p.box_type, method, p.inner.instance_id, &argv) {
                Ok(Some(ret)) => {
                    self.write_from_box(dst, ret);
                }
                Ok(None) => {
                    self.write_void(dst);
                }
                Err(e) => {
                    return Err(self.err_with_context(
                        &format!("PluginInvoke {}.{}", p.box_type, method),
                        &format!("{:?}", e),
                    ))
                }
            }
            Ok(())
        } else if method == "toString" {
            self.write_string(dst, recv_box.to_string_box().value);
            Ok(())
        } else {
            // Phase 25.1 diagnostic: Log unknown method details before error
            eprintln!(
                "[vm/method-dispatch/plugin-invoke] Unknown method on Box: box_type_name={}, method_name={}",
                recv_box.type_name(),
                method
            );
            Err(self.err_method_not_found(&recv_box.type_name(), method))
        }
    }

    /// S-5.2-improved: Made public for JoinIR Runner integration via execute_box_call wrapper
    pub fn handle_box_call(
        &mut self,
        dst: Option<ValueId>,
        box_val: ValueId,
        method: &str,
        args: &[ValueId],
    ) -> Result<(), VMError> {
        // Dev-safe: stringify(Void) → "null" (最小安全弁)
        if method == "stringify" {
            if let VMValue::Void = self.reg_load(box_val)? {
                self.write_string(dst, "null".to_string());
                return Ok(());
            }
            if let VMValue::BoxRef(b) = self.reg_load(box_val)? {
                if b.as_any()
                    .downcast_ref::<crate::box_trait::VoidBox>()
                    .is_some()
                {
                    self.write_string(dst, "null".to_string());
                    return Ok(());
                }
            }
        }
        // Primitive stringify/toString helpers (compat with std/operators/stringify.hako probes).
        // Keep this narrow and total: it prevents "unknown method stringify on IntegerBox" when
        // code checks `value.stringify != null` and then calls it.
        if args.is_empty() && (method == "stringify" || method == "toString") {
            match self.reg_load(box_val)? {
                VMValue::Integer(i) => {
                    self.write_string(dst, i.to_string());
                    return Ok(());
                }
                VMValue::Bool(b) => {
                    self.write_string(dst, if b { "true" } else { "false" }.to_string());
                    return Ok(());
                }
                VMValue::Float(f) => {
                    self.write_string(dst, f.to_string());
                    return Ok(());
                }
                _ => {}
            }
        }
        // Trace: method call (class inferred from receiver)
        if Self::box_trace_enabled() {
            let cls = match self.reg_load(box_val).unwrap_or(VMValue::Void) {
                VMValue::BoxRef(b) => {
                    if let Some(inst) = b.as_any().downcast_ref::<crate::instance_v2::InstanceBox>()
                    {
                        inst.class_name.clone()
                    } else {
                        b.type_name().to_string()
                    }
                }
                VMValue::String(_) => "StringBox".to_string(),
                VMValue::Integer(_) => "IntegerBox".to_string(),
                VMValue::Float(_) => "FloatBox".to_string(),
                VMValue::Bool(_) => "BoolBox".to_string(),
                VMValue::Void => "<Void>".to_string(),
                VMValue::Future(_) => "<Future>".to_string(),
                VMValue::WeakBox(_) => "<WeakRef>".to_string(), // Phase 285A0
            };
            self.box_trace_emit_call(&cls, method, args.len());
        }
        if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") && method == "trim" {
            eprintln!("[vm-trace] handle_box_call: method=trim (pre-dispatch)");
        }
        // Debug: trace length dispatch receiver type before any handler resolution
        if method == "length" && std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") {
            let recv = self.reg_load(box_val).unwrap_or(VMValue::Void);
            let type_name = match recv.clone() {
                VMValue::BoxRef(b) => b.type_name().to_string(),
                VMValue::Integer(_) => "Integer".to_string(),
                VMValue::Float(_) => "Float".to_string(),
                VMValue::Bool(_) => "Bool".to_string(),
                VMValue::String(_) => "String".to_string(),
                VMValue::Void => "Void".to_string(),
                VMValue::Future(_) => "Future".to_string(),
                VMValue::WeakBox(_) => "WeakRef".to_string(), // Phase 285A0
            };
            eprintln!("[vm-trace] length dispatch recv_type={}", type_name);
        }
        // Graceful void guard for common short-circuit patterns in user code
        // e.g., `A or not last.is_eof()` should not crash when last is absent.
        match self.reg_load(box_val)? {
            VMValue::Void => {
                if let Some(val) = super::boxes_void_guards::handle_void_method(method) {
                    self.write_result(dst, val);
                    return Ok(());
                }
            }
            VMValue::BoxRef(ref b) => {
                if b.as_any()
                    .downcast_ref::<crate::box_trait::VoidBox>()
                    .is_some()
                {
                    if let Some(val) = super::boxes_void_guards::handle_void_method(method) {
                        self.write_result(dst, val);
                        return Ok(());
                    }
                }
            }
            _ => {}
        }
        if super::boxes_object_fields::try_handle_object_fields(self, dst, box_val, method, args)? {
            trace_dispatch!(method, "object_fields");
            return Ok(());
        }
        // Policy gate: user InstanceBox BoxCall runtime fallback
        // - Prod: disallowed (builder must have rewritten obj.m(...) to a
        //   function call). Error here indicates a builder/using materialize
        //   miss.
        // - Dev/CI: allowed with WARN to aid diagnosis.
        let mut user_instance_class: Option<String> = None;
        if let VMValue::BoxRef(ref b) = self.reg_load(box_val)? {
            if let Some(inst) = b.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
                user_instance_class = Some(inst.class_name.clone());
            }
        }
        if user_instance_class.is_some() && !crate::config::env::vm_allow_user_instance_boxcall() {
            let cls = user_instance_class.unwrap();
            return Err(self.err_invalid(format!(
                "User Instance BoxCall disallowed in prod: {}.{} (enable builder rewrite)",
                cls, method
            )));
        }
        if user_instance_class.is_some() && crate::config::env::vm_allow_user_instance_boxcall() {
            if crate::config::env::cli_verbose() {
                eprintln!(
                    "[warn] dev fallback: user instance BoxCall {}.{} routed via VM instance-dispatch",
                    user_instance_class.as_ref().unwrap(),
                    method
                );
            }
        }
        if super::boxes_instance::try_handle_instance_box(self, dst, box_val, method, args)? {
            trace_dispatch!(method, "instance_box");
            return Ok(());
        }
        if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") && method == "trim" {
            eprintln!("[vm-trace] dispatch trying boxes_string");
        }
        if super::boxes_string::try_handle_string_box(self, dst, box_val, method, args)? {
            trace_dispatch!(method, "string_box");
            if std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1") && method == "trim" {
                eprintln!("[vm-trace] dispatch handled by boxes_string");
            }
            return Ok(());
        }
        if super::boxes_array::try_handle_array_box(self, dst, box_val, method, args)? {
            trace_dispatch!(method, "array_box");
            return Ok(());
        }
        if super::boxes_map::try_handle_map_box(self, dst, box_val, method, args)? {
            trace_dispatch!(method, "map_box");
            return Ok(());
        }
        // Narrow safety valve: if 'length' wasn't handled by any box-specific path,
        // treat it as 0 (avoids Lt on Void in common loops). This is a dev-time
        // robustness measure; precise behavior should be provided by concrete boxes.
        if method == "length" {
            trace_dispatch!(method, "fallback(length=0)");
            self.write_result(dst, VMValue::Integer(0));
            return Ok(());
        }
        // Fallback: unique-tail dynamic resolution for user-defined methods
        // Narrowing: restrict to receiver's class when available to avoid
        // accidentally binding methods from unrelated boxes that happen to
        // share the same method name/arity (e.g., JsonScanner.is_eof vs JsonToken.is_eof).
        if let Some(func) = {
            let tail = format!(".{}{}", method, format!("/{}", args.len()));
            let mut cands: Vec<String> = self
                .functions
                .keys()
                .filter(|k| k.ends_with(&tail))
                .cloned()
                .collect();
            // Determine receiver class name when possible
            let recv_cls: Option<String> = match self.reg_load(box_val).ok() {
                Some(VMValue::BoxRef(b)) => {
                    if let Some(inst) = b.as_any().downcast_ref::<crate::instance_v2::InstanceBox>()
                    {
                        Some(inst.class_name.clone())
                    } else {
                        None
                    }
                }
                _ => None,
            };
            if let Some(ref want) = recv_cls {
                let prefix = format!("{}.", want);
                cands.retain(|k| k.starts_with(&prefix));
            }
            if cands.len() == 1 {
                self.functions.get(&cands[0]).cloned()
            } else {
                None
            }
        } {
            // Build argv: pass receiver as first arg (me)
            let recv_vm = self.reg_load(box_val)?;
            let mut argv: Vec<VMValue> = Vec::with_capacity(1 + args.len());
            argv.push(recv_vm);
            for a in args {
                argv.push(self.reg_load(*a)?);
            }
            let ret = self.exec_function_inner(&func, Some(&argv))?;
            self.write_result(dst, ret);
            return Ok(());
        }

        super::boxes_plugin::invoke_plugin_box(self, dst, box_val, method, args)
    }

    // moved: try_handle_map_box → handlers/boxes_map.rs
    #[allow(dead_code)]
    fn try_handle_map_box(
        &mut self,
        dst: Option<ValueId>,
        box_val: ValueId,
        method: &str,
        args: &[ValueId],
    ) -> Result<bool, VMError> {
        super::boxes_map::try_handle_map_box(self, dst, box_val, method, args)
    }

    // moved: try_handle_string_box → handlers/boxes_string.rs
    #[allow(dead_code)]
    fn try_handle_string_box(
        &mut self,
        dst: Option<ValueId>,
        box_val: ValueId,
        method: &str,
        args: &[ValueId],
    ) -> Result<bool, VMError> {
        super::boxes_string::try_handle_string_box(self, dst, box_val, method, args)
    }

    // moved: try_handle_array_box → handlers/boxes_array.rs
    #[allow(dead_code)]
    fn try_handle_array_box(
        &mut self,
        dst: Option<ValueId>,
        box_val: ValueId,
        method: &str,
        args: &[ValueId],
    ) -> Result<bool, VMError> {
        super::boxes_array::try_handle_array_box(self, dst, box_val, method, args)
    }
}
