//! Phase 270 P1: Pattern 9 - Accumulator Const Loop (固定橋渡しパターン)
//!
//! **目的**: Phase 270 fixture (`phase270_p0_loop_min_const.hako`) を JoinIR 経路で通す
//!
//! **設計方針**:
//! - Pattern1 は test-only stub として保存（触らない）
//! - Pattern9 は Phase270 専用の最小固定パターン（汎用実装ではない）
//! - 将来 ExitKind+Frag に吸収される前提の橋渡しパターン
//!
//! ## 受理条件（Fail-Fast 固定）
//!
//! 1. **ループ条件**: `i < <int literal>` のみ
//! 2. **ループ本体**: 代入2本のみ（順序固定）
//!    - `sum = sum + i`
//!    - `i = i + 1`
//! 3. **制御構文**: break/continue/return があれば `Ok(None)` でフォールバック
//! 4. **loop後**: `return sum`
//!
//! ## JoinIR 構造
//!
//! ```text
//! main(i_init, sum_init):
//!   result = loop_step(i_init, sum_init)
//!   return result
//!
//! loop_step(i, sum):
//!   cond = (i < limit)
//!   exit_cond = !cond
//!   Jump(k_exit, [sum], cond=exit_cond)
//!   sum_next = sum + i
//!   i_next = i + 1
//!   Call(loop_step, [i_next, sum_next])  // tail recursion
//!
//! k_exit(sum):
//!   return sum
//! ```

use crate::ast::{ASTNode, BinaryOperator, LiteralValue};
use crate::mir::builder::MirBuilder;
use crate::mir::ValueId;

/// Phase 270 P1: Pattern 9 detection
///
/// **Fail-Fast 判定**: Phase270 fixture の形に厳密一致する場合のみ true
pub(crate) fn can_lower(builder: &MirBuilder, ctx: &super::router::LoopPatternContext) -> bool {
    // Step 1: ループ条件チェック: i < <int literal>
    let (loop_var, _limit) = match extract_loop_condition(ctx.condition) {
        Some(result) => result,
        None => return false,
    };

    // Step 2: ループ変数が variable_map に存在するか確認
    if !builder.variable_ctx.variable_map.contains_key(&loop_var) {
        return false;
    }

    // Step 3: ループ本体チェック: sum = sum + i; i = i + 1
    let (_sum_var, _i_var) = match extract_loop_body_assignments(ctx.body, &loop_var) {
        Some(result) => result,
        None => return false,
    };

    // Step 4: break/continue/return があれば reject
    if has_control_flow(ctx.body) {
        return false;
    }

    // Pattern9 受理
    true
}

/// Phase 270 P1: Pattern 9 lowering
///
/// **void stub 禁止**: JoinIR を生成して JoinIRConversionPipeline::execute へ渡す
pub(crate) fn lower(
    builder: &mut MirBuilder,
    ctx: &super::router::LoopPatternContext,
) -> Result<Option<ValueId>, String> {
    // Step 1: 最終検証（can_lower で漏れた条件確認）
    if !can_lower(builder, ctx) {
        return Ok(None); // Phase 263 PromoteDecision 思想: 他パターンへフォールバック
    }

    // Step 2: ループ条件とキャリア変数を抽出
    let (loop_var, limit) = extract_loop_condition(ctx.condition)
        .ok_or_else(|| "[pattern9] Failed to extract loop condition".to_string())?;

    let (sum_var, _i_var) = extract_loop_body_assignments(ctx.body, &loop_var)
        .ok_or_else(|| "[pattern9] Failed to extract body assignments".to_string())?;

    // Step 3: variable_map から HOST ValueId を取得
    let i_host_id = builder
        .variable_ctx
        .variable_map
        .get(&loop_var)
        .copied()
        .ok_or_else(|| format!("[pattern9] Loop variable '{}' not found", loop_var))?;

    let sum_host_id = builder
        .variable_ctx
        .variable_map
        .get(&sum_var)
        .copied()
        .ok_or_else(|| format!("[pattern9] Accumulator variable '{}' not found", sum_var))?;

    // Step 4: JoinIR lowerer を呼び出し
    use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
    let mut join_value_space = JoinValueSpace::new();

    let join_module = lower_accum_const_loop_joinir(limit, &mut join_value_space)?;

    // Step 5: JoinInlineBoundary を構築
    use crate::mir::join_ir::lowering::carrier_info::CarrierRole;
    use crate::mir::join_ir::lowering::inline_boundary::LoopExitBinding;
    use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;

    // k_exit のパラメータ（sum_exit）を取得
    let k_exit_func = join_module.require_function("k_exit", "Pattern 9");
    let sum_exit_value = k_exit_func
        .params
        .first()
        .copied()
        .expect("[pattern9] k_exit must have parameter for sum");

    // exit_bindings: sum のみ（i は捨てる）
    let exit_binding = LoopExitBinding {
        carrier_name: sum_var.clone(),
        join_exit_value: sum_exit_value,
        host_slot: sum_host_id,
        role: CarrierRole::LoopState,
    };

    // main のパラメータ（i_init, sum_init）を取得
    let main_func = join_module.require_function("main", "Pattern 9");
    let i_init_param = main_func
        .params
        .get(0)
        .copied()
        .expect("[pattern9] main must have i parameter");
    let sum_init_param = main_func
        .params
        .get(1)
        .copied()
        .expect("[pattern9] main must have sum parameter");

    let boundary = JoinInlineBoundaryBuilder::new()
        .with_inputs(
            vec![i_init_param, sum_init_param],
            vec![i_host_id, sum_host_id],
        )
        .with_exit_bindings(vec![exit_binding])
        .with_loop_var_name(Some(loop_var.clone()))
        .build();

    // Step 6: JoinIRConversionPipeline で MIR に変換
    use super::conversion_pipeline::JoinIRConversionPipeline;

    eprintln!("[pattern9] Lowering Phase270 fixture with Pattern9 (AccumConstLoop)");
    let _ = JoinIRConversionPipeline::execute(
        builder,
        join_module,
        Some(&boundary),
        "pattern9",
        ctx.debug,
    )?;

    // Phase 270 P1: Return Void (loop doesn't produce value)
    let void_val = crate::mir::builder::emission::constant::emit_void(builder);
    Ok(Some(void_val))
}

