use super::{CoreEffectPlan, CoreLoopPlan, CorePhiInfo, CorePlan, SplitScanPlan};
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::{BinaryOp, CompareOp, ConstValue, Effect, EffectMask, MirType};
use std::collections::BTreeMap;
use super::common::{build_body_if_frag, empty_args};

impl super::PlanNormalizer {
    /// SplitScan → CorePlan 変換
    ///
    /// Expands split-specific semantics into generic CorePlan:
    /// - 2 carriers: i (loop index), start (segment start)
    /// - 6 blocks: preheader, header, body, then, else, step, after
    /// - 4 PHI nodes: header (i_current, start_current) + step (i_next, start_next)
    /// - Side effect: result.push(segment) in then_bb
    pub(super) fn normalize_split_scan(
        builder: &mut MirBuilder,
        parts: SplitScanPlan,
        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/split_scan",
                &format!(
                    "Phase 273 P2: Normalizing SplitScan for {}",
                    ctx.func_name
                ),
            );
        }

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

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

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

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

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

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

        // Step 3: Allocate BasicBlockIds for 6 blocks
        let header_bb = builder.next_block_id();
        let body_bb = builder.next_block_id();
        let then_bb = builder.next_block_id();
        let else_bb = builder.next_block_id();
        let step_bb = builder.next_block_id();
        let after_bb = builder.next_block_id();

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

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

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

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

        // Step 5: Allocate ValueIds for expressions
        let sep_len = builder.next_value_id();
        builder
            .type_ctx
            .value_types
            .insert(sep_len, MirType::Integer);

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

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

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

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

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

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

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

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

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

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

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

        // Step 6: Build header_effects
        let header_effects = vec![
            // sep_len = sep.length()
            CoreEffectPlan::MethodCall {
                dst: Some(sep_len),
                object: sep_host,
                method: "length".to_string(),
                args: vec![],
                effects: EffectMask::PURE.add(Effect::Io),
            },
            // s_len = s.length()
            CoreEffectPlan::MethodCall {
                dst: Some(s_len),
                object: s_host,
                method: "length".to_string(),
                args: vec![],
                effects: EffectMask::PURE.add(Effect::Io),
            },
            // limit = s_len - sep_len
            CoreEffectPlan::BinOp {
                dst: limit,
                lhs: s_len,
                op: BinaryOp::Sub,
                rhs: sep_len,
            },
            // cond_loop = i <= limit
            CoreEffectPlan::Compare {
                dst: cond_loop,
                lhs: i_current,
                op: CompareOp::Le,
                rhs: limit,
            },
        ];

        // Step 7: Build body effects and plans
        let body = vec![
            // i_plus_sep = i + sep_len
            CorePlan::Effect(CoreEffectPlan::BinOp {
                dst: i_plus_sep,
                lhs: i_current,
                op: BinaryOp::Add,
                rhs: sep_len,
            }),
            // chunk = s.substring(i, i_plus_sep)
            CorePlan::Effect(CoreEffectPlan::MethodCall {
                dst: Some(chunk),
                object: s_host,
                method: "substring".to_string(),
                args: vec![i_current, i_plus_sep],
                effects: EffectMask::PURE.add(Effect::Io),
            }),
            // cond_match = chunk == sep
            CorePlan::Effect(CoreEffectPlan::Compare {
                dst: cond_match,
                lhs: chunk,
                op: CompareOp::Eq,
                rhs: sep_host,
            }),
        ];

        // Step 8: Build then_effects (push + updates)
        let then_effects = vec![
            // segment = s.substring(start, i)
            CoreEffectPlan::MethodCall {
                dst: Some(segment),
                object: s_host,
                method: "substring".to_string(),
                args: vec![start_current, i_current],
                effects: EffectMask::PURE.add(Effect::Io),
            },
            // result.push(segment) - Side effect!
            CoreEffectPlan::MethodCall {
                dst: None,
                object: result_host,
                method: "push".to_string(),
                args: vec![segment],
                effects: EffectMask::MUT,
            },
            // start_next_then = i + sep_len
            CoreEffectPlan::BinOp {
                dst: start_next_then,
                lhs: i_current,
                op: BinaryOp::Add,
                rhs: sep_len,
            },
        ];

        // Step 9: Build else_effects (increment i)
        let else_effects = vec![
            // one = const 1
            CoreEffectPlan::Const {
                dst: one,
                value: ConstValue::Integer(1),
            },
            // i_next_else = i + 1
            CoreEffectPlan::BinOp {
                dst: i_next_else,
                lhs: i_current,
                op: BinaryOp::Add,
                rhs: one,
            },
        ];

        // Step 9.5: Build Frags for compose::if_() (Phase 281 P0)
        let empty_args = empty_args();

        let body_if_frag = build_body_if_frag(body_bb, cond_match, then_bb, else_bb, step_bb, &empty_args);

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

        // Step 11: Build phis (4 PHIs: 2 in header + 2 in step)
        let phis = vec![
            CorePhiInfo {
                block: header_bb,
                dst: i_current,
                inputs: vec![(preheader_bb, i_init_val), (step_bb, i_next)],
                tag: format!("loop_carrier_i_{}", parts.i_var),
            },
            CorePhiInfo {
                block: header_bb,
                dst: start_current,
                inputs: vec![(preheader_bb, start_init_val), (step_bb, start_next)],
                tag: format!("loop_carrier_start_{}", parts.start_var),
            },
            CorePhiInfo {
                block: step_bb,
                dst: i_next,
                inputs: vec![(then_bb, start_next_then), (else_bb, i_next_else)],
                tag: format!("step_phi_i_{}", parts.i_var),
            },
            CorePhiInfo {
                block: step_bb,
                dst: start_next,
                inputs: vec![(then_bb, start_next_then), (else_bb, start_current)],
                tag: format!("step_phi_start_{}", parts.start_var),
            },
        ];

        // Step 12: Build Frag
        let mut 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(),
        }];

        branches.extend(body_if_frag.branches);

        let mut wires = Vec::new();
        wires.extend(body_if_frag.wires);

        wires.push(EdgeStub {
            from: step_bb,
            kind: ExitKind::Normal,
            target: Some(header_bb),
            args: empty_args.clone(),
        });

        let mut exits = BTreeMap::new();
        for (kind, stubs) in body_if_frag.exits {
            exits.insert(kind, stubs);
        }

        let frag = Frag {
            entry: header_bb,
            exits,
            wires,
            branches,
        };

        // Step 13: Build final_values (i, start for post-loop)
        let final_values = vec![(parts.i_var.clone(), i_current), (parts.start_var.clone(), start_current)];

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

        if debug {
            trace_logger.debug(
                "normalizer/split_scan",
                "CorePlan construction complete (6 blocks, 4 PHIs)",
            );
        }

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