//! Pattern 5: Infinite Loop with Early Exit (Phase 131-11)
//!
//! # Pattern Overview
//!
//! Handles `loop(true)` with both `break` and `continue` statements.
//! This is a specialized pattern for infinite loops with early exit conditions.
//!
//! # Example
//!
//! ```nyash
//! local counter = 0
//! loop (true) {
//!   counter = counter + 1
//!   if counter == 3 { break }
//!   continue
//! }
//! ```
//!
//! # Shape Guard (Fail-Fast)
//!
//! - Condition MUST be `true` literal (infinite loop)
//! - MUST have exactly 1 break statement
//! - MUST have exactly 1 continue statement
//! - Carriers: MUST be exactly 1 counter-like variable
//! - No nested loops (not yet supported)
//!
//! # Implementation Status
//!
//! Phase 131-11-C: Minimal skeleton with Fail-Fast guards
//! - Shape validation implemented
//! - Lowering logic: TODO (will fail with explicit error)

use super::super::trace;
use crate::ast::ASTNode;
use crate::mir::builder::MirBuilder;
use crate::mir::loop_pattern_detection::LoopPatternKind;
use crate::mir::ValueId;

use super::router::LoopPatternContext;

/// Phase 131-11-D: Enhanced shape guard with real count + position constraints
///
/// This function validates the loop structure for Pattern 5 with strict requirements:
/// - Exactly 1 break inside `if counter == N { break }`
/// - Exactly 1 continue at the end of the loop body (not nested)
/// - No nested loops
/// - No side effects beyond counter increment and break/continue
fn count_breaks_and_continues(body: &[ASTNode]) -> (usize, usize, bool) {
    let mut break_count = 0;
    let mut continue_count = 0;
    let mut has_nested_loop = false;

    fn scan_node(
        node: &ASTNode,
        break_count: &mut usize,
        continue_count: &mut usize,
        has_nested_loop: &mut bool,
        depth: usize,
    ) {
        match node {
            ASTNode::Break { .. } => {
                *break_count += 1;
            }
            ASTNode::Continue { .. } => {
                *continue_count += 1;
            }
            ASTNode::Loop { .. } if depth > 0 => {
                *has_nested_loop = true;
            }
            ASTNode::If {
                then_body,
                else_body,
                ..
            } => {
                for stmt in then_body {
                    scan_node(
                        stmt,
                        break_count,
                        continue_count,
                        has_nested_loop,
                        depth + 1,
                    );
                }
                if let Some(else_body) = else_body {
                    for stmt in else_body {
                        scan_node(
                            stmt,
                            break_count,
                            continue_count,
                            has_nested_loop,
                            depth + 1,
                        );
                    }
                }
            }
            _ => {}
        }
    }

    for stmt in body {
        scan_node(
            stmt,
            &mut break_count,
            &mut continue_count,
            &mut has_nested_loop,
            0,
        );
    }

    (break_count, continue_count, has_nested_loop)
}

/// Phase 131-11-D: Validate continue position (must be at loop body end)
fn validate_continue_position(body: &[ASTNode]) -> bool {
    if body.is_empty() {
        return false;
    }

    // Continue must be the last statement
    matches!(body.last(), Some(ASTNode::Continue { .. }))
}

/// Phase 131-11-D: Validate break is in simple if pattern
fn validate_break_pattern(body: &[ASTNode]) -> bool {
    for stmt in body {
        if let ASTNode::If {
            then_body,
            else_body,
            ..
        } = stmt
        {
            // Check then branch for simple break
            if then_body.len() == 1 && matches!(then_body[0], ASTNode::Break { .. }) {
                // Ensure no else-break pattern
                if else_body.is_none() {
                    return true;
                }
            }
        }
    }
    false
}

