//! Phase 33-23: Pattern 4 Carrier Analysis
//!
//! Extracts carrier analysis logic from pattern4_with_continue.rs.
//! Responsible for:
//! - Identifying which carriers are updated by continue branches
//! - Normalizing else-continue clauses
//! - Filtering out invalid carriers
//!
//! # Design Philosophy
//!
//! - **Single responsibility**: Focus only on carrier analysis for Pattern 4
//! - **Reusability**: Can be used by future continue-pattern variants
//! - **Testability**: Pure functions that are easy to unit test
//! - **Independence**: Does not depend on MirBuilder context

use crate::ast::ASTNode;
use crate::mir::join_ir::lowering::carrier_info::{CarrierInfo, CarrierVar};
use crate::mir::join_ir::lowering::continue_branch_normalizer::ContinueBranchNormalizer;
use crate::mir::join_ir::lowering::loop_update_analyzer::{LoopUpdateAnalyzer, UpdateExpr};
use std::collections::BTreeMap; // Phase 222.5-D: HashMap → BTreeMap for determinism

pub(crate) struct Pattern4CarrierAnalyzer;

impl Pattern4CarrierAnalyzer {
    /// Analyze and filter carriers for continue pattern
    ///
    /// Identifies which carriers are actually updated by continue branches
    /// and filters out carriers that don't participate in the loop body.
    ///
    /// # Arguments
    ///
    /// * `loop_body` - The loop body AST nodes to analyze
    /// * `all_carriers` - All potential carrier variables from initial analysis
    ///
    /// # Returns
    ///
    /// CarrierInfo containing only the carriers that are actually updated
    ///
    /// # Example
    ///
    /// ```nyash
    /// local i = 0
    /// local sum = 0
    /// local M = 10  // Constant, should be filtered out
    /// loop(i < M) {
    ///   i = i + 1
    ///   if i % 2 == 0 { continue }
    ///   sum = sum + i
    /// }
    /// // Result: carriers = [i, sum], NOT [i, sum, M]
    /// ```
    pub fn analyze_carriers(
        loop_body: &[ASTNode],
        all_carriers: &CarrierInfo,
    ) -> Result<CarrierInfo, String> {
        // Identify which carriers are updated in loop body
        let carrier_updates =
            LoopUpdateAnalyzer::analyze_carrier_updates(loop_body, &all_carriers.carriers);

        // Filter carriers: only keep those that have update expressions
        let updated_carriers: Vec<CarrierVar> = all_carriers
            .carriers
            .iter()
            .filter(|carrier| carrier_updates.contains_key(&carrier.name))
            .cloned()
            .collect();

        Ok(CarrierInfo {
            loop_var_name: all_carriers.loop_var_name.clone(),
            loop_var_id: all_carriers.loop_var_id,
            carriers: updated_carriers,
            trim_helper: all_carriers.trim_helper.clone(),
            promoted_loopbodylocals: all_carriers.promoted_loopbodylocals.clone(), // Phase 224
            #[cfg(feature = "normalized_dev")]
            promoted_bindings: all_carriers.promoted_bindings.clone(), // Phase 76
        })
    }

    /// Analyze carrier update expressions
    ///
    /// Delegates to LoopUpdateAnalyzer but returns the result in a more
    /// convenient form for Pattern 4 processing.
    ///
    /// # Returns
    ///
    /// Map from carrier name to UpdateExpr
    pub fn analyze_carrier_updates(
        loop_body: &[ASTNode],
        carriers: &[CarrierVar],
    ) -> BTreeMap<String, UpdateExpr> {
        // Phase 222.5-D: HashMap → BTreeMap for determinism
        LoopUpdateAnalyzer::analyze_carrier_updates(loop_body, carriers)
    }

