use super::{Effect, EffectMask, MirInstruction, MirType, ValueId};
use crate::ast::{ASTNode, CallExpr};
use crate::mir::utils::is_current_block_terminated;
use crate::mir::TypeOpKind;

impl super::MirBuilder {
    // Print statement: env.console.log(value) with early TypeOp handling
    pub(super) fn build_print_statement(&mut self, expression: ASTNode) -> Result<ValueId, String> {
        super::utils::builder_debug_log("enter build_print_statement");
        // Prefer wrapper for simple function-call pattern (non-breaking refactor)
        if let Ok(call) = CallExpr::try_from(expression.clone()) {
            if (call.name == "isType" || call.name == "asType") && call.arguments.len() == 2 {
                super::utils::builder_debug_log(
                    "pattern: print(FunctionCall isType|asType) [via wrapper]",
                );
                if let Some(type_name) =
                    super::MirBuilder::extract_string_literal(&call.arguments[1])
                {
                    super::utils::builder_debug_log(&format!(
                        "extract_string_literal OK: {}",
                        type_name
                    ));
                    let val = self.build_expression(call.arguments[0].clone())?;
                    let ty = super::MirBuilder::parse_type_name_to_mir(&type_name);
                    let dst = self.next_value_id();
                    let op = if call.name == "isType" {
                        TypeOpKind::Check
                    } else {
                        TypeOpKind::Cast
                    };
                    super::utils::builder_debug_log(&format!(
                        "emit TypeOp {:?} value={} dst= {}",
                        op, val, dst
                    ));
                    self.emit_instruction(MirInstruction::TypeOp {
                        dst,
                        op,
                        value: val,
                        ty,
                    })?;
                    self.emit_instruction(MirInstruction::ExternCall {
                        dst: None,
                        iface_name: "env.console".to_string(),
                        method_name: "log".to_string(),
                        args: vec![dst],
                        effects: EffectMask::PURE.add(Effect::Io),
                    })?;
                    return Ok(dst);
                } else {
                    super::utils::builder_debug_log("extract_string_literal FAIL [via wrapper]");
                }
            }
        }

        match &expression {
            // print(isType(val, "Type")) / print(asType(...))
            ASTNode::FunctionCall {
                name, arguments, ..
            } if (name == "isType" || name == "asType") && arguments.len() == 2 => {
                super::utils::builder_debug_log("pattern: print(FunctionCall isType|asType)");
                if let Some(type_name) = super::MirBuilder::extract_string_literal(&arguments[1]) {
                    super::utils::builder_debug_log(&format!(
                        "extract_string_literal OK: {}",
                        type_name
                    ));
                    let val = self.build_expression(arguments[0].clone())?;
                    let ty = super::MirBuilder::parse_type_name_to_mir(&type_name);
                    let dst = self.next_value_id();
                    let op = if name == "isType" {
                        TypeOpKind::Check
                    } else {
                        TypeOpKind::Cast
                    };
                    super::utils::builder_debug_log(&format!(
                        "emit TypeOp {:?} value={} dst= {}",
                        op, val, dst
                    ));
                    self.emit_instruction(MirInstruction::TypeOp {
                        dst,
                        op,
                        value: val,
                        ty,
                    })?;
                    self.emit_instruction(MirInstruction::ExternCall {
                        dst: None,
                        iface_name: "env.console".to_string(),
                        method_name: "log".to_string(),
                        args: vec![dst],
                        effects: EffectMask::PURE.add(Effect::Io),
                    })?;
                    return Ok(dst);
                } else {
                    super::utils::builder_debug_log("extract_string_literal FAIL");
                }
            }
            // print(obj.is("Type")) / print(obj.as("Type"))
            ASTNode::MethodCall {
                object,
                method,
                arguments,
                ..
            } if (method == "is" || method == "as") && arguments.len() == 1 => {
                super::utils::builder_debug_log("pattern: print(MethodCall is|as)");
                if let Some(type_name) = super::MirBuilder::extract_string_literal(&arguments[0]) {
                    super::utils::builder_debug_log(&format!(
                        "extract_string_literal OK: {}",
                        type_name
                    ));
                    let obj_val = self.build_expression(*object.clone())?;
                    let ty = super::MirBuilder::parse_type_name_to_mir(&type_name);
                    let dst = self.next_value_id();
                    let op = if method == "is" {
                        TypeOpKind::Check
                    } else {
                        TypeOpKind::Cast
                    };
                    super::utils::builder_debug_log(&format!(
                        "emit TypeOp {:?} obj={} dst= {}",
                        op, obj_val, dst
                    ));
                    self.emit_instruction(MirInstruction::TypeOp {
                        dst,
                        op,
                        value: obj_val,
                        ty,
                    })?;
                    self.emit_instruction(MirInstruction::ExternCall {
                        dst: None,
                        iface_name: "env.console".to_string(),
                        method_name: "log".to_string(),
                        args: vec![dst],
                        effects: EffectMask::PURE.add(Effect::Io),
                    })?;
                    return Ok(dst);
                } else {
                    super::utils::builder_debug_log("extract_string_literal FAIL");
                }
            }
            _ => {}
        }

        let value = self.build_expression(expression)?;
        super::utils::builder_debug_log(&format!("fallback print value={}", value));

        // Phase 3.2: Use unified call for print statements
        let use_unified = super::calls::call_unified::is_unified_call_enabled();

        if use_unified {
            // New unified path - treat print as global function
            self.emit_unified_call(
                None, // print returns nothing
                super::builder_calls::CallTarget::Global("print".to_string()),
                vec![value],
            )?;
        } else {
            // Legacy path - use ExternCall
            self.emit_instruction(MirInstruction::ExternCall {
                dst: None,
                iface_name: "env.console".to_string(),
                method_name: "log".to_string(),
                args: vec![value],
                effects: EffectMask::PURE.add(Effect::Io),
            })?;
        }
        Ok(value)
    }