// ================================================================
// JoinIR Lowerer (Phase 270 P1 - 固定2キャリア: i, sum)
// ================================================================

/// Phase 270 P1: JoinIR lowerer for accumulator const loop
///
/// **構造**:
/// - main(i_init, sum_init) → loop_step(i, sum) → k_exit(sum)
/// - loop_step: 条件チェック → sum_next = sum + i → i_next = i + 1 → tail recursion
fn lower_accum_const_loop_joinir(
    limit: i64,
    join_value_space: &mut crate::mir::join_ir::lowering::join_value_space::JoinValueSpace,
) -> Result<crate::mir::join_ir::JoinModule, String> {
    use crate::mir::join_ir::lowering::canonical_names as cn;
    use crate::mir::join_ir::{
        BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule,
        MirLikeInst, UnaryOp,
    };

    let mut join_module = JoinModule::new();

    // Function IDs
    let main_id = JoinFuncId::new(0);
    let loop_step_id = JoinFuncId::new(1);
    let k_exit_id = JoinFuncId::new(2);

    // ValueId allocation
    // main() params/locals
    let i_main_param = join_value_space.alloc_param(); // i_init
    let sum_main_param = join_value_space.alloc_param(); // sum_init
    let loop_result = join_value_space.alloc_local(); // result from loop_step

    // loop_step params/locals
    let i_step_param = join_value_space.alloc_param(); // i
    let sum_step_param = join_value_space.alloc_param(); // sum
    let const_limit = join_value_space.alloc_local(); // limit constant
    let cmp_lt = join_value_space.alloc_local(); // i < limit
    let exit_cond = join_value_space.alloc_local(); // !(i < limit)
    let sum_next = join_value_space.alloc_local(); // sum + i
    let const_1 = join_value_space.alloc_local(); // increment constant
    let i_next = join_value_space.alloc_local(); // i + 1

    // k_exit params
    let sum_exit_param = join_value_space.alloc_param(); // sum

    // ================================================================
    // main(i_init, sum_init) function
    // ================================================================
    let mut main_func = JoinFunction::new(
        main_id,
        "main".to_string(),
        vec![i_main_param, sum_main_param],
    );

    // result = loop_step(i_main_param, sum_main_param)
    main_func.body.push(JoinInst::Call {
        func: loop_step_id,
        args: vec![i_main_param, sum_main_param],
        k_next: None,
        dst: Some(loop_result),
    });

    // return result
    main_func.body.push(JoinInst::Ret {
        value: Some(loop_result),
    });

    join_module.add_function(main_func);

    // ================================================================
    // loop_step(i, sum) function
    // ================================================================
    let mut loop_step_func = JoinFunction::new(
        loop_step_id,
        cn::LOOP_STEP.to_string(),
        vec![i_step_param, sum_step_param],
    );

    // cond = (i < limit)
    loop_step_func
        .body
        .push(JoinInst::Compute(MirLikeInst::Const {
            dst: const_limit,
            value: ConstValue::Integer(limit),
        }));

    loop_step_func
        .body
        .push(JoinInst::Compute(MirLikeInst::Compare {
            dst: cmp_lt,
            op: CompareOp::Lt,
            lhs: i_step_param,
            rhs: const_limit,
        }));

    // exit_cond = !cond
    loop_step_func
        .body
        .push(JoinInst::Compute(MirLikeInst::UnaryOp {
            dst: exit_cond,
            op: UnaryOp::Not,
            operand: cmp_lt,
        }));

    // Jump(k_exit, [sum], cond=exit_cond)
    loop_step_func.body.push(JoinInst::Jump {
        cont: k_exit_id.as_cont(),
        args: vec![sum_step_param],
        cond: Some(exit_cond),
    });

    // sum_next = sum + i
    loop_step_func
        .body
        .push(JoinInst::Compute(MirLikeInst::BinOp {
            dst: sum_next,
            op: BinOpKind::Add,
            lhs: sum_step_param,
            rhs: i_step_param,
        }));

    // i_next = i + 1
    loop_step_func
        .body
        .push(JoinInst::Compute(MirLikeInst::Const {
            dst: const_1,
            value: ConstValue::Integer(1),
        }));

    loop_step_func
        .body
        .push(JoinInst::Compute(MirLikeInst::BinOp {
            dst: i_next,
            op: BinOpKind::Add,
            lhs: i_step_param,
            rhs: const_1,
        }));

    // Call(loop_step, [i_next, sum_next]) - tail recursion
    loop_step_func.body.push(JoinInst::Call {
        func: loop_step_id,
        args: vec![i_next, sum_next],
        k_next: None,
        dst: None,
    });

    join_module.add_function(loop_step_func);

    // ================================================================
    // k_exit(sum) function
    // ================================================================
    let mut k_exit_func = JoinFunction::new(k_exit_id, cn::K_EXIT.to_string(), vec![sum_exit_param]);

    // return sum
    k_exit_func.body.push(JoinInst::Ret {
        value: Some(sum_exit_param),
    });

    join_module.add_function(k_exit_func);

    // Set entry point
    join_module.entry = Some(main_id);

    eprintln!("[joinir/pattern9] Generated JoinIR for AccumConstLoop Pattern");
    eprintln!("[joinir/pattern9] Functions: main, loop_step, k_exit");

    Ok(join_module)
}

