use super::MirBuilder;
use crate::ast::ASTNode;
use crate::mir::{BasicBlockId, MirInstruction, ValueId};
use std::collections::BTreeMap; // Phase 25.1: 決定性確保

// Phase 84-5: if_phi.rs deleted, type inference now handled by GenericTypeResolver/PhiTypeResolver

impl MirBuilder {
    /// Merge all variables modified in then/else relative to pre_if_snapshot.
    /// In PHI-off mode inserts edge copies from branch exits to merge. In PHI-on mode emits Phi.
    /// `skip_var` allows skipping a variable already merged elsewhere (e.g., bound to an expression result).
    pub(super) fn merge_modified_vars(
        &mut self,
        _then_block: super::BasicBlockId,
        _else_block: super::BasicBlockId,
        then_exit_block_opt: Option<super::BasicBlockId>,
        else_exit_block_opt: Option<super::BasicBlockId>,
        pre_if_snapshot: &BTreeMap<String, super::ValueId>, // Phase 25.1: BTreeMap化
        then_map_end: &BTreeMap<String, super::ValueId>,    // Phase 25.1: BTreeMap化
        else_map_end_opt: &Option<BTreeMap<String, super::ValueId>>, // Phase 25.1: BTreeMap化
        skip_var: Option<&str>,
    ) -> Result<(), String> {
        // 📦 Phase 25.1q: Use PhiMergeHelper for unified PHI insertion
        // 📦 Phase 57: ConservativeMerge 冗長呼び出し削除
        //    - 以前: ここで ConservativeMerge::analyze を呼び、merge_all_vars 内でも呼んでいた（2回）
        //    - 現在: merge_all_vars が changed_vars を返すので、1回で済む
        use std::collections::HashSet;

        // Use PhiMergeHelper for unified variable merging
        let mut helper =
            super::phi_merge::PhiMergeHelper::new(self, then_exit_block_opt, else_exit_block_opt);
        let changed_set: HashSet<String> =
            helper.merge_all_vars(pre_if_snapshot, then_map_end, else_map_end_opt, skip_var)?;

        // Ensure pinned synthetic slots ("__pin$...") have a block-local definition at the merge,
        // even if their values did not change across branches. This avoids undefined uses when
        // subsequent blocks re-use pinned values without modifications.
        for (pin_name, pre_val) in pre_if_snapshot.iter() {
            if !pin_name.starts_with("__pin$") {
                continue;
            }
            if skip_var.map(|s| s == pin_name.as_str()).unwrap_or(false) {
                continue;
            }
            if changed_set.contains(pin_name) {
                continue;
            }
            let then_v = then_map_end
                .get(pin_name.as_str())
                .copied()
                .unwrap_or(*pre_val);
            let else_v = else_map_end_opt
                .as_ref()
                .and_then(|m| m.get(pin_name.as_str()).copied())
                .unwrap_or(*pre_val);
            let mut inputs: Vec<(super::BasicBlockId, super::ValueId)> = Vec::new();
            if let Some(tp) = then_exit_block_opt {
                inputs.push((tp, then_v));
            }
            if let Some(ep) = else_exit_block_opt {
                inputs.push((ep, else_v));
            }
            match inputs.len() {
                0 => {}
                1 => {
                    let (_pred, v) = inputs[0];
                    self.variable_ctx.variable_map.insert(pin_name.clone(), v);
                }
                _ => {
                    if let Some(func) = self.scope_ctx.current_function.as_mut() {
                        func.update_cfg();
                    }
                    if let (Some(func), Some(cur_bb)) =
                        (&self.scope_ctx.current_function, self.current_block)
                    {
                        crate::mir::phi_core::common::debug_verify_phi_inputs(
                            func, cur_bb, &inputs,
                        );
                    }
                    let merged = self.next_value_id();
                    if let (Some(func), Some(cur_bb)) =
                        (self.scope_ctx.current_function.as_mut(), self.current_block)
                    {
                        crate::mir::ssot::cf_common::insert_phi_at_head_spanned(
                            func,
                            cur_bb,
                            merged,
                            inputs,
                            self.metadata_ctx.current_span(),
                        );
                    } else {
                        self.emit_instruction(MirInstruction::Phi {
                            dst: merged,
                            inputs,
                            type_hint: None, // Phase 63-6: Legacy path, no type hint
                        })?;
                    }
                    self.variable_ctx
                        .variable_map
                        .insert(pin_name.clone(), merged);
                }
            }
        }
        Ok(())
    }
    /// Normalize Phi creation for if/else constructs.
    /// This handles variable reassignment patterns and ensures a single exit value.
    pub(super) fn normalize_if_else_phi(
        &mut self,
        _then_block: BasicBlockId,
        _else_block: BasicBlockId,
        then_exit_block_opt: Option<BasicBlockId>,
        else_exit_block_opt: Option<BasicBlockId>,
        then_value_raw: ValueId,
        else_value_raw: ValueId,
        pre_if_var_map: &BTreeMap<String, ValueId>, // Phase 25.1: BTreeMap化
        _then_ast_for_analysis: &ASTNode,
        _else_ast_for_analysis: &Option<ASTNode>,
        then_var_map_end: &BTreeMap<String, ValueId>, // Phase 25.1: BTreeMap化
        else_var_map_end_opt: &Option<BTreeMap<String, ValueId>>, // Phase 25.1: BTreeMap化
        pre_then_var_value: Option<ValueId>,
    ) -> Result<ValueId, String> {
        // If only the then-branch assigns a variable (e.g., `if c { x = ... }`) and the else
        // does not assign the same variable, bind that variable to a Phi of (then_value, pre_if_value).
        // Phase 38: Pre-analysis removed (JoinIR AST lowering handles assignment detection)
        let assigned_var_then: Option<String> = None;
        let assigned_var_else: Option<String> = None;
        let result_val = self.next_value_id();

        // フェーズM: no_phi_mode分岐削除（常にPHI命令を使用）

        if let Some(var_name) = assigned_var_then.clone() {
            let else_assigns_same = assigned_var_else
                .as_ref()
                .map(|s| s == &var_name)
                .unwrap_or(false);
            // Resolve branch-end values for the assigned variable
            let then_value_for_var = then_var_map_end
                .get(&var_name)
                .copied()
                .unwrap_or(then_value_raw);
            // Check if else branch actually modified the variable (even if not as last expression)
            let else_modified_var = else_var_map_end_opt
                .as_ref()
                .and_then(|m| m.get(&var_name).copied());
            let else_value_for_var = if else_assigns_same {
                else_var_map_end_opt
                    .as_ref()
                    .and_then(|m| m.get(&var_name).copied())
                    .unwrap_or(else_value_raw)
            } else if let Some(else_modified) = else_modified_var {
                // Else modifies the variable (even if not as the last expression)
                else_modified
            } else {
                // Else doesn't modify the variable: use pre-if value if available
                pre_then_var_value.unwrap_or(else_value_raw)
            };
            // Build inputs from reachable predecessors only
            let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
            if let Some(tp) = then_exit_block_opt {
                inputs.push((tp, then_value_for_var));
            }
            if let Some(ep) = else_exit_block_opt {
                inputs.push((ep, else_value_for_var));
            }
            match inputs.len() {
                0 => {}
                1 => {
                    // Direct bind (no PHI needed)
                    self.variable_ctx.variable_map = pre_if_var_map.clone();
                    self.variable_ctx.variable_map.insert(var_name, inputs[0].1);
                    return Ok(inputs[0].1);
                }
                _ => {
                    if let Some(func) = self.scope_ctx.current_function.as_mut() {
                        func.update_cfg();
                    }
                    if let (Some(func), Some(cur_bb)) =
                        (&self.scope_ctx.current_function, self.current_block)
                    {
                        crate::mir::phi_core::common::debug_verify_phi_inputs(
                            func, cur_bb, &inputs,
                        );
                    }
                    self.insert_phi_with_dst(result_val, inputs)?;
                }
            }
            self.variable_ctx.variable_map = pre_if_var_map.clone();
            self.variable_ctx.variable_map.insert(var_name, result_val);
        } else {
            // No variable assignment pattern detected – just emit Phi for expression result
            let mut inputs: Vec<(BasicBlockId, ValueId)> = Vec::new();
            if let Some(tp) = then_exit_block_opt {
                inputs.push((tp, then_value_raw));
            }
            if let Some(ep) = else_exit_block_opt {
                inputs.push((ep, else_value_raw));
            }
            match inputs.len() {
                0 => {
                    /* leave result_val as fresh, but unused; synthesize void */
                    let v = crate::mir::builder::emission::constant::emit_void(self);
                    return Ok(v);
                }
                1 => {
                    return Ok(inputs[0].1);
                }
                _ => {
                    if let Some(func) = self.scope_ctx.current_function.as_mut() {
                        func.update_cfg();
                    }
                    if let (Some(func), Some(cur_bb)) =
                        (&self.scope_ctx.current_function, self.current_block)
                    {
                        crate::mir::phi_core::common::debug_verify_phi_inputs(
                            func, cur_bb, &inputs,
                        );
                    }
                    self.insert_phi_with_dst(result_val, inputs)?;
                }
            }
            // Merge variable map conservatively to pre-if snapshot (no new bindings)
            self.variable_ctx.variable_map = pre_if_var_map.clone();
        }

        Ok(result_val)
    }
}