    // Block: sequentially build statements and return last value or Void
    pub(super) fn build_block(&mut self, statements: Vec<ASTNode>) -> Result<ValueId, String> {
        let trace = crate::mir::builder::control_flow::joinir::trace::trace();
        // Scope hint for bare block (Program)
        let scope_id = self.current_block.map(|bb| bb.as_u32()).unwrap_or(0);
        self.hint_scope_enter(scope_id);
        let _lex_scope = super::vars::lexical_scope::LexicalScopeGuard::new(self);

        let mut last_value = None;
        let total = statements.len();
        trace.emit_if(
            "debug",
            "build_block",
            &format!("Processing {} statements", total),
            trace.is_enabled(),
        );

        // Phase 132 P0.5: Use while loop instead of for loop to support suffix skipping
        let mut idx = 0;
        while idx < statements.len() {
            // Phase 132 P0.5: Try suffix router (dev-only)
            if crate::config::env::joinir_dev_enabled() {
                let remaining = &statements[idx..];
                // Clone func_name to avoid borrow conflict
                let func_name = self
                    .scope_ctx
                    .current_function
                    .as_ref()
                    .map(|f| f.signature.name.clone())
                    .unwrap_or_else(|| "unknown".to_string());
                let debug = trace.is_enabled();

                use crate::mir::builder::control_flow::joinir::patterns::policies::normalized_shadow_suffix_router_box::NormalizedShadowSuffixRouterBox;
                // Phase 141 P1.5: Pass prefix variables for external env inputs
                // Clone to avoid borrow checker conflict (self is borrowed mutably in try_lower_loop_suffix)
                let prefix_var_map = self.variable_ctx.variable_map.clone();
                match NormalizedShadowSuffixRouterBox::try_lower_loop_suffix(
                    self, remaining, &func_name, debug, Some(&prefix_var_map)
                )? {
                    Some(consumed) => {
                        trace.emit_if(
                            "debug",
                            "build_block/suffix_router",
                            &format!("Phase 142 P0: Suffix router consumed {} statement(s), continuing to process subsequent statements", consumed),
                            debug,
                        );
                        // Phase 142 P0: Normalization unit is now "statement (loop 1個)"
                        // Loop normalization returns consumed=1, and subsequent statements
                        // (return, assignments, etc.) are handled by normal MIR lowering
                        idx += consumed;
                        // No break - continue processing subsequent statements
                    }
                    None => {
                        // No match, proceed with normal statement build
                    }
                }
            }

            trace.emit_if(
                "debug",
                "build_block",
                &format!(
                    "Statement {}/{}  current_block={:?}  current_function={}",
                    idx + 1,
                    total,
                    self.current_block,
                    self.scope_ctx
                        .current_function
                        .as_ref()
                        .map(|f| f.signature.name.as_str())
                        .unwrap_or("none")
                ),
                trace.is_enabled(),
            );
            last_value = Some(self.build_statement(statements[idx].clone())?);
            idx += 1;

            // If the current block was terminated by this statement (e.g., return/throw),
            // do not emit any further instructions for this block.
            if is_current_block_terminated(self)? {
                trace.emit_if(
                    "debug",
                    "build_block",
                    &format!("Block terminated after statement {}", idx),
                    trace.is_enabled(),
                );
                break;
            }
        }
        let out = last_value.unwrap_or_else(|| {
            // Use ConstantEmissionBox for Void
            crate::mir::builder::emission::constant::emit_void(self)
        });
        // Scope leave only if block not already terminated
        if !self.is_current_block_terminated() {
            self.hint_scope_leave(scope_id);
        }
        trace.emit_if(
            "debug",
            "build_block",
            &format!("Completed, returning value {:?}", out),
            trace.is_enabled(),
        );
        Ok(out)
    }

