//! Phase 273 P3: PlanLowerer - CorePlan → MIR 生成 (SSOT)
//!
//! # Responsibilities
//!
//! - Receive CorePlan from PlanNormalizer
//! - Emit MIR instructions using pre-allocated ValueIds
//! - No pattern-specific knowledge (pattern-agnostic)
//!
//! # Key Design Decision
//!
//! Lowerer processes CorePlan ONLY. It does not know about scan, split, or
//! any other pattern-specific semantics. All pattern knowledge is in Normalizer.
//!
//! # Phase 273 P3: SSOT Finalization
//!
//! - Generalized fields (block_effects/phis/frag/final_values) are now REQUIRED
//! - Legacy fallback has been removed (Fail-Fast on missing fields)
//! - Pattern-specific emission functions (emit_scan_with_init_edgecfg) no longer used

use super::{CoreEffectPlan, CoreExitPlan, CoreIfPlan, CoreLoopPlan, CorePlan};
use crate::mir::builder::control_flow::joinir::patterns::router::LoopPatternContext;
use crate::mir::builder::MirBuilder;
use crate::mir::{MirInstruction, ValueId};

/// Phase 273 P1: PlanLowerer - CorePlan → MIR 生成 (SSOT)
pub(in crate::mir::builder) struct PlanLowerer;

impl PlanLowerer {
    /// CorePlan を受け取り、MIR を生成
    ///
    /// # Arguments
    ///
    /// * `builder` - MIR builder (mutable access for instruction emission)
    /// * `plan` - CorePlan from Normalizer (pre-allocated ValueIds)
    /// * `ctx` - Loop pattern context for debug/func_name
    pub(in crate::mir::builder) fn lower(
        builder: &mut MirBuilder,
        plan: CorePlan,
        ctx: &LoopPatternContext,
    ) -> Result<Option<ValueId>, String> {
        match plan {
            CorePlan::Seq(plans) => Self::lower_seq(builder, plans, ctx),
            CorePlan::Loop(loop_plan) => Self::lower_loop(builder, loop_plan, ctx),
            CorePlan::If(if_plan) => Self::lower_if(builder, if_plan, ctx),
            CorePlan::Effect(effect) => {
                Self::emit_effect(builder, &effect)?;
                Ok(None)
            }
            CorePlan::Exit(exit) => Self::lower_exit(builder, exit),
        }
    }

    /// Seq: process plans in order
    fn lower_seq(
        builder: &mut MirBuilder,
        plans: Vec<CorePlan>,
        ctx: &LoopPatternContext,
    ) -> Result<Option<ValueId>, String> {
        let mut result = None;
        for plan in plans {
            result = Self::lower(builder, plan, ctx)?;
        }
        Ok(result)
    }

    /// Loop: emit blocks, effects, PHI, and edge CFG
    ///
    /// This is pattern-agnostic. All pattern knowledge is in Normalizer.
    /// Phase 273 P3: Generalized fields are now REQUIRED (Fail-Fast).
    fn lower_loop(
        builder: &mut MirBuilder,
        loop_plan: CoreLoopPlan,
        ctx: &LoopPatternContext,
    ) -> Result<Option<ValueId>, String> {
        use crate::mir::builder::control_flow::joinir::trace;

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

        if debug {
            trace_logger.debug(
                "lowerer/loop",
                &format!(
                    "Phase 273 P3: Lowering CoreLoopPlan for {}",
                    ctx.func_name
                ),
            );
        }

        // Phase 273 P3: Generalized fields are now struct fields (not Option)
        // No validation needed - type system guarantees presence

        if debug {
            trace_logger.debug("lowerer/loop", "Processing generalized fields (SSOT)");
        }

        Self::lower_loop_generalized(builder, loop_plan, ctx)
    }

