use super::{CoreEffectPlan, CoreLoopPlan, CorePhiInfo, CorePlan, ScanWithInitPlan};
use crate::mir::builder::control_flow::plan::ScanDirection;
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::{BinaryOp, CompareOp, ConstValue, Effect, EffectMask, MirType};
use std::collections::BTreeMap;

impl super::PlanNormalizer {
    /// ScanWithInit → CorePlan 変換
    ///
    /// Expands scan-specific semantics into generic CorePlan:
    /// - header_effects: one=1, needle_len, len, bound, cond_loop
    /// - body: i+needle_len, substring, cond_match
    /// - step_effects: i_next = i + 1
    pub(super) fn normalize_scan_with_init(
        builder: &mut MirBuilder,
        parts: ScanWithInitPlan,
        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/scan_with_init",
                &format!(
                    "Phase 273 P1: Normalizing ScanWithInit for {}",
                    ctx.func_name
                ),
            );
        }

        // P1 Scope: forward (step=1) or reverse (step=-1)
        match parts.scan_direction {
            ScanDirection::Forward if parts.step_lit != 1 => {
                return Err(format!(
                    "[normalizer] P1 scope: forward scan requires step=1 (step={})",
                    parts.step_lit
                ));
            }
            ScanDirection::Reverse if parts.step_lit != -1 => {
                return Err(format!(
                    "[normalizer] P1 scope: reverse scan requires step=-1 (step={})",
                    parts.step_lit
                ));
            }
            _ => {}
        }

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

        let needle_host = builder
            .variable_ctx
            .variable_map
            .get(&parts.needle)
            .copied()
            .ok_or_else(|| format!("[normalizer] Variable {} not found", parts.needle))?;

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

        // Step 2: Capture preheader block (entry to loop) for PHI input
        let preheader_bb = builder
            .current_block
            .ok_or_else(|| "[normalizer] No current block for loop entry".to_string())?;

        // Step 3: Allocate BasicBlockIds for 5 blocks
        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();

        // Step 4: Allocate ValueIds for CorePlan
        let i_current = builder.next_value_id(); // PHI destination
        builder
            .type_ctx
            .value_types
            .insert(i_current, MirType::Integer);

        let one_val = builder.next_value_id();
        builder
            .type_ctx
            .value_types
            .insert(one_val, MirType::Integer);

        let needle_len_val = if parts.dynamic_needle {
            let v = builder.next_value_id();
            builder.type_ctx.value_types.insert(v, MirType::Integer);
            v
        } else {
            one_val // reuse one_val for fixed needle
        };

        let len_val = builder.next_value_id();
        builder
            .type_ctx
            .value_types
            .insert(len_val, MirType::Integer);

        let bound_val = builder.next_value_id();
        builder
            .type_ctx
            .value_types
            .insert(bound_val, MirType::Integer);

        let cond_loop = builder.next_value_id();
        builder
            .type_ctx
            .value_types
            .insert(cond_loop, MirType::Bool);

        let i_plus_needle_len = builder.next_value_id();
        builder
            .type_ctx
            .value_types
            .insert(i_plus_needle_len, MirType::Integer);

        let window_val = builder.next_value_id();
        builder
            .type_ctx
            .value_types
            .insert(window_val, MirType::String);

        let cond_match = builder.next_value_id();
        builder
            .type_ctx
            .value_types
            .insert(cond_match, MirType::Bool);

        let i_next_val = builder.next_value_id();
        builder
            .type_ctx
            .value_types
            .insert(i_next_val, MirType::Integer);

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

        // Step 5: Build header_effects (emitted in header_bb)
        let mut header_effects = vec![
            // one = 1
            CoreEffectPlan::Const {
                dst: one_val,
                value: ConstValue::Integer(1),
            },
        ];

        // needle_len = needle.length() if dynamic, else reuse one_val
        if parts.dynamic_needle {
            header_effects.push(CoreEffectPlan::MethodCall {
                dst: Some(needle_len_val),
                object: needle_host,
                method: "length".to_string(),
                args: vec![],
                effects: EffectMask::PURE.add(Effect::Io),
            });
        }

        match parts.scan_direction {
            ScanDirection::Forward => {
                // len = s.length()
                header_effects.push(CoreEffectPlan::MethodCall {
                    dst: Some(len_val),
                    object: s_host,
                    method: "length".to_string(),
                    args: vec![],
                    effects: EffectMask::PURE.add(Effect::Io),
                });

                // bound = len - needle_len
                header_effects.push(CoreEffectPlan::BinOp {
                    dst: bound_val,
                    lhs: len_val,
                    op: BinaryOp::Sub,
                    rhs: needle_len_val,
                });

                // cond_loop = i <= bound
                header_effects.push(CoreEffectPlan::Compare {
                    dst: cond_loop,
                    lhs: i_current,
                    op: CompareOp::Le,
                    rhs: bound_val,
                });
            }
            ScanDirection::Reverse => {
                let zero_val = builder.next_value_id();
                builder
                    .type_ctx
                    .value_types
                    .insert(zero_val, MirType::Integer);

                // zero = 0
                header_effects.push(CoreEffectPlan::Const {
                    dst: zero_val,
                    value: ConstValue::Integer(0),
                });

                // cond_loop = i >= 0
                header_effects.push(CoreEffectPlan::Compare {
                    dst: cond_loop,
                    lhs: i_current,
                    op: CompareOp::Ge,
                    rhs: zero_val,
                });
            }
        }

        // Step 6: Build body (emitted in body_bb)
        let body = vec![
            // i_plus_needle_len = i + needle_len
            CorePlan::Effect(CoreEffectPlan::BinOp {
                dst: i_plus_needle_len,
                lhs: i_current,
                op: BinaryOp::Add,
                rhs: needle_len_val,
            }),
            // window = s.substring(i, i + needle_len)
            CorePlan::Effect(CoreEffectPlan::MethodCall {
                dst: Some(window_val),
                object: s_host,
                method: "substring".to_string(),
                args: vec![i_current, i_plus_needle_len],
                effects: EffectMask::PURE.add(Effect::Io),
            }),
            // cond_match = window == needle
            CorePlan::Effect(CoreEffectPlan::Compare {
                dst: cond_match,
                lhs: window_val,
                op: CompareOp::Eq,
                rhs: needle_host,
            }),
        ];

        // Step 7: Build step_effects (emitted in step_bb)
        let step_effects = match parts.scan_direction {
            ScanDirection::Forward => vec![CoreEffectPlan::BinOp {
                // i_next = i + 1
                dst: i_next_val,
                lhs: i_current,
                op: BinaryOp::Add,
                rhs: one_val,
            }],
            ScanDirection::Reverse => vec![CoreEffectPlan::BinOp {
                // i_next = i - 1
                dst: i_next_val,
                lhs: i_current,
                op: BinaryOp::Sub,
                rhs: one_val,
            }],
        };

        // Step 8: Build block_effects (Phase 273 P2)
        let block_effects = vec![
            (preheader_bb, vec![]),
            (header_bb, header_effects.clone()),
            (body_bb, vec![]),
            (step_bb, step_effects.clone()),
        ];

        // Step 9: Build phis (Phase 273 P2)
        let phis = vec![CorePhiInfo {
            block: header_bb,
            dst: i_current,
            inputs: vec![(preheader_bb, i_init_val), (step_bb, i_next_val)],
            tag: format!("loop_carrier_{}", parts.loop_var),
        }];

        // Step 10: Build Frag (Phase 273 P2)
        let empty_args = EdgeArgs {
            layout: JumpArgsLayout::CarriersOnly,
            values: vec![],
        };

        let ret_found_args = EdgeArgs {
            layout: JumpArgsLayout::CarriersOnly,
            values: vec![i_current],
        };

        use crate::mir::builder::control_flow::edgecfg::api::compose;

        let main_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_match,
                then_target: found_bb,
                then_args: empty_args.clone(),
                else_target: step_bb,
                else_args: empty_args.clone(),
            },
        ];

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

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

        let cleanup_exits = vec![EdgeStub {
            from: found_bb,
            kind: ExitKind::Return,
            target: None,
            args: ret_found_args,
        }];

        let cleanup_frag = Frag {
            entry: found_bb,
            exits: BTreeMap::from([(ExitKind::Return, cleanup_exits)]),
            wires: vec![],
            branches: vec![],
        };

        let frag = compose::cleanup(main_frag, cleanup_frag, None, None)
            .expect("compose::cleanup() failed in normalize_scan_with_init");

        // Step 11: Build final_values (Phase 273 P2)
        let final_values = vec![(parts.loop_var.clone(), i_current)];

        // Step 12: Build CoreLoopPlan (Phase 273 P3: all generalized fields REQUIRED)
        let loop_plan = CoreLoopPlan {
            preheader_bb,
            header_bb,
            body_bb,
            step_bb,
            after_bb,
            found_bb,
            body,
            cond_loop,
            cond_match,
            block_effects,
            phis,
            frag,
            final_values,
        };

        if debug {
            trace_logger.debug(
                "normalizer/scan_with_init",
                "CorePlan construction complete",
            );
        }

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