//! Continue/Break/Return Detection
//!
//! Phase 287 P1: Extracted from ast_feature_extractor.rs
//!
//! This module provides simple recursive detection of continue, break, and return statements
//! within loop bodies and nested structures.

use crate::ast::ASTNode;

/// Detect if a loop body contains continue statements
///
/// # Arguments
///
/// * `body` - Loop body statements to analyze
///
/// # Returns
///
/// `true` if at least one continue statement is found in the body or nested structures
///
/// # Notes
///
/// This is a simple recursive scan that doesn't handle nested loops perfectly,
/// but is sufficient for initial pattern detection.
pub(crate) fn detect_continue_in_body(body: &[ASTNode]) -> bool {
    for stmt in body {
        if has_continue_node(stmt) {
            return true;
        }
    }
    false
}

/// Detect if a loop body contains break statements
///
/// # Arguments
///
/// * `body` - Loop body statements to analyze
///
/// # Returns
///
/// `true` if at least one break statement is found in the body or nested structures
pub(crate) fn detect_break_in_body(body: &[ASTNode]) -> bool {
    for stmt in body {
        if has_break_node(stmt) {
            return true;
        }
    }
    false
}

/// Detect if a loop body contains return statements
///
/// This is used for dev-only parity checks with structure SSOT (StepTree).
pub(crate) fn detect_return_in_body(body: &[ASTNode]) -> bool {
    for stmt in body {
        if has_return_node(stmt) {
            return true;
        }
    }
    false
}

/// Recursive helper to check if AST node contains continue
pub(super) fn has_continue_node(node: &ASTNode) -> bool {
    match node {
        ASTNode::Continue { .. } => true,
        ASTNode::If {
            then_body,
            else_body,
            ..
        } => {
            then_body.iter().any(has_continue_node)
                || else_body
                    .as_ref()
                    .map_or(false, |e| e.iter().any(has_continue_node))
        }
        ASTNode::Loop { body, .. } => body.iter().any(has_continue_node),
        _ => false,
    }
}

/// Recursive helper to check if AST node contains break
fn has_break_node(node: &ASTNode) -> bool {
    match node {
        ASTNode::Break { .. } => true,
        ASTNode::If {
            then_body,
            else_body,
            ..
        } => {
            then_body.iter().any(has_break_node)
                || else_body
                    .as_ref()
                    .map_or(false, |e| e.iter().any(has_break_node))
        }
        ASTNode::Loop { body, .. } => body.iter().any(has_break_node),
        _ => false,
    }
}

/// Recursive helper to check if AST node contains return
fn has_return_node(node: &ASTNode) -> bool {
    match node {
        ASTNode::Return { .. } => true,
        ASTNode::If {
            then_body,
            else_body,
            ..
        } => {
            then_body.iter().any(has_return_node)
                || else_body
                    .as_ref()
                    .map_or(false, |e| e.iter().any(has_return_node))
        }
        ASTNode::Loop { body, .. } => body.iter().any(has_return_node),
        ASTNode::ScopeBox { body, .. } => body.iter().any(has_return_node),
        _ => false,
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_detect_continue_simple() {
        let continue_node = ASTNode::Continue {
            span: crate::ast::Span::unknown(),
        };
        assert!(has_continue_node(&continue_node));
    }

    #[test]
    fn test_detect_break_simple() {
        let break_node = ASTNode::Break {
            span: crate::ast::Span::unknown(),
        };
        assert!(has_break_node(&break_node));
    }

    #[test]
    fn test_empty_body() {
        let empty: Vec<ASTNode> = vec![];
        assert!(!detect_continue_in_body(&empty));
        assert!(!detect_break_in_body(&empty));
    }
}
