use super::*;

fn matches_mir_type(value: &VMValue, ty: &MirType) -> bool {
    match ty {
        MirType::Unknown => true,
        MirType::Void => match value {
            VMValue::Void => true,
            VMValue::BoxRef(bx) => {
                bx.as_any().downcast_ref::<crate::box_trait::VoidBox>().is_some()
                    || bx
                        .as_any()
                        .downcast_ref::<crate::boxes::missing_box::MissingBox>()
                        .is_some()
            }
            _ => false,
        },
        MirType::Bool => match value {
            VMValue::Bool(_) => true,
            VMValue::BoxRef(bx) => bx.as_any().downcast_ref::<crate::box_trait::BoolBox>().is_some(),
            _ => false,
        },
        MirType::Integer => match value {
            VMValue::Integer(_) => true,
            VMValue::BoxRef(bx) => {
                bx.as_any().downcast_ref::<crate::box_trait::IntegerBox>().is_some()
            }
            _ => false,
        },
        MirType::Float => match value {
            VMValue::Float(_) => true,
            VMValue::BoxRef(bx) => bx.as_any().downcast_ref::<crate::boxes::FloatBox>().is_some(),
            _ => false,
        },
        MirType::String => match value {
            VMValue::String(_) => true,
            VMValue::BoxRef(bx) => {
                bx.as_any().downcast_ref::<crate::box_trait::StringBox>().is_some()
            }
            _ => false,
        },
        MirType::WeakRef => matches!(value, VMValue::WeakBox(_)), // Phase 285A1
        MirType::Future(_) => matches!(value, VMValue::Future(_)),
        MirType::Array(_) => {
            // Current VM representation is BoxRef(ArrayBox) (not a distinct VMValue variant).
            // Keep this as a conservative name match to avoid guessing.
            match value {
                VMValue::BoxRef(bx) => {
                    if let Some(inst) = bx.as_any().downcast_ref::<crate::instance_v2::InstanceBox>()
                    {
                        inst.class_name == "ArrayBox"
                    } else {
                        bx.type_name() == "ArrayBox"
                    }
                }
                _ => false,
            }
        }
        MirType::Box(name) => match value {
            VMValue::BoxRef(bx) => {
                // User-defined boxes are represented as InstanceBox (type_name is not stable for user boxes).
                if let Some(inst) = bx.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
                    if inst.class_name == *name {
                        return true;
                    }
                    // Builtin inner box name match (best-effort).
                    if let Some(inner) = inst.inner_content.as_ref() {
                        if inner.type_name() == name {
                            return true;
                        }
                    }
                    false
                } else {
                    bx.type_name() == name
                }
            }
            // Allow primitive to satisfy core "Box types" by name when user writes "IntegerBox" etc.
            VMValue::Integer(_) => name == "IntegerBox",
            VMValue::Float(_) => name == "FloatBox",
            VMValue::Bool(_) => name == "BoolBox",
            VMValue::String(_) => name == "StringBox",
            VMValue::Void => name == "VoidBox" || name == "NullBox",
            _ => false,
        },
    }
}

impl MirInterpreter {
    pub(super) fn handle_type_op(
        &mut self,
        dst: ValueId,
        op: TypeOpKind,
        value: ValueId,
        ty: &MirType,
    ) -> Result<(), VMError> {
        let v = self.reg_load(value)?;
        match op {
            TypeOpKind::Check => {
                let ok = matches_mir_type(&v, ty);
                self.regs.insert(dst, VMValue::Bool(ok));
                Ok(())
            }
            TypeOpKind::Cast => {
                if matches_mir_type(&v, ty) {
                    self.regs.insert(dst, v);
                    Ok(())
                } else {
                    Err(VMError::TypeError(format!(
                        "type cast failed: expected {:?}, got {:?}",
                        ty, v
                    )))
                }
            }
        }
    }
}