    /// If: emit Branch and lower then/else plans (standalone)
    fn lower_if(
        builder: &mut MirBuilder,
        if_plan: CoreIfPlan,
        ctx: &LoopPatternContext,
    ) -> Result<Option<ValueId>, String> {
        use crate::mir::builder::emission::branch::{emit_conditional, emit_jump};

        let _pre_branch_bb = builder
            .current_block
            .ok_or_else(|| "[lowerer] No current block for CorePlan::If".to_string())?;

        let then_bb = builder.next_block_id();
        let else_bb = builder.next_block_id();
        let merge_bb = builder.next_block_id();

        builder.ensure_block_exists(then_bb)?;
        builder.ensure_block_exists(else_bb)?;
        builder.ensure_block_exists(merge_bb)?;

        let mut condition_val = if_plan.condition;
        crate::mir::builder::ssa::local::finalize_branch_cond(builder, &mut condition_val);
        emit_conditional(builder, condition_val, then_bb, else_bb)?;

        // then
        builder.start_new_block(then_bb)?;
        for plan in if_plan.then_plans {
            Self::lower(builder, plan, ctx)?;
        }
        let then_reaches_merge = !builder.is_current_block_terminated();
        if then_reaches_merge {
            emit_jump(builder, merge_bb)?;
        }

        // else
        builder.start_new_block(else_bb)?;
        if let Some(else_plans) = if_plan.else_plans {
            for plan in else_plans {
                Self::lower(builder, plan, ctx)?;
            }
        }
        let else_reaches_merge = !builder.is_current_block_terminated();
        if else_reaches_merge {
            emit_jump(builder, merge_bb)?;
        }

        // merge (may be unreachable if both branches terminate)
        builder.start_new_block(merge_bb)?;
        Ok(None)
    }

    /// Exit: emit Return (standalone); Break/Continue require loop context
    fn lower_exit(
        builder: &mut MirBuilder,
        exit: CoreExitPlan,
    ) -> Result<Option<ValueId>, String> {
        match exit {
            CoreExitPlan::Return(opt_val) => {
                builder.emit_instruction(MirInstruction::Return { value: opt_val })?;
                Ok(opt_val)
            }
            CoreExitPlan::Break => Err("[lowerer] CorePlan::Exit::Break requires loop context".to_string()),
            CoreExitPlan::Continue => {
                Err("[lowerer] CorePlan::Exit::Continue requires loop context".to_string())
            }
        }
    }

    /// Phase 273 P3: Generalized loop lowering (SSOT)
    fn lower_loop_generalized(
        builder: &mut MirBuilder,
        loop_plan: CoreLoopPlan,
        ctx: &LoopPatternContext,
    ) -> Result<Option<ValueId>, String> {
        use crate::mir::builder::control_flow::joinir::trace;
        use crate::mir::builder::control_flow::edgecfg::api::emit_frag;

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

        // Phase 273 P3: Direct access (not Option - type system guarantees presence)
        let block_effects = &loop_plan.block_effects;
        let phis = &loop_plan.phis;
        let frag = &loop_plan.frag;
        let final_values = &loop_plan.final_values;

        // Step 1: Emit Jump from current block to loop entry
        if builder.current_block.is_some() {
            builder.emit_instruction(MirInstruction::Jump {
                target: frag.entry,
                edge_args: None,
            })?;
        }

        // Step 2: Emit block effects in SSOT order (preheader, header, body, step)
        // Note: Body effects are handled separately via body CorePlan
        for (block_id, effects) in block_effects {
            builder.start_new_block(*block_id)?;

            // Special handling for body block: emit body CorePlan instead of effects
            if *block_id == loop_plan.body_bb {
                for plan in &loop_plan.body {
                    Self::emit_body_plan(builder, plan)?;
                }
            } else {
                // Normal block: emit effects
                for effect in effects {
                    Self::emit_effect(builder, effect)?;
                }
            }
        }

        if debug {
            trace_logger.debug(
                "lowerer/loop_generalized",
                &format!("Block effects emitted: {} blocks", block_effects.len()),
            );
        }

        // Step 3: Ensure non-effect blocks exist (after_bb, found_bb, etc.)
        builder.ensure_block_exists(loop_plan.after_bb)?;
        builder.ensure_block_exists(loop_plan.found_bb)?;

        // Step 4: Insert PHIs
        use crate::mir::builder::emission::phi::insert_loop_phi;

        for phi in phis {
            insert_loop_phi(
                builder,
                phi.block,
                phi.dst,
                phi.inputs.clone(),
                &phi.tag,
            )?;
        }

        if debug {
            trace_logger.debug(
                "lowerer/loop_generalized",
                &format!("PHI inserted: {} PHIs", phis.len()),
            );
        }

        // Step 5: Emit Frag (terminators)
        if let Some(ref mut func) = builder.scope_ctx.current_function {
            emit_frag(func, frag)?;
        } else {
            return Err("[lowerer] current_function is None".to_string());
        }

        if debug {
            trace_logger.debug("lowerer/loop_generalized", "Frag emitted");
        }

        // Step 6: Update variable_map for final values
        for (name, value_id) in final_values {
            builder
                .variable_ctx
                .variable_map
                .insert(name.clone(), *value_id);
        }

        // Step 7: Setup after_bb for subsequent AST lowering
        builder.start_new_block(loop_plan.after_bb)?;

        // Step 8: Return Void (pattern applied successfully)
        use crate::mir::builder::emission::constant::emit_void;
        let void_val = emit_void(builder);

        if debug {
            trace_logger.debug(
                "lowerer/loop_generalized",
                &format!("Loop complete, returning Void {:?}", void_val),
            );
        }

        Ok(Some(void_val))
    }