    /// Build a single statement node.
    ///
    /// Note:
    /// - While/ForRange は将来 Loop lowering へ委譲する拡張ポイントとして扱い、
    ///   現状は他の専用ビルダ／既存パスと同様に build_expression に委譲する。
    ///
    /// Phase 212.5: If statement のサポート追加
    /// - Statement としての If（副作用のみが欲しい）を明示的に処理
    /// - Expression としての If（値を使う）は build_expression 経由のまま
    pub(super) fn build_statement(&mut self, node: ASTNode) -> Result<ValueId, String> {
        // Align current_span to this statement node before lowering expressions under it.
        self.metadata_ctx.set_current_span(node.span());
        match node {
            // Phase 212.5: Statement としての If 処理
            ASTNode::If {
                condition,
                then_body,
                else_body,
                ..
            } => {
                // Statement としての If - 既存 If lowering を呼ぶ
                self.build_if_statement(*condition, then_body, else_body)?;
                // Statement なので値は使わない（Void を返す）
                Ok(crate::mir::builder::emission::constant::emit_void(self))
            }
            // 将来ここに While / ForRange / Match / Using など statement 専用分岐を追加する。
            other => self.build_expression(other),
        }
    }

    /// Phase 212.5: Statement としての If 処理（副作用のみ）
    ///
    /// ループ内 if や top-level statement if はここを通る。
    /// Expression としての if（値を使う場合）は build_expression 経由。
    ///
    /// # Arguments
    /// * `condition` - If の条件式
    /// * `then_body` - then ブロックの statements
    /// * `else_body` - else ブロックの statements (optional)
    ///
    /// # Example
    /// ```hako
    /// if i > 0 {
    ///     sum = sum + 1  // ← Statement としての If
    /// }
    /// ```
    pub(super) fn build_if_statement(
        &mut self,
        condition: ASTNode,
        then_body: Vec<ASTNode>,
        else_body: Option<Vec<ASTNode>>,
    ) -> Result<(), String> {
        use crate::ast::Span;

        // then_body と else_body を ASTNode::Program に変換
        let then_node = ASTNode::Program {
            statements: then_body,
            span: Span::unknown(),
        };
        let else_node = else_body.map(|b| ASTNode::Program {
            statements: b,
            span: Span::unknown(),
        });

        // 既存の If lowering を呼ぶ（cf_if は lower_if_form を呼ぶ）
        // 戻り値は無視（Statement なので値は使わない）
        let _result = self.cf_if(condition, then_node, else_node)?;

        Ok(())
    }

