use super::helpers::{create_phi_bindings, LoopBlocksWithIfPhi};
use super::{CoreEffectPlan, CoreLoopPlan, CorePhiInfo, CorePlan, Pattern3IfPhiPlan};
use crate::mir::basic_block::EdgeArgs;
use crate::mir::builder::control_flow::edgecfg::api::{BranchStub, EdgeStub, ExitKind, Frag};
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
use crate::mir::builder::MirBuilder;
use crate::mir::join_ir::lowering::inline_boundary::JumpArgsLayout;
use crate::mir::MirType;
use std::collections::BTreeMap;

impl super::PlanNormalizer {
    /// Phase 286 P2.6.1: Pattern3IfPhi → CorePlan 変換
    ///
    /// Expands Pattern3 (Loop with If-Phi) semantics into generic CorePlan:
    /// - CFG structure: preheader → header → body → then/else → merge → step → header
    /// - 3 PHIs: 2 in header (loop_var, carrier), 1 in merge (carrier_next)
    /// - If-else branching with PHI merge (no Select instruction)
    pub(super) fn normalize_pattern3_if_phi(
        builder: &mut MirBuilder,
        parts: Pattern3IfPhiPlan,
        ctx: &LoopPatternContext,
    ) -> Result<CorePlan, String> {
        use crate::mir::builder::control_flow::joinir::trace;

        let trace_logger = trace::trace();
        let debug = ctx.debug;

        if debug {
            trace_logger.debug(
                "normalizer/pattern3_if_phi",
                &format!(
                    "Phase 286 P2.6.1: Normalizing Pattern3IfPhi for {} (loop_var: {}, carrier_var: {})",
                    ctx.func_name, parts.loop_var, parts.carrier_var
                ),
            );
        }

        // Step 1: Get host ValueIds for variables
        let loop_var_init = builder
            .variable_ctx
            .variable_map
            .get(&parts.loop_var)
            .copied()
            .ok_or_else(|| format!("[normalizer] Loop variable {} not found", parts.loop_var))?;

        let carrier_init = builder
            .variable_ctx
            .variable_map
            .get(&parts.carrier_var)
            .copied()
            .ok_or_else(|| format!("[normalizer] Carrier variable {} not found", parts.carrier_var))?;

        // Step 2-3: Allocate BasicBlockIds
        let blocks = LoopBlocksWithIfPhi::allocate(builder)?;
        let LoopBlocksWithIfPhi {
            preheader_bb,
            header_bb,
            body_bb,
            then_bb,
            else_bb,
            merge_bb,
            step_bb,
            after_bb,
        } = blocks;

        if debug {
            trace_logger.debug(
                "normalizer/pattern3_if_phi",
                &format!(
                    "Allocated: preheader={:?}, header={:?}, body={:?}, then={:?}, else={:?}, merge={:?}, step={:?}, after={:?}",
                    preheader_bb,
                    header_bb,
                    body_bb,
                    then_bb,
                    else_bb,
                    merge_bb,
                    step_bb,
                    after_bb
                ),
            );
        }

        // Step 4: Allocate ValueIds for loop control
        let loop_var_current = builder.alloc_typed(MirType::Integer);
        let carrier_current = builder.alloc_typed(MirType::Integer);
        let cond_loop = builder.alloc_typed(MirType::Bool);
        let cond_if = builder.alloc_typed(MirType::Bool);
        let carrier_then = builder.alloc_typed(MirType::Integer);
        let carrier_else = builder.alloc_typed(MirType::Integer);
        let carrier_next = builder.alloc_typed(MirType::Integer);
        let loop_var_next = builder.alloc_typed(MirType::Integer);

        // Step 5: Build phi_bindings
        let phi_bindings = create_phi_bindings(&[
            (&parts.loop_var, loop_var_current),
            (&parts.carrier_var, carrier_current),
        ]);

        // Step 6: Lower AST expressions
        let (loop_cond_lhs, loop_cond_op, loop_cond_rhs, loop_cond_consts) =
            Self::lower_compare_ast(&parts.condition, builder, &phi_bindings)?;

        let (if_cond_lhs, if_cond_op, if_cond_rhs, if_cond_consts) =
            Self::lower_compare_ast(&parts.if_condition, builder, &phi_bindings)?;

        let (then_lhs, then_op, then_rhs, then_consts) =
            Self::lower_binop_ast(&parts.then_update, builder, &phi_bindings)?;

        let (else_lhs, else_op, else_rhs, else_consts) =
            Self::lower_binop_ast(&parts.else_update, builder, &phi_bindings)?;

        let (loop_inc_lhs, loop_inc_op, loop_inc_rhs, loop_inc_consts) =
            Self::lower_binop_ast(&parts.loop_increment, builder, &phi_bindings)?;

        // Step 7: Build header_effects
        let mut header_effects = loop_cond_consts;
        header_effects.push(CoreEffectPlan::Compare {
            dst: cond_loop,
            lhs: loop_cond_lhs,
            op: loop_cond_op,
            rhs: loop_cond_rhs,
        });

        // Step 8: Build body plans
        let mut body_plans: Vec<CorePlan> = Vec::new();
        for const_effect in if_cond_consts {
            body_plans.push(CorePlan::Effect(const_effect));
        }
        body_plans.push(CorePlan::Effect(CoreEffectPlan::Compare {
            dst: cond_if,
            lhs: if_cond_lhs,
            op: if_cond_op,
            rhs: if_cond_rhs,
        }));

        // Step 9: Build then_effects
        let mut then_effects = then_consts;
        then_effects.push(CoreEffectPlan::BinOp {
            dst: carrier_then,
            lhs: then_lhs,
            op: then_op,
            rhs: then_rhs,
        });

        // Step 10: Build else_effects
        let mut else_effects = else_consts;
        else_effects.push(CoreEffectPlan::BinOp {
            dst: carrier_else,
            lhs: else_lhs,
            op: else_op,
            rhs: else_rhs,
        });

        // Step 11: Build step_effects
        let mut step_effects = loop_inc_consts;
        step_effects.push(CoreEffectPlan::BinOp {
            dst: loop_var_next,
            lhs: loop_inc_lhs,
            op: loop_inc_op,
            rhs: loop_inc_rhs,
        });

        // Step 12: Build block_effects
        let block_effects = vec![
            (preheader_bb, vec![]),
            (header_bb, header_effects),
            (body_bb, vec![]),
            (then_bb, then_effects),
            (else_bb, else_effects),
            (merge_bb, vec![]),
            (step_bb, step_effects),
        ];

        // Step 13: Build PHIs
        let phis = vec![
            CorePhiInfo {
                block: header_bb,
                dst: loop_var_current,
                inputs: vec![(preheader_bb, loop_var_init), (step_bb, loop_var_next)],
                tag: format!("loop_var_{}", parts.loop_var),
            },
            CorePhiInfo {
                block: header_bb,
                dst: carrier_current,
                inputs: vec![(preheader_bb, carrier_init), (step_bb, carrier_next)],
                tag: format!("carrier_{}", parts.carrier_var),
            },
            CorePhiInfo {
                block: merge_bb,
                dst: carrier_next,
                inputs: vec![(then_bb, carrier_then), (else_bb, carrier_else)],
                tag: format!("merge_{}", parts.carrier_var),
            },
        ];

        // Step 14: Build Frag
        let empty_args = EdgeArgs {
            layout: JumpArgsLayout::CarriersOnly,
            values: vec![],
        };

        let branches = vec![
            BranchStub {
                from: header_bb,
                cond: cond_loop,
                then_target: body_bb,
                then_args: empty_args.clone(),
                else_target: after_bb,
                else_args: empty_args.clone(),
            },
            BranchStub {
                from: body_bb,
                cond: cond_if,
                then_target: then_bb,
                then_args: empty_args.clone(),
                else_target: else_bb,
                else_args: empty_args.clone(),
            },
        ];

        let wires = vec![
            EdgeStub {
                from: then_bb,
                kind: ExitKind::Normal,
                target: Some(merge_bb),
                args: empty_args.clone(),
            },
            EdgeStub {
                from: else_bb,
                kind: ExitKind::Normal,
                target: Some(merge_bb),
                args: empty_args.clone(),
            },
            EdgeStub {
                from: merge_bb,
                kind: ExitKind::Normal,
                target: Some(step_bb),
                args: empty_args.clone(),
            },
            EdgeStub {
                from: step_bb,
                kind: ExitKind::Normal,
                target: Some(header_bb),
                args: empty_args.clone(),
            },
        ];

        let frag = Frag {
            entry: header_bb,
            exits: BTreeMap::new(),
            wires,
            branches,
        };

        // Step 15: Build final_values
        let final_values = vec![
            (parts.loop_var.clone(), loop_var_current),
            (parts.carrier_var.clone(), carrier_current),
        ];

        // Step 16: Build CoreLoopPlan
        let loop_plan = CoreLoopPlan {
            preheader_bb,
            header_bb,
            body_bb,
            step_bb,
            after_bb,
            found_bb: after_bb,
            body: body_plans,
            cond_loop,
            cond_match: cond_if,
            block_effects,
            phis,
            frag,
            final_values,
        };

        if debug {
            trace_logger.debug(
                "normalizer/pattern3_if_phi",
                "CorePlan construction complete (7 blocks, 3 PHIs)",
            );
        }

        Ok(CorePlan::Loop(loop_plan))
    }
}