    // Phase 273 P3: lower_loop_legacy() has been REMOVED
    // All patterns must use generalized fields (block_effects/phis/frag/final_values)
    // Pattern-specific emission functions (emit_scan_with_init_edgecfg) are no longer used

    /// Emit a single CoreEffectPlan as MirInstruction
    fn emit_effect(builder: &mut MirBuilder, effect: &CoreEffectPlan) -> Result<(), String> {
        match effect {
            CoreEffectPlan::Const { dst, value } => {
                builder.emit_instruction(MirInstruction::Const {
                    dst: *dst,
                    value: value.clone(),
                })?;
            }
            CoreEffectPlan::MethodCall { dst, object, method, args, effects } => {
                // P2: dst and effects are now specified by Normalizer
                builder.emit_instruction(MirInstruction::BoxCall {
                    dst: *dst,
                    box_val: *object,
                    method: method.clone(),
                    method_id: None,
                    args: args.clone(),
                    effects: *effects,
                })?;
            }
            CoreEffectPlan::BinOp { dst, lhs, op, rhs } => {
                builder.emit_instruction(MirInstruction::BinOp {
                    dst: *dst,
                    lhs: *lhs,
                    op: *op,
                    rhs: *rhs,
                })?;
            }
            CoreEffectPlan::Compare { dst, lhs, op, rhs } => {
                builder.emit_instruction(MirInstruction::Compare {
                    dst: *dst,
                    lhs: *lhs,
                    op: *op,
                    rhs: *rhs,
                })?;
            }
        }
        Ok(())
    }