/// Phase 131-11: Pattern detection for InfiniteEarlyExit
///
/// This function checks if the loop matches Pattern 5 characteristics.
/// Uses Fail-Fast approach: narrow shape guard to avoid false positives.
pub(crate) fn can_lower(_builder: &MirBuilder, ctx: &LoopPatternContext) -> bool {
    let debug = ctx.debug;

    // Step 1: Check pattern classification
    if ctx.pattern_kind != LoopPatternKind::InfiniteEarlyExit {
        if debug {
            trace::trace().debug(
                "pattern5/detect",
                &format!(
                    "Pattern kind mismatch: expected InfiniteEarlyExit, got {:?}",
                    ctx.pattern_kind
                ),
            );
        }
        return false;
    }

    // Step 2: Shape guard - infinite loop condition (true literal)
    if !ctx.features.is_infinite_loop {
        if debug {
            trace::trace().debug(
                "pattern5/detect",
                "Not an infinite loop (condition != true)",
            );
        }
        return false;
    }

    // Phase 131-11-D: Enhanced real count validation
    let (real_break_count, real_continue_count, has_nested_loop) =
        count_breaks_and_continues(ctx.body);

    // Step 3: Shape guard - exactly 1 break (real count)
    if real_break_count != 1 {
        if debug {
            trace::trace().debug(
                "pattern5/detect",
                &format!(
                    "Real break count mismatch: expected 1, got {}",
                    real_break_count
                ),
            );
        }
        return false;
    }

    // Step 4: Shape guard - exactly 1 continue (real count)
    if real_continue_count != 1 {
        if debug {
            trace::trace().debug(
                "pattern5/detect",
                &format!(
                    "Real continue count mismatch: expected 1, got {}",
                    real_continue_count
                ),
            );
        }
        return false;
    }

    // Step 5: No nested loops
    if has_nested_loop {
        if debug {
            trace::trace().debug("pattern5/detect", "Nested loops not supported");
        }
        return false;
    }

    // Step 6: Validate continue position (must be at end)
    if !validate_continue_position(ctx.body) {
        if debug {
            trace::trace().debug("pattern5/detect", "Continue must be at loop body end");
        }
        return false;
    }

    // Step 7: Validate break pattern (must be in simple if)
    if !validate_break_pattern(ctx.body) {
        if debug {
            trace::trace().debug("pattern5/detect", "Break must be in simple if pattern");
        }
        return false;
    }

    // Step 8: Shape guard - exactly 1 carrier (counter-like)
    // Phase 131-11-C: Start with minimal carrier requirement
    if ctx.features.carrier_count != 1 {
        if debug {
            trace::trace().debug(
                "pattern5/detect",
                &format!(
                    "Carrier count mismatch: expected 1, got {}",
                    ctx.features.carrier_count
                ),
            );
        }
        return false;
    }

    // All shape guards passed
    if debug {
        trace::trace().debug(
            "pattern5/detect",
            &format!(
                "Pattern 5 detected: infinite={}, real_break={}, real_continue={}, carriers={}",
                ctx.features.is_infinite_loop,
                real_break_count,
                real_continue_count,
                ctx.features.carrier_count
            ),
        );
    }

    true
}

/// Phase 131-11-D: Extract counter variable name from break condition
///
/// Looks for pattern: `if counter == N { break }`
/// Returns the counter variable name
fn extract_counter_name(body: &[ASTNode]) -> Result<String, String> {
    use crate::ast::BinaryOperator;

    for stmt in body {
        if let ASTNode::If {
            condition,
            then_body,
            ..
        } = stmt
        {
            // Check if then_body contains just break
            if then_body.len() == 1 && matches!(then_body[0], ASTNode::Break { .. }) {
                // Extract counter from condition
                if let ASTNode::BinaryOp {
                    operator: BinaryOperator::Equal,
                    left,
                    ..
                } = condition.as_ref()
                {
                    if let ASTNode::Variable { name, .. } = left.as_ref() {
                        return Ok(name.clone());
                    }
                }
            }
        }
    }

    Err("Could not extract counter variable from break condition".to_string())
}

