use super::helpers::create_phi_bindings;
use super::{
    CoreEffectPlan, CoreLoopPlan, CorePhiInfo, CorePlan, Pattern5ExitKind,
    Pattern5InfiniteEarlyExitPlan,
};
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::{ConstValue, MirType, ValueId};
use std::collections::BTreeMap;

impl super::PlanNormalizer {
    /// Pattern5InfiniteEarlyExit → CorePlan 変換 (Phase 286 P3.2)
    ///
    /// Expands infinite loop with early exit into CorePlan.
    ///
    /// # Return version CFG (5 blocks)
    /// ```text
    /// preheader → header(PHI: i_current) → body(exit_cond)
    ///               ↑                           ↓
    ///               └───── step ←────────  else path
    ///                                           ↓
    ///                                then path: Return (found_bb)
    /// ```
    ///
    /// # Break version CFG (6 blocks)
    /// ```text
    /// preheader → header(PHI: i, carrier) → body(exit_cond)
    ///               ↑                             ↓
    ///               └───── step ←──────────  else path
    ///                                             ↓
    ///                                  then path → after_bb(PHI: carrier_out)
    /// ```
    pub(super) fn normalize_pattern5_infinite_early_exit(
        builder: &mut MirBuilder,
        parts: Pattern5InfiniteEarlyExitPlan,
        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/pattern5_infinite_early_exit",
                &format!(
                    "Phase 286 P3.2: Normalizing Pattern5 for {} (loop_var={}, exit_kind={:?})",
                    ctx.func_name, parts.loop_var, parts.exit_kind
                ),
            );
        }

        match parts.exit_kind {
            Pattern5ExitKind::Return => {
                Self::normalize_pattern5_return(builder, parts, ctx, debug, &trace_logger)
            }
            Pattern5ExitKind::Break => {
                Self::normalize_pattern5_break(builder, parts, ctx, debug, &trace_logger)
            }
        }
    }

    /// Pattern5 Return version normalizer
    ///
    /// CFG: preheader → header → body → found_bb (return) / step → header
    fn normalize_pattern5_return(
        builder: &mut MirBuilder,
        parts: Pattern5InfiniteEarlyExitPlan,
        _ctx: &LoopPatternContext,
        debug: bool,
        trace_logger: &crate::mir::builder::control_flow::joinir::trace::JoinLoopTrace,
    ) -> Result<CorePlan, String> {
        // Step 1: Block allocation (5 blocks)
        let preheader_bb = builder
            .current_block
            .ok_or_else(|| "Pattern5Return: no current block".to_string())?;
        let header_bb = builder.next_block_id();
        let body_bb = builder.next_block_id();
        let found_bb = builder.next_block_id();
        let step_bb = builder.next_block_id();
        let after_bb = builder.next_block_id();

        if debug {
            trace_logger.debug(
                "normalizer/pattern5_return",
                &format!(
                    "Block allocation: preheader={:?}, header={:?}, body={:?}, found={:?}, step={:?}, after={:?}",
                    preheader_bb, header_bb, body_bb, found_bb, step_bb, after_bb
                ),
            );
        }

        // Step 2: Get initial values from variable_map
        let loop_var_init = builder
            .variable_ctx
            .variable_map
            .get(&parts.loop_var)
            .copied()
            .ok_or_else(|| {
                format!(
                    "Pattern5Return: loop_var '{}' not in variable_map",
                    parts.loop_var
                )
            })?;

        // Step 3: ValueId allocation
        let loop_var_current = builder.alloc_typed(MirType::Integer);
        let cond_exit = builder.alloc_typed(MirType::Bool);
        let loop_var_next = builder.alloc_typed(MirType::Integer);
        let true_val = builder.alloc_typed(MirType::Bool);

        // Step 4: phi_bindings for AST lowering
        let phi_bindings = create_phi_bindings(&[(&parts.loop_var, loop_var_current)]);

        // Step 5: Lower AST expressions
        let (exit_cond_lhs, exit_cond_op, exit_cond_rhs, exit_cond_consts) =
            Self::lower_compare_ast(&parts.exit_condition, builder, &phi_bindings)?;

        let return_value: Option<(ValueId, crate::ast::ASTNode)> =
            if let Some(ref exit_val_ast) = parts.exit_value {
            let return_val_id = builder.alloc_typed(MirType::Integer);
            let return_val_ast = exit_val_ast.clone();
            Some((return_val_id, return_val_ast))
        } else {
            None
        };

        let (inc_lhs, inc_op, inc_rhs, inc_consts) =
            Self::lower_binop_ast(&parts.loop_increment, builder, &phi_bindings)?;

        // Step 6: Build header_effects
        let header_effects = vec![CoreEffectPlan::Const {
            dst: true_val,
            value: ConstValue::Bool(true),
        }];

        // Step 7: Build body plans
        let mut body_plans: Vec<CorePlan> = Vec::new();
        for eff in exit_cond_consts {
            body_plans.push(CorePlan::Effect(eff));
        }
        body_plans.push(CorePlan::Effect(CoreEffectPlan::Compare {
            dst: cond_exit,
            lhs: exit_cond_lhs,
            op: exit_cond_op,
            rhs: exit_cond_rhs,
        }));

        // Step 8: Build found_bb effects (return value computation + Return)
        let mut found_effects: Vec<CoreEffectPlan> = Vec::new();
        let return_val_id = if let Some((ret_val_id, ref ret_val_ast)) = return_value {
            if let crate::ast::ASTNode::Literal {
                value: crate::ast::LiteralValue::Integer(n),
                ..
            } = ret_val_ast
            {
                found_effects.push(CoreEffectPlan::Const {
                    dst: ret_val_id,
                    value: ConstValue::Integer(*n),
                });
            } else {
                let (lhs, op, rhs, consts) =
                    Self::lower_binop_ast(ret_val_ast, builder, &phi_bindings)?;
                for c in consts {
                    found_effects.push(c);
                }
                found_effects.push(CoreEffectPlan::BinOp {
                    dst: ret_val_id,
                    lhs,
                    op,
                    rhs,
                });
            }
            Some(ret_val_id)
        } else {
            None
        };

        // Step 9: Build step_effects (increment loop var)
        let mut step_effects = inc_consts;
        step_effects.push(CoreEffectPlan::BinOp {
            dst: loop_var_next,
            lhs: inc_lhs,
            op: inc_op,
            rhs: inc_rhs,
        });

        // Step 10: 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_carrier_{}", parts.loop_var),
        }];

        // Step 11: Build block_effects
        let block_effects = vec![
            (preheader_bb, vec![]),
            (header_bb, header_effects),
            (body_bb, vec![]),
            (found_bb, found_effects),
            (step_bb, step_effects),
            (after_bb, vec![]),
        ];

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

        let branches = vec![
            BranchStub {
                from: header_bb,
                cond: true_val,
                then_target: body_bb,
                else_target: after_bb,
                then_args: empty_args.clone(),
                else_args: empty_args.clone(),
            },
            BranchStub {
                from: body_bb,
                cond: cond_exit,
                then_target: found_bb,
                else_target: step_bb,
                then_args: empty_args.clone(),
                else_args: empty_args.clone(),
            },
        ];

        let return_args = EdgeArgs {
            layout: JumpArgsLayout::CarriersOnly,
            values: if let Some(val_id) = return_val_id {
                vec![val_id]
            } else {
                vec![]
            },
        };

        let wires = vec![
            EdgeStub {
                from: step_bb,
                kind: ExitKind::Normal,
                target: Some(header_bb),
                args: empty_args.clone(),
            },
            EdgeStub {
                from: found_bb,
                kind: ExitKind::Return,
                target: None,
                args: return_args,
            },
            EdgeStub {
                from: after_bb,
                kind: ExitKind::Return,
                target: None,
                args: empty_args.clone(),
            },
        ];

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

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

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

        if debug {
            trace_logger.debug(
                "normalizer/pattern5_return",
                "CorePlan construction complete (5 blocks, 1 PHI, Return exit)",
            );
        }

        Ok(CorePlan::Loop(loop_plan))
    }

    /// Pattern5 Break version normalizer
    ///
    /// CFG: preheader → header → body → break_then → after / step → header
    fn normalize_pattern5_break(
        builder: &mut MirBuilder,
        parts: Pattern5InfiniteEarlyExitPlan,
        _ctx: &LoopPatternContext,
        debug: bool,
        trace_logger: &crate::mir::builder::control_flow::joinir::trace::JoinLoopTrace,
    ) -> Result<CorePlan, String> {
        // Step 1: Block allocation (6 blocks)
        let preheader_bb = builder
            .current_block
            .ok_or_else(|| "Pattern5Break: no current block".to_string())?;
        let header_bb = builder.next_block_id();
        let body_bb = builder.next_block_id();
        let break_then_bb = builder.next_block_id();
        let step_bb = builder.next_block_id();
        let after_bb = builder.next_block_id();

        if debug {
            trace_logger.debug(
                "normalizer/pattern5_break",
                &format!(
                    "Block allocation: preheader={:?}, header={:?}, body={:?}, break_then={:?}, step={:?}, after={:?}",
                    preheader_bb, header_bb, body_bb, break_then_bb, step_bb, after_bb
                ),
            );
        }

        // Step 2: Get carrier_var (required for Break version)
        let carrier_var = parts
            .carrier_var
            .as_ref()
            .ok_or_else(|| "Pattern5Break: carrier_var required for Break version".to_string())?;
        let carrier_update_ast = parts
            .carrier_update
            .as_ref()
            .ok_or_else(|| "Pattern5Break: carrier_update required for Break version".to_string())?;

        // Step 3: Get initial values from variable_map
        let loop_var_init = builder
            .variable_ctx
            .variable_map
            .get(&parts.loop_var)
            .copied()
            .ok_or_else(|| {
                format!(
                    "Pattern5Break: loop_var '{}' not in variable_map",
                    parts.loop_var
                )
            })?;

        let carrier_init = builder
            .variable_ctx
            .variable_map
            .get(carrier_var)
            .copied()
            .ok_or_else(|| {
                format!(
                    "Pattern5Break: carrier_var '{}' not in variable_map",
                    carrier_var
                )
            })?;

        // Step 4: ValueId allocation
        let loop_var_current = builder.alloc_typed(MirType::Integer);
        let carrier_current = builder.alloc_typed(MirType::Integer);
        let cond_exit = builder.alloc_typed(MirType::Bool);
        let carrier_step = builder.alloc_typed(MirType::Integer);
        let loop_var_next = builder.alloc_typed(MirType::Integer);
        let carrier_out = builder.alloc_typed(MirType::Integer);
        let true_val = builder.alloc_typed(MirType::Bool);

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

        // Step 6: Lower AST expressions
        let (exit_cond_lhs, exit_cond_op, exit_cond_rhs, exit_cond_consts) =
            Self::lower_compare_ast(&parts.exit_condition, builder, &phi_bindings)?;

        let (carrier_lhs, carrier_op, carrier_rhs, carrier_consts) =
            Self::lower_binop_ast(carrier_update_ast, builder, &phi_bindings)?;

        let (inc_lhs, inc_op, inc_rhs, inc_consts) =
            Self::lower_binop_ast(&parts.loop_increment, builder, &phi_bindings)?;

        // Step 7: Build header_effects
        let header_effects = vec![CoreEffectPlan::Const {
            dst: true_val,
            value: ConstValue::Bool(true),
        }];

        // Step 8: Build body plans (exit condition check)
        let mut body_plans: Vec<CorePlan> = Vec::new();
        for eff in exit_cond_consts {
            body_plans.push(CorePlan::Effect(eff));
        }
        body_plans.push(CorePlan::Effect(CoreEffectPlan::Compare {
            dst: cond_exit,
            lhs: exit_cond_lhs,
            op: exit_cond_op,
            rhs: exit_cond_rhs,
        }));

        // Step 9: Build break_then_effects (empty)
        let break_then_effects = vec![];

        // Step 10: Build step_effects (carrier update + loop increment)
        let mut step_effects = carrier_consts;
        step_effects.push(CoreEffectPlan::BinOp {
            dst: carrier_step,
            lhs: carrier_lhs,
            op: carrier_op,
            rhs: carrier_rhs,
        });
        for c in inc_consts {
            step_effects.push(c);
        }
        step_effects.push(CoreEffectPlan::BinOp {
            dst: loop_var_next,
            lhs: inc_lhs,
            op: inc_op,
            rhs: inc_rhs,
        });

        // Step 11: 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_carrier_{}", parts.loop_var),
            },
            CorePhiInfo {
                block: header_bb,
                dst: carrier_current,
                inputs: vec![(preheader_bb, carrier_init), (step_bb, carrier_step)],
                tag: format!("loop_carrier_{}", carrier_var),
            },
            CorePhiInfo {
                block: after_bb,
                dst: carrier_out,
                inputs: vec![(break_then_bb, carrier_current)],
                tag: format!("exit_carrier_{}", carrier_var),
            },
        ];

        // Step 12: Build block_effects
        let block_effects = vec![
            (preheader_bb, vec![]),
            (header_bb, header_effects),
            (body_bb, vec![]),
            (break_then_bb, break_then_effects),
            (step_bb, step_effects),
        ];

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

        let branches = vec![
            BranchStub {
                from: header_bb,
                cond: true_val,
                then_target: body_bb,
                else_target: after_bb,
                then_args: empty_args.clone(),
                else_args: empty_args.clone(),
            },
            BranchStub {
                from: body_bb,
                cond: cond_exit,
                then_target: break_then_bb,
                else_target: step_bb,
                then_args: empty_args.clone(),
                else_args: empty_args.clone(),
            },
        ];

        let loop_id = crate::mir::control_form::LoopId(0);

        let wires = vec![
            EdgeStub {
                from: break_then_bb,
                kind: ExitKind::Break(loop_id),
                target: Some(after_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 14: Build final_values
        let final_values = vec![
            (parts.loop_var.clone(), loop_var_current),
            (carrier_var.clone(), carrier_out),
        ];

        // Step 15: 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: true_val,
            cond_match: cond_exit,
            block_effects,
            phis,
            frag,
            final_values,
        };

        if debug {
            trace_logger.debug(
                "normalizer/pattern5_break",
                "CorePlan construction complete (6 blocks, 3 PHIs, after_bb PHI for carrier)",
            );
        }

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