use super::super::{MirBuilder, ValueId};

/// Phase 287 P5: Guard against rewriting primitive type toString/stringify/str
///
/// Returns true if should block rewrite (primitive type detected)
/// SSOT: toString/stringify/str use universal slot #0, not user-defined methods
fn should_block_primitive_str_rewrite(
    builder: &MirBuilder,
    object_value: ValueId,
    method: &str,
) -> bool {
    // Only guard toString/stringify/str methods
    if !(method == "toString" || method == "stringify" || method == "str") {
        return false;
    }

    let Some(recv_type) = builder.type_ctx.value_types.get(&object_value) else {
        return false;
    };

    use crate::mir::MirType;
    let is_primitive = match recv_type {
        MirType::Integer | MirType::Float | MirType::Bool | MirType::String => true,
        MirType::Box(name) if name == "IntegerBox" || name == "FloatBox"
            || name == "BoolBox" || name == "StringBox" => true,
        _ => false,
    };

    if is_primitive {
        if std::env::var("NYASH_STATIC_CALL_TRACE").ok().as_deref() == Some("1") {
            eprintln!("[P287-GUARD] Primitive type {:?} uses universal slot #0 for {}, not user-defined method",
                recv_type, method);
        }
        true
    } else {
        false
    }
}

/// Gate: whether instance→function rewrite is enabled.
fn rewrite_enabled() -> bool {
    // New primary flag (P4): NYASH_REWRITE_KNOWN_DEFAULT (default ON; allow explicit OFF)
    if let Ok(v) = std::env::var("NYASH_REWRITE_KNOWN_DEFAULT") {
        let s = v.to_ascii_lowercase();
        if s == "0" || s == "false" || s == "off" {
            return false;
        }
        if s == "1" || s == "true" || s == "on" {
            return true;
        }
        // fallthrough to legacy if malformed
    }
    // Legacy flag (kept for compatibility): NYASH_BUILDER_REWRITE_INSTANCE (default ON)
    match std::env::var("NYASH_BUILDER_REWRITE_INSTANCE")
        .ok()
        .as_deref()
        .map(|v| v.to_ascii_lowercase())
    {
        Some(ref s) if s == "0" || s == "false" || s == "off" => false,
        Some(ref s) if s == "1" || s == "true" || s == "on" => true,
        _ => true, // default ON (spec unchanged; can opt out by setting ...=0)
    }
}

/// Try Known‑route instance→function rewrite.
/// 既存の安全ガード（user_defined/存在確認/ENV）を尊重して関数化する。
#[allow(dead_code)]
pub(crate) fn try_known_rewrite(
    builder: &mut MirBuilder,
    object_value: ValueId,
    cls: &str,
    method: &str,
    mut arg_values: Vec<ValueId>,
) -> Option<Result<ValueId, String>> {
    // Global gate
    if !rewrite_enabled() {
        return None;
    }
    // Receiver must be Known (origin 由来)
    if builder
        .type_ctx
        .value_origin_newbox
        .get(&object_value)
        .is_none()
    {
        return None;
    }
    // Only user-defined boxes (plugin/core boxesは対象外)
    if !builder.comp_ctx.user_defined_boxes.contains_key(cls) { // Phase 285LLVM-1.1: HashMap
        return None;
    }

    // Phase 287 P5: Unified primitive guard (toString/stringify/str use universal slot #0)
    if should_block_primitive_str_rewrite(builder, object_value, method) {
        return None;
    }

    // Policy gates（従来互換）
    let allow_userbox_rewrite =
        std::env::var("NYASH_DEV_REWRITE_USERBOX").ok().as_deref() == Some("1");
    let allow_new_origin = std::env::var("NYASH_DEV_REWRITE_NEW_ORIGIN")
        .ok()
        .as_deref()
        == Some("1");
    let from_new_origin = builder
        .type_ctx
        .value_origin_newbox
        .get(&object_value)
        .is_some();
    let arity = arg_values.len();
    let fname = crate::mir::builder::calls::function_lowering::generate_method_function_name(
        cls, method, arity,
    );
    let module_has = if let Some(ref module) = builder.current_module {
        module.functions.contains_key(&fname)
    } else {
        false
    };
    if !((module_has || allow_userbox_rewrite) || (from_new_origin && allow_new_origin)) {
        return None;
    }
    // Materialize function call: pass 'me' first, then args (unified call)
    let mut call_args = Vec::with_capacity(arity + 1);
    call_args.push(object_value);
    call_args.append(&mut arg_values);
    crate::mir::builder::ssa::local::finalize_args(builder, &mut call_args);
    let dst = builder.next_value_id();
    if let Err(e) = builder.emit_unified_call(
        Some(dst),
        crate::mir::builder::builder_calls::CallTarget::Global(fname.clone()),
        call_args,
    ) {
        return Some(Err(e));
    }
    // Annotate and emit choose
    let chosen = fname.clone();
    builder.annotate_call_result_from_func_name(dst, &chosen);
    let meta = serde_json::json!({
        "recv_cls": cls,
        "method": method,
        "arity": arity,
        "chosen": chosen,
        "reason": "userbox-rewrite",
        "certainty": "Known",
    });
    super::super::observe::resolve::emit_choose(builder, meta);
    Some(Ok(dst))
}

