//! Phase 282 P6: Pattern4 (Loop with Continue) Extraction
//! Phase 282 P9a: Integrated with common_helpers

use crate::ast::ASTNode;

// Phase 282 P9a: Use common_helpers
// Phase 284 P1: has_return_statement removed (now handled by return_collector SSOT)
use super::common_helpers::{count_control_flow, has_break_statement, ControlFlowDetector};

#[derive(Debug, Clone)]
pub(crate) struct Pattern4Parts {
    pub loop_var: String,        // Extracted from condition (i < n)
    pub continue_count: usize,   // Must be >= 1 for Pattern4
}

/// Extract Pattern4 (Loop with Continue) parts
///
/// # Detection Criteria
///
/// 1. **Condition**: Comparison operator with left=variable (Pattern1 reuse)
/// 2. **Body**: At least 1 continue statement (Pattern4 必要条件)
/// 3. **Reject**: break → Pattern2 territory
/// 4. **Fail-Fast**: return → Err (close-but-unsupported, Phase 142 P2 pending)
///
/// # Five-Phase Validation (USER CORRECTION: Recursive detection required!)
///
/// **Phase 1**: Validate condition structure (reuse Pattern1)
/// **Phase 2**: Validate HAS continue (count >= 1, **recursive**)
/// **Phase 3**: Validate NO break (fail-fast or Ok(None), **recursive**)
/// **Phase 4**: Check for return (Err if found, **recursive**)
/// **Phase 5**: Return extracted parts
///
/// # Fail-Fast Rules
///
/// - `Ok(Some(parts))`: Pattern4 match confirmed
/// - `Ok(None)`: Not Pattern4 (wrong structure)
/// - `Err(msg)`: Close-but-unsupported (e.g., return found) or malformed AST
///
/// # Note
///
/// - USER CORRECTION: All detection is recursive (handles `if { continue }` patterns)
/// - USER CORRECTION: No loop_var update validation (deferred to Pattern4CarrierAnalyzer)
/// - Nested loop's continue/break statements are NOT counted (only current loop)
pub(crate) fn extract_loop_with_continue_parts(
    condition: &ASTNode,
    body: &[ASTNode],
) -> Result<Option<Pattern4Parts>, String> {
    // Phase 1: Validate condition structure (reuse Pattern1's validator)
    use super::pattern1::validate_condition_structure;

    let loop_var = match validate_condition_structure(condition) {
        Some(var) => var,
        None => return Ok(None), // Not comparison pattern → Other patterns
    };

    // Phase 2: Count continue statements (USER CORRECTION: Recursive!)
    let continue_count = count_continue_statements_recursive(body);
    if continue_count == 0 {
        return Ok(None); // No continue → Pattern1/2 territory
    }

    // Phase 3: Check for break (USER CORRECTION: Recursive!)
    if has_break_statement_recursive(body) {
        // Has break → Pattern2 territory (break takes priority)
        return Ok(None);
    }

    // Phase 284 P1: Return check removed - now handled by return_collector SSOT
    // in conversion_pipeline.rs (JoinIR line common entry point)

    // Phase 5: Return extracted parts
    // USER CORRECTION: No loop_var update validation - defer to Pattern4CarrierAnalyzer
    Ok(Some(Pattern4Parts {
        loop_var,
        continue_count,
    }))
}

// ============================================================================
// Helper Functions (Internal) - USER CORRECTION: All must be recursive!
// ============================================================================

/// Count continue statements recursively
///
/// # Phase 282 P9a: Delegates to common_helpers::count_control_flow
///
/// - Skips nested loop bodies (default behavior)
fn count_continue_statements_recursive(body: &[ASTNode]) -> usize {
    count_control_flow(body, ControlFlowDetector::default()).continue_count
}

/// Check recursively if body has break statement
///
/// # Phase 282 P9a: Delegates to common_helpers::has_break_statement
fn has_break_statement_recursive(body: &[ASTNode]) -> bool {
    has_break_statement(body)
}

// Phase 284 P1: has_return_statement_recursive removed
// Return detection now handled by return_collector SSOT in conversion_pipeline.rs

// ============================================================================
// Phase 286 P2: Pattern4 → Plan/Frag SSOT extractor
// ============================================================================