/// Phase 131-11-D: Extract limit constant from break condition
///
/// Looks for pattern: `if counter == LIMIT { break }`
/// Returns the LIMIT value
fn extract_limit_value(body: &[ASTNode]) -> Result<i64, String> {
    use crate::ast::{BinaryOperator, LiteralValue};

    for stmt in body {
        if let ASTNode::If {
            condition,
            then_body,
            ..
        } = stmt
        {
            // Check if then_body contains just break
            if then_body.len() == 1 && matches!(then_body[0], ASTNode::Break { .. }) {
                // Extract limit from condition
                if let ASTNode::BinaryOp {
                    operator: BinaryOperator::Equal,
                    right,
                    ..
                } = condition.as_ref()
                {
                    if let ASTNode::Literal {
                        value: LiteralValue::Integer(limit),
                        ..
                    } = right.as_ref()
                    {
                        return Ok(*limit);
                    }
                }
            }
        }
    }

    Err("Could not extract limit constant from break condition".to_string())
}

/// Phase 131-11: Lower InfiniteEarlyExit pattern to JoinIR
///
/// # Implementation Status
///
/// Phase 131-11-D: Full implementation with JoinIR generation
///
/// # JoinIR Structure (post-increment pattern)
///
/// ```text
/// fn main(counter_init):
///   result = loop_step(counter_init)
///   return Void
///
/// fn loop_step(counter):
///   counter_next = counter + 1
///   break_cond = (counter_next == LIMIT)
///   Jump(k_exit, [counter_next], cond=break_cond)
///   Call(loop_step, [counter_next])  // tail call
///
/// fn k_exit(counter_exit):
///   return Void
/// ```
pub(crate) fn lower(
    builder: &mut MirBuilder,
    ctx: &LoopPatternContext,
) -> Result<Option<ValueId>, String> {
    let debug = ctx.debug;

    if debug {
        trace::trace().debug(
            "pattern5/lower",
            "Phase 131-11-D: Starting Pattern 5 lowering",
        );
    }

    // Step 1: Extract counter variable name
    let counter_name = extract_counter_name(ctx.body)?;
    if debug {
        trace::trace().debug(
            "pattern5/lower",
            &format!("Extracted counter variable: '{}'", counter_name),
        );
    }

    // Step 2: Get counter ValueId from variable_ctx.variable_map
    let counter_id = builder
        .variable_ctx
        .variable_map
        .get(&counter_name)
        .copied()
        .ok_or_else(|| {
            format!(
                "Counter variable '{}' not found in variable_ctx.variable_map",
                counter_name
            )
        })?;

    if debug {
        trace::trace().debug(
            "pattern5/lower",
            &format!("Counter ValueId: {:?}", counter_id),
        );
    }

    // Step 3: Extract limit value
    let limit = extract_limit_value(ctx.body)?;
    if debug {
        trace::trace().debug(
            "pattern5/lower",
            &format!("Extracted limit value: {}", limit),
        );
    }

    // Step 4: Generate JoinIR
    use crate::mir::join_ir::lowering::join_value_space::JoinValueSpace;
    use crate::mir::join_ir::{
        BinOpKind, CompareOp, ConstValue, JoinFuncId, JoinFunction, JoinInst, JoinModule,
        MirLikeInst,
    };

    let mut join_value_space = JoinValueSpace::new();
    let mut alloc_value = || join_value_space.alloc_local();

    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() locals
    let counter_init = alloc_value(); // ValueId(1000) - initial counter
    let loop_result = alloc_value(); // ValueId(1001) - result from loop_step

    // loop_step locals
    let counter_param = alloc_value(); // ValueId(1002) - parameter
    let const_1 = alloc_value(); // ValueId(1003) - increment constant
    let counter_next = alloc_value(); // ValueId(1004) - counter + 1
    let const_limit = alloc_value(); // ValueId(1005) - limit constant
    let break_cond = alloc_value(); // ValueId(1006) - counter_next == LIMIT

    // k_exit locals
    let counter_exit = alloc_value(); // ValueId(1007) - exit parameter

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

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

    // return 0 (Pattern 5 doesn't produce a value, but we return 0 by convention)
    let const_0 = alloc_value();
    main_func.body.push(JoinInst::Compute(MirLikeInst::Const {
        dst: const_0,
        value: ConstValue::Integer(0),
    }));

    main_func.body.push(JoinInst::Ret {
        value: Some(const_0),
    });

    join_module.add_function(main_func);

    // ==================================================================
    // loop_step(counter) function - post-increment pattern
    // ==================================================================
    let mut loop_step_func =
        JoinFunction::new(loop_step_id, "loop_step".to_string(), vec![counter_param]);

    // counter_next = counter + 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: counter_next,
            op: BinOpKind::Add,
            lhs: counter_param,
            rhs: const_1,
        }));

    // break_cond = (counter_next == 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: break_cond,
            op: CompareOp::Eq,
            lhs: counter_next,
            rhs: const_limit,
        }));

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

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

    join_module.add_function(loop_step_func);

    // ==================================================================
    // k_exit(counter_exit) function
    // ==================================================================
    let mut k_exit_func = JoinFunction::new(k_exit_id, "k_exit".to_string(), vec![counter_exit]);

    // Return counter_exit (the final counter value) instead of const 0
    k_exit_func.body.push(JoinInst::Ret {
        value: Some(counter_exit),
    });

    join_module.add_function(k_exit_func);

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

    if debug {
        trace::trace().debug(
            "pattern5/lower",
            "Generated JoinIR: main, loop_step, k_exit",
        );
    }

    // Step 5: Create CarrierInfo for the counter (Phase 131-11-D)
    // Note: counter is the loop variable, NOT a separate carrier
    use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, ExitMeta};

    let carrier_info = CarrierInfo {
        loop_var_name: counter_name.clone(),
        loop_var_id: counter_id,
        carriers: vec![], // No separate carriers - counter is the loop variable itself
        trim_helper: None,
        promoted_loopbodylocals: Vec::new(),
        #[cfg(feature = "normalized_dev")]
        promoted_bindings: std::collections::BTreeMap::new(),
    };

    // Step 6: Create ExitMeta with counter as exit value
    // Phase 131-11-D: The counter flows through jump_args and must be registered in exit_values
    let exit_meta = ExitMeta {
        exit_values: vec![(counter_name.clone(), counter_exit)],
    };

    // Step 7: Build boundary and merge
    use crate::mir::builder::control_flow::joinir::merge::exit_line::ExitMetaCollector;
    use crate::mir::join_ir::lowering::JoinInlineBoundaryBuilder;

    let exit_bindings = ExitMetaCollector::collect(builder, &exit_meta, Some(&carrier_info), debug);

    let boundary = JoinInlineBoundaryBuilder::new()
        .with_inputs(
            vec![counter_init], // JoinIR main() parameter
            vec![counter_id],   // Host counter value
        )
        .with_exit_bindings(exit_bindings)
        .with_loop_var_name(Some(counter_name.clone())) // Phase 131-11-D: For LoopHeaderPhiBuilder
        .with_carrier_info(carrier_info) // Phase 131-11-D: For exit line reconnection
        .build();

    // Step 7: Execute conversion pipeline
    use super::conversion_pipeline::JoinIRConversionPipeline;
    let _ = JoinIRConversionPipeline::execute(
        builder,
        join_module,
        Some(&boundary),
        "pattern5",
        debug,
    )?;

    // Step 8: Return Void (loops don't produce values)
    let void_val = crate::mir::builder::emission::constant::emit_void(builder);

    if debug {
        trace::trace().debug(
            "pattern5/lower",
            &format!("Pattern 5 lowering complete, returning Void {:?}", void_val),
        );
    }

    Ok(Some(void_val))
}