/// Variant: try Known rewrite but honor a requested destination.
pub(crate) fn try_known_rewrite_to_dst(
    builder: &mut MirBuilder,
    want_dst: Option<ValueId>,
    object_value: ValueId,
    cls: &str,
    method: &str,
    mut arg_values: Vec<ValueId>,
) -> Option<Result<ValueId, String>> {
    if !rewrite_enabled() {
        return None;
    }
    if builder
        .type_ctx
        .value_origin_newbox
        .get(&object_value)
        .is_none()
    {
        return None;
    }
    if !builder.comp_ctx.user_defined_boxes.contains_key(cls) { // Phase 285LLVM-1.1: HashMap
        return None;
    }

    // Phase 287 P5: Unified primitive guard (toString/stringify/str use universal slot #0)
    if should_block_primitive_str_rewrite(builder, object_value, method) {
        return None;
    }

    let allow_userbox_rewrite =
        std::env::var("NYASH_DEV_REWRITE_USERBOX").ok().as_deref() == Some("1");
    let allow_new_origin = std::env::var("NYASH_DEV_REWRITE_NEW_ORIGIN")
        .ok()
        .as_deref()
        == Some("1");
    let from_new_origin = builder
        .type_ctx
        .value_origin_newbox
        .get(&object_value)
        .is_some();
    let arity = arg_values.len();
    let fname = crate::mir::builder::calls::function_lowering::generate_method_function_name(
        cls, method, arity,
    );
    let module_has = if let Some(ref module) = builder.current_module {
        module.functions.contains_key(&fname)
    } else {
        false
    };
    if !((module_has || allow_userbox_rewrite) || (from_new_origin && allow_new_origin)) {
        return None;
    }
    // unified global function call (module-local)
    let mut call_args = Vec::with_capacity(arity + 1);
    call_args.push(object_value);
    call_args.append(&mut arg_values);
    crate::mir::builder::ssa::local::finalize_args(builder, &mut call_args);
    let actual_dst = want_dst.unwrap_or_else(|| builder.next_value_id());
    if let Err(e) = builder.emit_unified_call(
        Some(actual_dst),
        crate::mir::builder::builder_calls::CallTarget::Global(fname.clone()),
        call_args,
    ) {
        return Some(Err(e));
    }
    builder.annotate_call_result_from_func_name(actual_dst, &fname);
    let meta = serde_json::json!({
        "recv_cls": cls,
        "method": method,
        "arity": arity,
        "chosen": fname,
        "reason": "userbox-rewrite",
        "certainty": "Known",
    });
    super::super::observe::resolve::emit_choose(builder, meta);
    Some(Ok(actual_dst))
}

/// Fallback: when exactly one user-defined method matches by name/arity across the module,
/// resolve to that even if class inference failed. Deterministic via uniqueness and user-box prefix.
#[allow(dead_code)]
pub(crate) fn try_unique_suffix_rewrite(
    builder: &mut MirBuilder,
    object_value: ValueId,
    method: &str,
    mut arg_values: Vec<ValueId>,
) -> Option<Result<ValueId, String>> {
    if !rewrite_enabled() {
        return None;
    }
    // Only attempt if receiver is Known (keeps behavior stable and avoids surprises)
    if builder
        .type_ctx
        .value_origin_newbox
        .get(&object_value)
        .is_none()
    {
        return None;
    }
    let mut cands: Vec<String> = builder.method_candidates(method, arg_values.len());
    if cands.len() != 1 {
        return None;
    }
    let fname = cands.remove(0);
    // 🎯 Phase 21.7++ Phase 3: StaticMethodId SSOT 実装
    let id = crate::mir::naming::StaticMethodId::parse(&fname)?;
    if !builder.comp_ctx.user_defined_boxes.contains_key(&id.box_name) { // Phase 285LLVM-1.1: HashMap
        return None;
    }

    // Phase 287 P5: Unified primitive guard (toString/stringify/str use universal slot #0)
    if should_block_primitive_str_rewrite(builder, object_value, method) {
        return None;
    }

    // unified
    let mut call_args = Vec::with_capacity(arg_values.len() + 1);
    call_args.push(object_value); // 'me'
    let arity_us = arg_values.len();
    call_args.append(&mut arg_values);
    crate::mir::builder::ssa::local::finalize_args(builder, &mut call_args);
    let dst = builder.next_value_id();
    if let Err(e) = builder.emit_unified_call(
        Some(dst),
        crate::mir::builder::builder_calls::CallTarget::Global(fname.clone()),
        call_args,
    ) {
        return Some(Err(e));
    }
    builder.annotate_call_result_from_func_name(dst, &fname);
    let meta = serde_json::json!({
        "recv_cls": builder.type_ctx.value_origin_newbox.get(&object_value).cloned().unwrap_or_default(),
        "method": method,
        "arity": arity_us,
        "chosen": fname,
        "reason": "unique-suffix",
        "certainty": "Heuristic",
    });
    super::super::observe::resolve::emit_choose(builder, meta);
    Some(Ok(dst))
}