/// Phase 286 P2: Minimal subset extractor for Pattern4 Plan line
///
/// Supported subset (PoC safety - Compare + Add only):
/// - Loop condition: `<var> < <int_lit>`
/// - Continue condition: `<var> == <int_lit>` (NO Mod operator)
/// - Carrier update: `<var> = <var> + <var>` (accumulator pattern)
/// - Loop increment: `<var> = <var> + <int_lit>`
///
/// Returns Ok(None) for unsupported patterns → legacy fallback
pub(crate) fn extract_pattern4_plan(
    condition: &ASTNode,
    body: &[ASTNode],
) -> Result<Option<crate::mir::builder::control_flow::plan::DomainPlan>, String> {
    use crate::mir::builder::control_flow::plan::{DomainPlan, Pattern4ContinuePlan};

    // Step 1: Validate via existing extractor
    let parts = extract_loop_with_continue_parts(condition, body)?;
    if parts.is_none() {
        return Ok(None);  // Not Pattern4 → legacy fallback
    }

    // Step 2: Extract loop variable from condition `i < N`
    let loop_var = match extract_loop_condition_plan(condition)? {
        Some((var, _bound)) => var,
        None => return Ok(None),  // Unsupported condition
    };

    // Step 3: Find continue condition from body
    let continue_cond = match find_continue_condition_plan(body)? {
        Some(cond) => cond,
        None => return Ok(None),  // No continue condition found
    };

    // Step 4: Extract carrier updates (accumulator pattern)
    let carrier_updates = extract_carrier_updates_plan(body, &loop_var)?;
    if carrier_updates.is_empty() {
        return Ok(None);  // No carrier → unsupported
    }

    // Step 5: Extract loop increment `i = i + 1`
    // Phase 286 P2.2: Use common helper from common_helpers
    let loop_increment = match super::common_helpers::extract_loop_increment_plan(body, &loop_var)? {
        Some(inc) => inc,
        None => return Ok(None),  // No loop increment found
    };

    Ok(Some(DomainPlan::Pattern4Continue(Pattern4ContinuePlan {
        loop_var: loop_var.clone(),
        carrier_vars: carrier_updates.keys().cloned().collect(),
        condition: condition.clone(),
        continue_condition: continue_cond,
        carrier_updates,
        loop_increment,
    })))
}

/// Extract loop condition: supports `<var> < <int_lit>` only
fn extract_loop_condition_plan(cond: &ASTNode) -> Result<Option<(String, i64)>, String> {
    use crate::ast::{BinaryOperator, LiteralValue};

    if let ASTNode::BinaryOp { operator, left, right, .. } = cond {
        if !matches!(operator, BinaryOperator::Less) {
            return Ok(None);  // Only < supported
        }

        let var_name = if let ASTNode::Variable { name, .. } = left.as_ref() {
            name.clone()
        } else {
            return Ok(None);
        };

        let bound = if let ASTNode::Literal { value: LiteralValue::Integer(n), .. } = right.as_ref() {
            *n
        } else {
            return Ok(None);
        };

        Ok(Some((var_name, bound)))
    } else {
        Ok(None)
    }
}

/// Find continue condition in if statement
fn find_continue_condition_plan(body: &[ASTNode]) -> Result<Option<ASTNode>, String> {
    for stmt in body {
        if let ASTNode::If { condition, then_body, .. } = stmt {
            for then_stmt in then_body {
                if matches!(then_stmt, ASTNode::Continue { .. }) {
                    return Ok(Some(condition.as_ref().clone()));
                }
            }
        }
    }
    Ok(None)
}

/// Extract carrier updates: `<var> = <var> + <var>` pattern only
fn extract_carrier_updates_plan(
    body: &[ASTNode],
    loop_var: &str,
) -> Result<std::collections::BTreeMap<String, ASTNode>, String> {
    use crate::ast::BinaryOperator;
    use std::collections::BTreeMap;

    let mut updates = BTreeMap::new();

    for stmt in body {
        if let ASTNode::Assignment { target, value, .. } = stmt {
            let var_name = if let ASTNode::Variable { name, .. } = target.as_ref() {
                name.clone()
            } else {
                continue;
            };

            // Skip loop variable itself
            if var_name == loop_var {
                continue;
            }

            // Check for accumulator pattern: `var = var + x`
            if let ASTNode::BinaryOp { operator, left, .. } = value.as_ref() {
                if matches!(operator, BinaryOperator::Add) {
                    if let ASTNode::Variable { name, .. } = left.as_ref() {
                        if name == &var_name {
                            updates.insert(var_name, value.as_ref().clone());
                        }
                    }
                }
            }
        }
    }

    Ok(updates)
}

// ============================================================================
// Unit Tests
// ============================================================================

#[cfg(test)]
mod tests {
    use super::*;
    use crate::ast::{BinaryOperator, LiteralValue, Span};

    /// Helper: Create comparison condition (var < limit)
    fn make_condition(var: &str, limit: i64) -> ASTNode {
        ASTNode::BinaryOp {
            operator: BinaryOperator::Less,
            left: Box::new(ASTNode::Variable {
                name: var.to_string(),
                span: Span::unknown(),
            }),
            right: Box::new(ASTNode::Literal {
                value: LiteralValue::Integer(limit),
                span: Span::unknown(),
            }),
            span: Span::unknown(),
        }
    }

