use super::{MirBuilder, ValueId};
use crate::ast::ASTNode;
use crate::mir::control_form::IfShape;
use crate::mir::loop_api::LoopBuilderApi; // for current_block()
use crate::mir::phi_core::phi_builder_box::PhiBuilderOps;
use crate::mir::{BasicBlockId, ConstValue, MirInstruction};

/// Phase 61-4-F: MirBuilder 用 PhiBuilderOps 実装
///
/// ループ外 if の JoinIR 経路で emit_toplevel_phis() を呼ぶためのラッパー。
struct ToplevelOps<'a>(&'a mut MirBuilder);

impl<'a> PhiBuilderOps for ToplevelOps<'a> {
    fn new_value(&mut self) -> ValueId {
        self.0.next_value_id()
    }

    fn emit_phi(
        &mut self,
        block: BasicBlockId,
        dst: ValueId,
        inputs: Vec<(BasicBlockId, ValueId)>,
    ) -> Result<(), String> {
        // merge ブロックの先頭に PHI を挿入
        if let (Some(func), Some(_cur_bb)) = (
            self.0.scope_ctx.current_function.as_mut(),
            self.0.current_block,
        ) {
            crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
                func,
                block,
                dst,
                inputs,
                self.0.metadata_ctx.current_span(),
            );
        } else {
            self.0.emit_instruction(MirInstruction::Phi {
                dst,
                inputs,
                type_hint: None,
            })?;
        }
        Ok(())
    }

    fn update_var(&mut self, name: String, value: ValueId) {
        self.0.variable_ctx.variable_map.insert(name, value);
    }

    fn get_block_predecessors(&self, block: BasicBlockId) -> Vec<BasicBlockId> {
        if let Some(ref func) = self.0.scope_ctx.current_function {
            func.blocks
                .get(&block)
                .map(|bb| bb.predecessors.iter().copied().collect())
                .unwrap_or_default()
        } else {
            Vec::new()
        }
    }

    fn emit_void(&mut self) -> ValueId {
        let void_id = self.0.next_value_id();
        let _ = self.0.emit_instruction(MirInstruction::Const {
            dst: void_id,
            value: ConstValue::Void,
        });
        void_id
    }

    fn set_current_block(&mut self, block: BasicBlockId) -> Result<(), String> {
        self.0.current_block = Some(block);
        Ok(())
    }

    fn block_exists(&self, block: BasicBlockId) -> bool {
        if let Some(ref func) = self.0.scope_ctx.current_function {
            func.blocks.contains_key(&block)
        } else {
            false
        }
    }
}