// ================================================================
// Helper Functions (Fail-Fast 検証)
// ================================================================

/// ループ条件を抽出: `i < <int literal>` のみ受理
///
/// Returns: Some((loop_var, limit)) or None
fn extract_loop_condition(condition: &ASTNode) -> Option<(String, i64)> {
    match condition {
        ASTNode::BinaryOp {
            operator: BinaryOperator::Less,
            left,
            right,
            ..
        } => {
            // left: Variable
            let loop_var = match &**left {
                ASTNode::Variable { name, .. } => name.clone(),
                _ => return None,
            };

            // right: Literal::Integer
            let limit = match &**right {
                ASTNode::Literal {
                    value: LiteralValue::Integer(val),
                    ..
                } => *val,
                _ => return None,
            };

            Some((loop_var, limit))
        }
        _ => None,
    }
}

/// ループ本体の代入を抽出: `sum = sum + i; i = i + 1` のみ受理
///
/// Returns: Some((sum_var, i_var)) or None
fn extract_loop_body_assignments(body: &[ASTNode], expected_i: &str) -> Option<(String, String)> {
    // 厳密に2つの代入のみ
    if body.len() != 2 {
        return None;
    }

    // 1つ目: sum = sum + i
    let sum_var = match &body[0] {
        ASTNode::Assignment { target, value, .. } => {
            let sum_name = match target.as_ref() {
                ASTNode::Variable { name, .. } => name.clone(),
                _ => return None,
            };

            // value: sum + i
            match value.as_ref() {
                ASTNode::BinaryOp {
                    operator: BinaryOperator::Add,
                    left,
                    right,
                    ..
                } => {
                    // left: sum
                    let left_var = match left.as_ref() {
                        ASTNode::Variable { name, .. } => name,
                        _ => return None,
                    };
                    if left_var != &sum_name {
                        return None;
                    }

                    // right: i
                    let right_var = match right.as_ref() {
                        ASTNode::Variable { name, .. } => name,
                        _ => return None,
                    };
                    if right_var != expected_i {
                        return None;
                    }

                    sum_name
                }
                _ => return None,
            }
        }
        _ => return None,
    };

    // 2つ目: i = i + 1
    match &body[1] {
        ASTNode::Assignment { target, value, .. } => {
            let i_name = match target.as_ref() {
                ASTNode::Variable { name, .. } => name,
                _ => return None,
            };
            if i_name != expected_i {
                return None;
            }

            // value: i + 1
            match value.as_ref() {
                ASTNode::BinaryOp {
                    operator: BinaryOperator::Add,
                    left,
                    right,
                    ..
                } => {
                    // left: i
                    let left_var = match left.as_ref() {
                        ASTNode::Variable { name, .. } => name,
                        _ => return None,
                    };
                    if left_var != expected_i {
                        return None;
                    }

                    // right: 1
                    match right.as_ref() {
                        ASTNode::Literal {
                            value: LiteralValue::Integer(1),
                            ..
                        } => {}
                        _ => return None,
                    }
                }
                _ => return None,
            }
        }
        _ => return None,
    }

    Some((sum_var, expected_i.to_string()))
}

/// break/continue/return の有無をチェック
fn has_control_flow(body: &[ASTNode]) -> bool {
    body.iter().any(|stmt| {
        matches!(
            stmt,
            ASTNode::Break { .. } | ASTNode::Continue { .. } | ASTNode::Return { .. }
        )
    })
}