/// Variant: unique-suffix rewrite honoring requested destination.
pub(crate) fn try_unique_suffix_rewrite_to_dst(
    builder: &mut MirBuilder,
    want_dst: Option<ValueId>,
    object_value: ValueId,
    method: &str,
    mut arg_values: Vec<ValueId>,
) -> Option<Result<ValueId, String>> {
    if !rewrite_enabled() {
        return None;
    }
    if builder
        .type_ctx
        .value_origin_newbox
        .get(&object_value)
        .is_none()
    {
        return None;
    }
    let mut cands: Vec<String> = builder.method_candidates(method, arg_values.len());
    if cands.len() != 1 {
        return None;
    }
    let fname = cands.remove(0);
    // 🎯 Phase 21.7++ Phase 3: StaticMethodId SSOT 実装
    let id = crate::mir::naming::StaticMethodId::parse(&fname)?;
    if !builder.comp_ctx.user_defined_boxes.contains_key(&id.box_name) { // Phase 285LLVM-1.1: HashMap
        return None;
    }

    // Phase 287 P5: Unified primitive guard (toString/stringify/str use universal slot #0)
    if should_block_primitive_str_rewrite(builder, object_value, method) {
        return None;
    }

    let _name_const = match crate::mir::builder::name_const::make_name_const_result(builder, &fname)
    {
        Ok(v) => v,
        Err(e) => return Some(Err(e)),
    };
    let mut call_args = Vec::with_capacity(arg_values.len() + 1);
    call_args.push(object_value);
    let arity_us = arg_values.len();
    call_args.append(&mut arg_values);
    crate::mir::builder::ssa::local::finalize_args(builder, &mut call_args);
    let actual_dst = want_dst.unwrap_or_else(|| builder.next_value_id());
    if let Err(e) = builder.emit_unified_call(
        Some(actual_dst),
        crate::mir::builder::builder_calls::CallTarget::Global(fname.clone()),
        call_args,
    ) {
        return Some(Err(e));
    }
    builder.annotate_call_result_from_func_name(actual_dst, &fname);
    let meta = serde_json::json!({
        "recv_cls": builder.type_ctx.value_origin_newbox.get(&object_value).cloned().unwrap_or_default(),
        "method": method,
        "arity": arity_us,
        "chosen": fname,
        "reason": "unique-suffix",
        "certainty": "Heuristic",
    });
    super::super::observe::resolve::emit_choose(builder, meta);
    Some(Ok(actual_dst))
}

/// Unified entry: try Known rewrite first, then unique-suffix fallback.
#[allow(dead_code)]
pub(crate) fn try_known_or_unique(
    builder: &mut MirBuilder,
    object_value: ValueId,
    class_name_opt: &Option<String>,
    method: &str,
    arg_values: Vec<ValueId>,
) -> Option<Result<ValueId, String>> {
    if let Some(cls) = class_name_opt.as_ref() {
        if let Some(res) = try_known_rewrite(builder, object_value, cls, method, arg_values.clone())
        {
            return Some(res);
        }
    }
    try_unique_suffix_rewrite(builder, object_value, method, arg_values)
}

/// Variant: honor requested destination
pub(crate) fn try_known_or_unique_to_dst(
    builder: &mut MirBuilder,
    want_dst: Option<ValueId>,
    object_value: ValueId,
    class_name_opt: &Option<String>,
    method: &str,
    arg_values: Vec<ValueId>,
) -> Option<Result<ValueId, String>> {
    if let Some(cls) = class_name_opt.as_ref() {
        if let Some(res) = try_known_rewrite_to_dst(
            builder,
            want_dst,
            object_value,
            cls,
            method,
            arg_values.clone(),
        ) {
            return Some(res);
        }
    }
    try_unique_suffix_rewrite_to_dst(builder, want_dst, object_value, method, arg_values)
}