impl MirBuilder {
    /// Lower an if/else using a structured IfForm (header→then/else→merge).
    /// PHI-off: edge-copy only on predecessors; PHI-on: Phi at merge.
    pub(super) fn lower_if_form(
        &mut self,
        condition: ASTNode,
        then_branch: ASTNode,
        else_branch: Option<ASTNode>,
    ) -> Result<ValueId, String> {
        // Reserve a deterministic join id for debug region labeling
        let join_id = self.debug_next_join_id();
        // Pre-pin heuristic was deprecated; keep operands as-is for predictability.

        let condition_val = self.build_expression(condition)?;
        let condition_val = self.local_cond(condition_val);

        // Create blocks
        let then_block = self.next_block_id();
        let else_block = self.next_block_id();
        let merge_block = self.next_block_id();

        // Branch
        let pre_branch_bb = self.current_block()?;
        let mut condition_val = condition_val;
        crate::mir::builder::ssa::local::finalize_branch_cond(self, &mut condition_val);
        // Phase 268 P0: emit_conditional() deleted (replaced by emit_conditional_edgecfg() at line 206)

        // Snapshot variables before entering branches
        let pre_if_var_map = self.variable_ctx.variable_map.clone();

        let trace_if = std::env::var("NYASH_IF_TRACE").ok().as_deref() == Some("1");

        // then
        self.start_new_block(then_block)?;
        // Debug region: join then-branch
        self.debug_push_region(format!("join#{}", join_id) + "/then");
        // Scope enter for then-branch
        self.hint_scope_enter(0);
        let then_ast_for_analysis = then_branch.clone();
        self.variable_ctx.variable_map = pre_if_var_map.clone();
        // Materialize all variables at block entry via single-pred Phi (correctness-first)
        for (name, &pre_v) in pre_if_var_map.iter() {
            let phi_val = self.insert_phi_single(pre_branch_bb, pre_v)?;
            self.variable_ctx.variable_map.insert(name.clone(), phi_val);
            if trace_if {
                eprintln!(
                    "[if-trace] then-entry phi var={} pre={:?} -> dst={:?}",
                    name, pre_v, phi_val
                );
            }
        }
        let then_value_raw = self.build_expression(then_branch)?;
        let then_exit_block = self.current_block()?;
        let then_reaches_merge = !self.is_current_block_terminated();
        let then_var_map_end = self.variable_ctx.variable_map.clone();
        if then_reaches_merge {
            // Scope leave for then-branch
            self.hint_scope_leave(0);
            // Phase 268 P0: emit_jump() deleted (handled by emit_conditional_edgecfg())
        }
        // Pop then-branch debug region
        self.debug_pop_region();

        // else
        self.start_new_block(else_block)?;
        // Debug region: join else-branch
        self.debug_push_region(format!("join#{}", join_id) + "/else");
        // Scope enter for else-branch
        self.hint_scope_enter(0);
        let (else_value_raw, else_ast_for_analysis, else_var_map_end_opt) =
            if let Some(else_ast) = else_branch {
                // Reset variable_map BEFORE materializing PHI nodes (same pattern as then-branch)
                self.variable_ctx.variable_map = pre_if_var_map.clone();
                // Materialize all variables at block entry via single-pred Phi (correctness-first)
                for (name, &pre_v) in pre_if_var_map.iter() {
                    let phi_val = self.insert_phi_single(pre_branch_bb, pre_v)?;
                    self.variable_ctx.variable_map.insert(name.clone(), phi_val);
                    if trace_if {
                        eprintln!(
                            "[if-trace] else-entry phi var={} pre={:?} -> dst={:?}",
                            name, pre_v, phi_val
                        );
                    }
                }
                let val = self.build_expression(else_ast.clone())?;
                (
                    val,
                    Some(else_ast),
                    Some(self.variable_ctx.variable_map.clone()),
                )
            } else {
                // No else branch: materialize PHI nodes for the empty else block
                self.variable_ctx.variable_map = pre_if_var_map.clone();
                for (name, &pre_v) in pre_if_var_map.iter() {
                    let phi_val = self.insert_phi_single(pre_branch_bb, pre_v)?;
                    self.variable_ctx.variable_map.insert(name.clone(), phi_val);
                    if trace_if {
                        eprintln!(
                            "[if-trace] else-entry phi var={} pre={:?} -> dst={:?}",
                            name, pre_v, phi_val
                        );
                    }
                }
                let void_val = crate::mir::builder::emission::constant::emit_void(self);
                // Phase 25.1c/k: Pass PHI-renamed variable_map for empty else branch
                // This ensures merge_modified_vars uses correct ValueIds after PHI renaming
                (void_val, None, Some(self.variable_ctx.variable_map.clone()))
            };
        let else_exit_block = self.current_block()?;
        let else_reaches_merge = !self.is_current_block_terminated();
        if else_reaches_merge {
            // Scope leave for else-branch
            self.hint_scope_leave(0);
            // Phase 268 P0: emit_jump() deleted (handled by emit_conditional_edgecfg())
        }
        // Pop else-branch debug region
        self.debug_pop_region();

        // Phase 268 P0: EdgeCFG Fragment ベース emit（emission 層経由）
        crate::mir::builder::emission::branch::emit_conditional_edgecfg(
            self,
            pre_branch_bb,
            condition_val,
            then_block,
            then_exit_block,
            then_reaches_merge,
            else_block,
            else_exit_block,
            else_reaches_merge,
            merge_block,
        )?;

        // merge: primary result via helper, then delta-based variable merges
        // Ensure PHIs are first in the block by suppressing entry pin copies here
        self.suppress_next_entry_pin_copy();
        self.start_new_block(merge_block)?;
        // Debug region: join merge
        self.debug_push_region(format!("join#{}", join_id) + "/join");
        self.push_if_merge(merge_block);

        // Phase 38: Pre-analysis hints removed (JoinIR AST lowering handles assignment detection)
        let assigned_then_pre: Option<String> = None;
        let assigned_else_pre: Option<String> = None;
        let pre_then_var_value: Option<ValueId> = None;

        // Phase 61-4: JoinIR 経路試行（ループ外 If）
        //
        // - 実際の有効化判定は config::env 側のポリシー関数に集約する。
        let joinir_enabled = crate::config::env::joinir_if_select_enabled();
        let joinir_toplevel = crate::config::env::joinir_if_toplevel_enabled();
        let joinir_dryrun = crate::config::env::joinir_if_toplevel_dryrun_enabled();
        let mut joinir_success = false;

        // 関数名ガードチェック
        let func_name = self
            .scope_ctx
            .current_function
            .as_ref()
            .map(|f| f.signature.name.as_str())
            .unwrap_or("");
        let is_target = crate::mir::join_ir::lowering::is_joinir_if_toplevel_target(func_name);

        // Phase 80: Core ON 時は代表関数で JoinIR を本線として試行
        let core_mainline =
            crate::mir::join_ir::lowering::should_try_joinir_mainline(func_name, false);
        let strict_mode =
            crate::mir::join_ir::lowering::should_panic_on_joinir_failure(func_name, false);

        // Core ON + 本線対象の場合は環境変数に関わらず試行
        let should_try_joinir =
            core_mainline || (joinir_enabled && is_target && (joinir_toplevel || joinir_dryrun));

        if should_try_joinir {
            if let Some(ref func) = self.scope_ctx.current_function {
                let context =
                    crate::mir::join_ir::lowering::if_phi_context::IfPhiContext::pure_if();

                match crate::mir::join_ir::lowering::try_lower_if_to_joinir(
                    func,
                    pre_branch_bb,
                    false,
                    Some(&context),
                ) {
                    Some(join_inst) => {
                        if joinir_dryrun || joinir_toplevel {
                            eprintln!(
                                "[Phase 61-4] ✅ Toplevel If lowered via JoinIR ({}): {:?}",
                                func_name, join_inst
                            );
                        }

                        // PhiSpec 計算
                        let phi_spec = crate::mir::join_ir::lowering::if_phi_spec::compute_phi_spec_from_joinir(&context, &join_inst);

                        if joinir_dryrun {
                            eprintln!(
                                "[Phase 61-4] 🔍 dry-run: JoinIR PhiSpec header={}, exit={}",
                                phi_spec.header_count(),
                                phi_spec.exit_count()
                            );
                        }

                        // Phase 61-4-F: 本番経路 - emit_toplevel_phis でPHI生成
                        if joinir_toplevel {
                            // IfShape 構築
                            let if_shape = IfShape {
                                cond_block: pre_branch_bb,
                                then_block,
                                else_block: Some(else_block),
                                merge_block,
                            };

                            // PHI 生成
                            let phi_count = {
                                let mut ops = ToplevelOps(self);
                                crate::mir::if_in_loop_phi::IfInLoopPhiEmitter::emit_toplevel_phis(
                                    &phi_spec,
                                    &pre_if_var_map,
                                    &then_var_map_end,
                                    else_var_map_end_opt.as_ref(),
                                    &mut ops,
                                    &if_shape,
                                )?
                            };

                            if joinir_dryrun {
                                eprintln!(
                                    "[Phase 61-4] ✅ Production path: {} PHIs generated via JoinIR",
                                    phi_count
                                );
                            }

                            joinir_success = true;
                        }
                    }
                    None => {
                        if joinir_dryrun {
                            eprintln!(
                                "[Phase 61-4] ⏭️ JoinIR pattern not matched for {}, using fallback",
                                func_name
                            );
                        }
                        // Phase 80/81: Strict mode では本線対象関数の失敗でパニック
                        if strict_mode {
                            panic!(
                                "[joinir/if] strict mode: pattern not matched for {} (if_form.rs)",
                                func_name
                            );
                        }
                    }
                }
            }
        }

        // Phase 61-4: JoinIR成功時はスキップ、失敗時は既存経路
        let result_val = if joinir_success {
            // JoinIR 経路（未実装 - フォールバック）
            crate::mir::builder::emission::constant::emit_void(self)
        } else {
            self.normalize_if_else_phi(
                then_block,
                else_block,
                if then_reaches_merge {
                    Some(then_exit_block)
                } else {
                    None
                },
                if else_reaches_merge {
                    Some(else_exit_block)
                } else {
                    None
                },
                then_value_raw,
                else_value_raw,
                &pre_if_var_map,
                &then_ast_for_analysis,
                &else_ast_for_analysis,
                &then_var_map_end,
                &else_var_map_end_opt,
                pre_then_var_value,
            )?
        };

        // Hint: join result variable(s)
        // 1) Primary: if both branches assign to the same variable name, emit a hint for that name
        if let (Some(tn), Some(en)) = (assigned_then_pre.as_deref(), assigned_else_pre.as_deref()) {
            if tn == en {
                self.hint_join_result(tn);
            }
        }
        // 2) Secondary: if both branches assign multiple variables, hint全件（制限なし）
        if let Some(ref else_map_end) = else_var_map_end_opt {
            for name in then_var_map_end.keys() {
                if Some(name.as_str()) == assigned_then_pre.as_deref() {
                    continue;
                }
                if else_map_end.contains_key(name) {
                    self.hint_join_result(name.as_str());
                }
            }
        }

        // Merge other modified variables (skip the primary assignment if any)
        if !joinir_success {
            let skip_name = assigned_then_pre.as_deref();
            self.merge_modified_vars(
                then_block,
                else_block,
                if then_reaches_merge {
                    Some(then_exit_block)
                } else {
                    None
                },
                if else_reaches_merge {
                    Some(else_exit_block)
                } else {
                    None
                },
                &pre_if_var_map,
                &then_var_map_end,
                &else_var_map_end_opt,
                skip_name,
            )?;
        }

        self.pop_if_merge();
        // Pop merge debug region
        self.debug_pop_region();
        Ok(result_val)
    }
}