    /// Helper: Create if-continue block (if (skip) { continue })
    fn make_if_continue() -> ASTNode {
        ASTNode::If {
            condition: Box::new(ASTNode::Variable {
                name: "skip".to_string(),
                span: Span::unknown(),
            }),
            then_body: vec![ASTNode::Continue {
                span: Span::unknown(),
            }],
            else_body: None,
            span: Span::unknown(),
        }
    }

    /// Helper: Create if-break block (if (done) { break })
    fn make_if_break() -> ASTNode {
        ASTNode::If {
            condition: Box::new(ASTNode::Variable {
                name: "done".to_string(),
                span: Span::unknown(),
            }),
            then_body: vec![ASTNode::Break {
                span: Span::unknown(),
            }],
            else_body: None,
            span: Span::unknown(),
        }
    }

    /// Helper: Create increment (i = i + 1)
    fn make_increment(var: &str) -> ASTNode {
        ASTNode::Assignment {
            target: Box::new(ASTNode::Variable {
                name: var.to_string(),
                span: Span::unknown(),
            }),
            value: Box::new(ASTNode::BinaryOp {
                operator: BinaryOperator::Add,
                left: Box::new(ASTNode::Variable {
                    name: var.to_string(),
                    span: Span::unknown(),
                }),
                right: Box::new(ASTNode::Literal {
                    value: LiteralValue::Integer(1),
                    span: Span::unknown(),
                }),
                span: Span::unknown(),
            }),
            span: Span::unknown(),
        }
    }

    #[test]
    fn test_pattern4_continue_success() {
        // loop(i < 10) { if (skip) { continue } i = i + 1 }
        let condition = make_condition("i", 10);
        let body = vec![make_if_continue(), make_increment("i")];

        let result = extract_loop_with_continue_parts(&condition, &body);
        assert!(result.is_ok());
        let parts = result.unwrap();
        assert!(parts.is_some());

        let parts = parts.unwrap();
        assert_eq!(parts.loop_var, "i");
        assert_eq!(parts.continue_count, 1);
    }

    #[test]
    fn test_pattern4_no_continue_returns_none() {
        // loop(i < 10) { i = i + 1 } → Not Pattern4 (Pattern1 territory)
        let condition = make_condition("i", 10);
        let body = vec![make_increment("i")];

        let result = extract_loop_with_continue_parts(&condition, &body);
        assert!(result.is_ok());
        assert!(result.unwrap().is_none()); // No continue → Not Pattern4
    }

    #[test]
    fn test_pattern4_with_break_returns_none() {
        // loop(i < 10) { if (done) { break } i = i + 1 } → Pattern2
        let condition = make_condition("i", 10);
        let body = vec![make_if_break(), make_increment("i")];

        let result = extract_loop_with_continue_parts(&condition, &body);
        assert!(result.is_ok());
        assert!(result.unwrap().is_none()); // Has break → Pattern2
    }

    #[test]
    fn test_pattern4_with_return_is_allowed() {
        // Phase 284 P1: loop(i < 10) { if (done) { return } i = i + 1 } → Ok(Some(...))
        // Return check moved to return_collector SSOT in conversion_pipeline.rs
        let condition = make_condition("i", 10);
        let body = vec![
            ASTNode::If {
                condition: Box::new(ASTNode::Variable {
                    name: "done".to_string(),
                    span: Span::unknown(),
                }),
                then_body: vec![ASTNode::Return {
                    value: None,
                    span: Span::unknown(),
                }],
                else_body: None,
                span: Span::unknown(),
            },
            make_if_continue(),
            make_increment("i"),
        ];

        let result = extract_loop_with_continue_parts(&condition, &body);
        assert!(result.is_ok()); // Phase 284 P1: Now allowed at extractor level
        let parts = result.unwrap();
        assert!(parts.is_some()); // Pattern4 extraction succeeds
        let parts = parts.unwrap();
        assert_eq!(parts.loop_var, "i");
        assert_eq!(parts.continue_count, 1);
    }

    #[test]
    fn test_pattern4_multi_continue_count() {
        // loop(i < 10) { if (a) { continue } if (b) { continue } i = i + 1 }
        let condition = make_condition("i", 10);
        let body = vec![
            make_if_continue(), // First continue
            ASTNode::If {
                condition: Box::new(ASTNode::Variable {
                    name: "b".to_string(),
                    span: Span::unknown(),
                }),
                then_body: vec![ASTNode::Continue {
                    span: Span::unknown(),
                }],
                else_body: None,
                span: Span::unknown(),
            }, // Second continue
            make_increment("i"),
        ];

        let result = extract_loop_with_continue_parts(&condition, &body);
        assert!(result.is_ok());
        let parts = result.unwrap();
        assert!(parts.is_some());

        let parts = parts.unwrap();
        assert_eq!(parts.loop_var, "i");
        assert_eq!(parts.continue_count, 2); // Two continue statements detected
    }
}