    fn emit_body_plan(builder: &mut MirBuilder, plan: &CorePlan) -> Result<(), String> {
        match plan {
            CorePlan::Effect(effect) => Self::emit_effect(builder, effect),
            CorePlan::Seq(plans) => {
                for nested in plans {
                    Self::emit_body_plan(builder, nested)?;
                }
                Ok(())
            }
            _ => Err("[lowerer] Non-Effect plan in Loop body not yet supported".to_string()),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::ast::{ASTNode, LiteralValue, Span};
    use crate::mir::builder::control_flow::edgecfg::api::Frag;
    use crate::mir::{ConstValue, MirInstruction};

    fn make_ctx<'a>(condition: &'a ASTNode, body: &'a [ASTNode]) -> LoopPatternContext<'a> {
        LoopPatternContext::new(condition, body, "test_coreplan", false, false)
    }

    #[test]
    fn test_lower_exit_return_sets_terminator() {
        let mut builder = MirBuilder::new();
        builder.enter_function_for_test("test_exit".to_string());

        let ret_val = builder.alloc_value_for_test();
        builder
            .emit_for_test(MirInstruction::Const {
                dst: ret_val,
                value: ConstValue::Integer(1),
            })
            .expect("emit const");

        let cond = ASTNode::Literal {
            value: LiteralValue::Bool(true),
            span: Span::unknown(),
        };
        let body: Vec<ASTNode> = vec![];
        let ctx = make_ctx(&cond, &body);

        let plan = CorePlan::Exit(CoreExitPlan::Return(Some(ret_val)));
        let result = PlanLowerer::lower(&mut builder, plan, &ctx);
        assert!(result.is_ok());

        let entry = builder.current_block_for_test().expect("entry block");
        let func = builder.scope_ctx.current_function.as_ref().expect("function");
        let block = func.get_block(entry).expect("block");
        assert!(
            matches!(block.terminator, Some(MirInstruction::Return { value: Some(v) }) if v == ret_val),
            "expected Return terminator"
        );
    }

    #[test]
    fn test_lower_if_emits_branch() {
        let mut builder = MirBuilder::new();
        builder.enter_function_for_test("test_if".to_string());

        let entry = builder.current_block_for_test().expect("entry block");
        let cond_val = builder.alloc_value_for_test();
        builder
            .emit_for_test(MirInstruction::Const {
                dst: cond_val,
                value: ConstValue::Bool(true),
            })
            .expect("emit const");

        let then_val = builder.alloc_value_for_test();
        let if_plan = CoreIfPlan {
            condition: cond_val,
            then_plans: vec![CorePlan::Effect(CoreEffectPlan::Const {
                dst: then_val,
                value: ConstValue::Integer(2),
            })],
            else_plans: None,
        };

        let cond = ASTNode::Literal {
            value: LiteralValue::Bool(true),
            span: Span::unknown(),
        };
        let body: Vec<ASTNode> = vec![];
        let ctx = make_ctx(&cond, &body);

        let result = PlanLowerer::lower(&mut builder, CorePlan::If(if_plan), &ctx);
        assert!(result.is_ok());

        let func = builder.scope_ctx.current_function.as_ref().expect("function");
        let block = func.get_block(entry).expect("entry block");
        assert!(
            matches!(block.terminator, Some(MirInstruction::Branch { .. })),
            "expected Branch terminator"
        );
    }

    #[test]
    fn test_lower_loop_body_seq_flattens() {
        let mut builder = MirBuilder::new();
        builder.enter_function_for_test("test_loop_body_seq".to_string());

        let preheader_bb = builder.next_block_id();
        let header_bb = builder.next_block_id();
        let body_bb = builder.next_block_id();
        let step_bb = builder.next_block_id();
        let after_bb = builder.next_block_id();
        let found_bb = builder.next_block_id();

        let eff1 = CoreEffectPlan::Const {
            dst: builder.alloc_value_for_test(),
            value: ConstValue::Integer(1),
        };
        let eff2 = CoreEffectPlan::Const {
            dst: builder.alloc_value_for_test(),
            value: ConstValue::Integer(2),
        };
        let eff3 = CoreEffectPlan::Const {
            dst: builder.alloc_value_for_test(),
            value: ConstValue::Integer(3),
        };

        let loop_plan = CoreLoopPlan {
            preheader_bb,
            header_bb,
            body_bb,
            step_bb,
            after_bb,
            found_bb,
            body: vec![CorePlan::Seq(vec![
                CorePlan::Effect(eff1),
                CorePlan::Seq(vec![CorePlan::Effect(eff2)]),
                CorePlan::Effect(eff3),
            ])],
            cond_loop: builder.alloc_value_for_test(),
            cond_match: builder.alloc_value_for_test(),
            block_effects: vec![
                (preheader_bb, vec![]),
                (header_bb, vec![]),
                (body_bb, vec![]),
                (step_bb, vec![]),
            ],
            phis: vec![],
            frag: Frag::new(header_bb),
            final_values: vec![],
        };

        let cond = ASTNode::Literal {
            value: LiteralValue::Bool(true),
            span: Span::unknown(),
        };
        let body: Vec<ASTNode> = vec![];
        let ctx = make_ctx(&cond, &body);

        let result = PlanLowerer::lower(&mut builder, CorePlan::Loop(loop_plan), &ctx);
        assert!(result.is_ok());

        let func = builder.scope_ctx.current_function.as_ref().expect("function");
        let block = func.get_block(body_bb).expect("body block");
        let const_count = block
            .instructions
            .iter()
            .filter(|inst| matches!(inst, MirInstruction::Const { .. }))
            .count();
        assert_eq!(const_count, 3, "expected 3 Const effects in body");
    }
}