    /// Normalize continue branches to standard form
    ///
    /// Transforms else-continue patterns to a canonical form
    /// for easier JoinIR lowering.
    ///
    /// # Pattern
    ///
    /// Transforms: `if (cond) { body } else { continue }`
    /// Into:      `if (!cond) { continue } else { body }`
    ///
    /// # Arguments
    ///
    /// * `body` - Loop body statements to normalize
    ///
    /// # Returns
    ///
    /// Normalized loop body with all continue statements in then branches
    pub fn normalize_continue_branches(body: &[ASTNode]) -> Vec<ASTNode> {
        ContinueBranchNormalizer::normalize_loop_body(body)
    }

    /// Verify continue pattern structure
    ///
    /// Ensures the loop has proper continue structure for Pattern 4.
    ///
    /// # Arguments
    ///
    /// * `body` - Loop body statements to validate
    ///
    /// # Returns
    ///
    /// Ok(()) if continue structure is valid, Err(message) otherwise
    #[allow(dead_code)]
    pub fn validate_continue_structure(body: &[ASTNode]) -> Result<(), String> {
        // Check for at least one continue statement
        for stmt in body {
            if Self::has_continue(stmt) {
                return Ok(());
            }
        }
        Err("No continue statement found in loop body".to_string())
    }

    /// Helper: Check if node or its children contain continue
    ///
    /// Recursively searches the AST for continue statements.
    ///
    /// # Arguments
    ///
    /// * `node` - AST node to check
    ///
    /// # Returns
    ///
    /// true if the node or any of its children is a Continue statement
    #[allow(dead_code)]
    fn has_continue(node: &ASTNode) -> bool {
        match node {
            ASTNode::Continue { .. } => true,
            ASTNode::If {
                then_body,
                else_body,
                ..
            } => {
                then_body.iter().any(|n| Self::has_continue(n))
                    || else_body
                        .as_ref()
                        .map_or(false, |body| body.iter().any(|n| Self::has_continue(n)))
            }
            ASTNode::Loop { body, .. } => body.iter().any(|n| Self::has_continue(n)),
            _ => false,
        }
    }
}

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

    #[test]
    fn test_validate_continue_structure_present() {
        // Test with continue statement
        let body = vec![ASTNode::Continue {
            span: Span::unknown(),
        }];
        assert!(Pattern4CarrierAnalyzer::validate_continue_structure(&body).is_ok());
    }

    #[test]
    fn test_validate_continue_structure_absent() {
        // Test without continue statement
        let body = vec![ASTNode::Break {
            span: Span::unknown(),
        }];
        assert!(Pattern4CarrierAnalyzer::validate_continue_structure(&body).is_err());
    }

    #[test]
    fn test_has_continue_detection() {
        let continue_node = ASTNode::Continue {
            span: Span::unknown(),
        };
        assert!(Pattern4CarrierAnalyzer::has_continue(&continue_node));

        let break_node = ASTNode::Break {
            span: Span::unknown(),
        };
        assert!(!Pattern4CarrierAnalyzer::has_continue(&break_node));
    }

    #[test]
    fn test_has_continue_nested_in_if() {
        // if (x) { continue } else { ... }
        let if_node = ASTNode::If {
            condition: Box::new(ASTNode::Variable {
                name: "x".to_string(),
                span: Span::unknown(),
            }),
            then_body: vec![ASTNode::Continue {
                span: Span::unknown(),
            }],
            else_body: None,
            span: Span::unknown(),
        };
        assert!(Pattern4CarrierAnalyzer::has_continue(&if_node));
    }

    #[test]
    fn test_analyze_carriers_filtering() {
        // Test that analyze_carriers filters out non-updated carriers
        let span = Span::unknown();

        // Create loop body: i = i + 1, sum = sum + i
        let loop_body = vec![
            ASTNode::Assignment {
                target: Box::new(ASTNode::Variable {
                    name: "i".to_string(),
                    span: span.clone(),
                }),
                value: Box::new(ASTNode::BinaryOp {
                    operator: BinaryOperator::Add,
                    left: Box::new(ASTNode::Variable {
                        name: "i".to_string(),
                        span: span.clone(),
                    }),
                    right: Box::new(ASTNode::Literal {
                        value: LiteralValue::Integer(1),
                        span: span.clone(),
                    }),
                    span: span.clone(),
                }),
                span: span.clone(),
            },
            ASTNode::Assignment {
                target: Box::new(ASTNode::Variable {
                    name: "sum".to_string(),
                    span: span.clone(),
                }),
                value: Box::new(ASTNode::BinaryOp {
                    operator: BinaryOperator::Add,
                    left: Box::new(ASTNode::Variable {
                        name: "sum".to_string(),
                        span: span.clone(),
                    }),
                    right: Box::new(ASTNode::Variable {
                        name: "i".to_string(),
                        span: span.clone(),
                    }),
                    span: span.clone(),
                }),
                span: span.clone(),
            },
        ];

        // Create CarrierInfo with i, sum, and M (constant)
        let all_carriers = CarrierInfo {
            loop_var_name: "i".to_string(),
            loop_var_id: ValueId(0),
            carriers: vec![
                CarrierVar {
                    name: "i".to_string(),
                    host_id: ValueId(1),
                    join_id: None,
                    role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
                    init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
                    #[cfg(feature = "normalized_dev")]
                    binding_id: None,
                },
                CarrierVar {
                    name: "sum".to_string(),
                    host_id: ValueId(2),
                    join_id: None,
                    role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
                    init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
                    #[cfg(feature = "normalized_dev")]
                    binding_id: None,
                },
                CarrierVar {
                    name: "M".to_string(),
                    host_id: ValueId(3),
                    join_id: None,
                    role: crate::mir::join_ir::lowering::carrier_info::CarrierRole::LoopState,
                    init: crate::mir::join_ir::lowering::carrier_info::CarrierInit::FromHost, // Phase 228
                    #[cfg(feature = "normalized_dev")]
                    binding_id: None,
                },
            ],
            trim_helper: None,
            promoted_loopbodylocals: Vec::new(), // Phase 224
            #[cfg(feature = "normalized_dev")]
            promoted_bindings: std::collections::BTreeMap::new(), // Phase 76
        };

        // Analyze carriers
        let result = Pattern4CarrierAnalyzer::analyze_carriers(&loop_body, &all_carriers);
        assert!(result.is_ok());

        let filtered = result.unwrap();
        // Should only have i and sum, not M
        assert_eq!(filtered.carriers.len(), 2);
        assert!(filtered.carriers.iter().any(|c| c.name == "i"));
        assert!(filtered.carriers.iter().any(|c| c.name == "sum"));
        assert!(!filtered.carriers.iter().any(|c| c.name == "M"));
    }

    #[test]
    fn test_normalize_continue_branches() {
        // Test normalization delegation
        let span = Span::unknown();

        // Create if-else-continue pattern
        let body = vec![ASTNode::If {
            condition: Box::new(ASTNode::Variable {
                name: "x".to_string(),
                span: span.clone(),
            }),
            then_body: vec![ASTNode::Assignment {
                target: Box::new(ASTNode::Variable {
                    name: "y".to_string(),
                    span: span.clone(),
                }),
                value: Box::new(ASTNode::Literal {
                    value: LiteralValue::Integer(1),
                    span: span.clone(),
                }),
                span: span.clone(),
            }],
            else_body: Some(vec![ASTNode::Continue { span: span.clone() }]),
            span: span.clone(),
        }];

        let normalized = Pattern4CarrierAnalyzer::normalize_continue_branches(&body);

        // Should be transformed to if (!x) { continue } else { y = 1 }
        assert_eq!(normalized.len(), 1);
        if let ASTNode::If {
            condition,
            then_body,
            ..
        } = &normalized[0]
        {
            // Condition should be negated
            assert!(matches!(**condition, ASTNode::UnaryOp { .. }));
            // Then body should be continue
            assert_eq!(then_body.len(), 1);
            assert!(matches!(then_body[0], ASTNode::Continue { .. }));
        } else {
            panic!("Expected If node");
        }
    }
}