    // Local declarations with optional initializers
    pub(super) fn build_local_statement(
        &mut self,
        variables: Vec<String>,
        initial_values: Vec<Option<Box<ASTNode>>>,
    ) -> Result<ValueId, String> {
        if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
            crate::mir::builder::control_flow::joinir::trace::trace().stderr_if(
                &format!(
                    "[build_local_statement] ENTRY: variables={:?}, initial_values.len()={}",
                    variables,
                    initial_values.len()
                ),
                true,
            );
        }
        let mut last_value = None;
        for (i, var_name) in variables.iter().enumerate() {
            let var_id = if i < initial_values.len() && initial_values[i].is_some() {
                // Evaluate the initializer expression
                let init_expr = initial_values[i].as_ref().unwrap();
                let init_val = self.build_expression(*init_expr.clone())?;

                // FIX: Allocate a new ValueId for this local variable
                // Use next_value_id() which respects function context
                let var_id = self.next_value_id();

                if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
                    crate::mir::builder::control_flow::joinir::trace::trace().stderr_if(
                        &format!(
                            "[build_local_statement] '{}': init_val={:?}, allocated var_id={:?}",
                            var_name, init_val, var_id
                        ),
                        true,
                    );
                }

                self.emit_instruction(crate::mir::MirInstruction::Copy {
                    dst: var_id,
                    src: init_val,
                })?;

                // Propagate metadata (type/origin) from initializer to variable
                crate::mir::builder::metadata::propagate::propagate(self, init_val, var_id);

                var_id
            } else {
                // Create a concrete register for uninitialized locals (Void)
                let void_id = crate::mir::builder::emission::constant::emit_void(self);
                if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
                    crate::mir::builder::control_flow::joinir::trace::trace().stderr_if(
                        &format!(
                            "[build_local_statement] '{}': uninitialized, void_id={:?}",
                            var_name, void_id
                        ),
                        true,
                    );
                }
                void_id
            };

            if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
                crate::mir::builder::control_flow::joinir::trace::trace().stderr_if(
                    &format!(
                        "[build_local_statement] Inserting '{}' -> {:?} into variable_map",
                        var_name, var_id
                    ),
                    true,
                );
            }
            self.declare_local_in_current_scope(var_name, var_id)?;
            // SlotRegistry にもローカル変数スロットを登録しておくよ（観測専用）
            if let Some(reg) = self.comp_ctx.current_slot_registry.as_mut() {
                let ty = self.type_ctx.value_types.get(&var_id).cloned();
                reg.ensure_slot(&var_name, ty);
            }
            last_value = Some(var_id);
        }
        // Phase 135 P0: Use function-level ValueId (SSOT) - build_local_statement is always in function context
        Ok(last_value.unwrap_or_else(|| self.next_value_id()))
    }

    // Return statement
    pub(super) fn build_return_statement(
        &mut self,
        value: Option<Box<ASTNode>>,
    ) -> Result<ValueId, String> {
        // Enforce cleanup policy
        if self.in_cleanup_block && !self.cleanup_allow_return {
            return Err("return is not allowed inside cleanup block (enable NYASH_CLEANUP_ALLOW_RETURN=1 to permit)".to_string());
        }

        let return_value = if let Some(expr) = value {
            self.build_expression(*expr)?
        } else {
            crate::mir::builder::emission::constant::emit_void(self)
        };

        if self.return_defer_active {
            // Defer: copy into slot and jump to target
            if let (Some(slot), Some(target)) = (self.return_defer_slot, self.return_defer_target) {
                self.return_deferred_emitted = true;
                self.emit_instruction(MirInstruction::Copy {
                    dst: slot,
                    src: return_value,
                })?;
                crate::mir::builder::metadata::propagate::propagate(self, return_value, slot);
                if !self.is_current_block_terminated() {
                    crate::mir::builder::emission::branch::emit_jump(self, target)?;
                }
                Ok(return_value)
            } else {
                // Fallback: no configured slot/target; emit a real return
                self.emit_instruction(MirInstruction::Return {
                    value: Some(return_value),
                })?;
                Ok(return_value)
            }
        } else {
            // Normal return
            self.emit_instruction(MirInstruction::Return {
                value: Some(return_value),
            })?;
            Ok(return_value)
        }
    }

    // Nowait: prefer env.future.spawn_instance if method call; else FutureNew
    pub(super) fn build_nowait_statement(
        &mut self,
        variable: String,
        expression: ASTNode,
    ) -> Result<ValueId, String> {
        if let ASTNode::MethodCall {
            object,
            method,
            arguments,
            ..
        } = expression.clone()
        {
            let recv_val = self.build_expression(*object)?;
            let mname_id =
                crate::mir::builder::emission::constant::emit_string(self, method.clone());
            let mut arg_vals: Vec<ValueId> = Vec::with_capacity(2 + arguments.len());
            arg_vals.push(recv_val);
            arg_vals.push(mname_id);
            for a in arguments.into_iter() {
                arg_vals.push(self.build_expression(a)?);
            }
            let future_id = self.next_value_id();
            self.emit_instruction(MirInstruction::ExternCall {
                dst: Some(future_id),
                iface_name: "env.future".to_string(),
                method_name: "spawn_instance".to_string(),
                args: arg_vals,
                effects: crate::mir::effect::EffectMask::PURE.add(crate::mir::effect::Effect::Io),
            })?;
            // Future spawn returns a Future<T>; the inner type is not statically known here.
            // Register at least Future<Unknown> to avoid later fail-fast type inference panics.
            self.type_ctx
                .value_types
                .insert(future_id, MirType::Future(Box::new(MirType::Unknown)));
            self.variable_ctx
                .variable_map
                .insert(variable.clone(), future_id);
            if let Some(reg) = self.comp_ctx.current_slot_registry.as_mut() {
                reg.ensure_slot(&variable, None);
            }
            return Ok(future_id);
        }
        let expression_value = self.build_expression(expression)?;
        let future_id = self.next_value_id();
        self.emit_instruction(MirInstruction::FutureNew {
            dst: future_id,
            value: expression_value,
        })?;
        let inner = self
            .type_ctx
            .value_types
            .get(&expression_value)
            .cloned()
            .unwrap_or(MirType::Unknown);
        self.type_ctx
            .value_types
            .insert(future_id, MirType::Future(Box::new(inner)));
        self.variable_ctx
            .variable_map
            .insert(variable.clone(), future_id);
        if let Some(reg) = self.comp_ctx.current_slot_registry.as_mut() {
            reg.ensure_slot(&variable, None);
        }
        Ok(future_id)
    }

    // Await: insert Safepoint before/after and emit Await
    pub(super) fn build_await_expression(
        &mut self,
        expression: ASTNode,
    ) -> Result<ValueId, String> {
        let future_value = self.build_expression(expression)?;
        self.emit_instruction(MirInstruction::Safepoint)?;
        let result_id = self.next_value_id();
        self.emit_instruction(MirInstruction::Await {
            dst: result_id,
            future: future_value,
        })?;
        let result_type = match self.type_ctx.value_types.get(&future_value) {
            Some(MirType::Future(inner)) => (**inner).clone(),
            _ => MirType::Unknown,
        };
        self.type_ctx.value_types.insert(result_id, result_type);
        self.emit_instruction(MirInstruction::Safepoint)?;
        Ok(result_id)
    }

    /// MeResolverBox - SSOT for "me" resolution
    /// 箱理論: variable_map["me"] のみを参照、文字列フォールバック禁止
    /// Phase 269 P1.2: Removed string constant fallback (Fail-Fast principle)
    pub(super) fn build_me_expression(&mut self) -> Result<ValueId, String> {
        // Phase 269 P1.2: SSOT - variable_map["me"] only (no string fallback)
        const ME_VAR: &str = "me";  // Small constant SSOT

        // Fast path: return if "me" is in variable_map
        if let Some(id) = self.variable_ctx.variable_map.get(ME_VAR).cloned() {
            return Ok(id);
        }

        // ✅ Fail-Fast: "me" must be in variable_map (no string fallback)
        // This is a contract violation - caller must initialize "me" before use

        let function_context = self.scope_ctx.current_function
            .as_ref()
            .map(|f| f.signature.name.clone())
            .unwrap_or_else(|| "unknown".to_string());

        let static_box_context = self.comp_ctx.current_static_box
            .as_ref()
            .map(|s| s.as_str())
            .unwrap_or("none");

        Err(format!(
            "[Phase269/P1.2/MeResolverBox] 'me'/'this' not found in variable_map\n\
             \n\
             Function: {}\n\
             Static box context: {}\n\
             \n\
             This is an **instance method** context error.\n\
             The legacy string constant fallback has been removed (Fail-Fast principle).\n\
             \n\
             Expected: variable_map contains 'me' → Box receiver ValueId (instance method)\n\
             Got: variable_map missing 'me' entry\n\
             \n\
             Possible causes:\n\
             1. Instance method called without proper 'me' initialization\n\
             2. Method called from incorrect context (instance method in static context)\n\
             \n\
             Note: For **static box this.method()** calls, use ReceiverNormalizeBox\n\
             (MethodCall common entry point handles static call normalization).\n\
             \n\
             Hint: Enable NYASH_TRACE_VARMAP=1 to trace variable_map changes.",
            function_context,
            static_box_context
        ))
    }
}
