//! Pattern 6: Scan with Init (index_of/find/contains form)
//!
//! Phase 254 P0: Dedicated pattern for scan loops with init-time method calls
//!
//! ## Pattern Structure
//!
//! ```nyash
//! index_of(s, ch) {
//!     local i = 0
//!     loop(i < s.length()) {
//!         if s.substring(i, i + 1) == ch {
//!             return i
//!         }
//!         i = i + 1
//!     }
//!     return -1
//! }
//! ```
//!
//! ## Detection Criteria (Structure Only - No Function Names)
//!
//! 1. Loop condition: `i < x.length()` or `i < len`
//! 2. Loop body has if statement with:
//!    - Condition containing MethodCall (e.g., `substring(i, i+1) == ch`)
//!    - Then branch: early return (break)
//! 3. Loop body has step: `i = i + 1`
//! 4. Post-loop: return statement (not-found value)
//!
//! ## Why Not Pattern 2?
//!
//! - Pattern 2 expects break condition without init-time MethodCall
//! - This pattern needs MethodCall in condition (substring)
//! - MethodCall allowed_in_condition() = false, but allowed_in_init() = true
//! - Need to hoist MethodCall to init phase

 // Phase 255 P2: Use shared var() helper
use crate::ast::ASTNode;
use crate::mir::builder::control_flow::joinir::patterns::common::{finalize_extract, ExtractDecision};

// Phase 273 P1: Import DomainPlan types (Plan renamed to DomainPlan)
use crate::mir::builder::control_flow::plan::{DomainPlan, ScanDirection as PlanScanDirection, ScanWithInitPlan};

// Phase 273 P0.1: ScanDirection/ScanWithInitPlan is SSOT in plan/mod.rs

// Phase 273 P0.1: extract_scan_with_init_parts() returns ScanWithInitPlan directly

/// Phase 273 P1: Pure extractor that returns DomainPlan (SSOT)
///
/// This is the new entry point for Pattern6 extraction.
/// Router calls this directly, then passes to Normalizer + Verifier + Lowerer.
///
/// # Returns
///
/// * `Ok(Some(DomainPlan::ScanWithInit(...)))` - Successfully extracted the pattern
/// * `Ok(None)` - Not a scan-with-init pattern (try next pattern)
/// * `Err(String)` - Contract violation (fail-fast)
pub(crate) fn extract_scan_with_init_plan(
    condition: &ASTNode,
    body: &[ASTNode],
    fn_body: Option<&[ASTNode]>,
) -> Result<Option<DomainPlan>, String> {
    // Call internal extraction helper
    let parts = finalize_extract(
        extract_scan_with_init_parts(condition, body, fn_body),
        "phase29ab/pattern6/contract",
    )?;

    // Wrap in DomainPlan if extracted successfully
    Ok(parts.map(DomainPlan::ScanWithInit))
}

// Phase 273 P0.1: can_lower() removed (router now calls extract_scan_with_init_plan() directly)

/// Phase 258 P0: Extract and validate substring window arguments
///
/// Checks if the substring call uses a dynamic window or fixed window:
/// - Fixed: `substring(i, i + 1)` → returns `false`
/// - Dynamic: `substring(i, i + substr.length())` → returns `true`
///
/// # Arguments
///
/// * `substring_call` - The MethodCall AST node for substring()
/// * `loop_var` - The loop index variable name (e.g., "i")
///
/// # Returns
///
/// * `Ok(true)` - Dynamic needle (variable.length())
/// * `Ok(false)` - Fixed needle (literal 1)
/// * `Err(String)` - Invalid substring pattern (not this pattern)
fn extract_substring_window(
    substring_call: &ASTNode,
    loop_var: &str,
) -> Result<bool, String> {
    use crate::ast::{BinaryOperator, LiteralValue};

    // Extract arguments from substring(start, end)
    let args = match substring_call {
        ASTNode::MethodCall { method, arguments, .. } if method == "substring" => arguments,
        _ => return Err("Not a substring call".to_string()),
    };

    if args.len() != 2 {
        return Err(format!("substring expects 2 args, got {}", args.len()));
    }

    // Check arg[0] is loop_var
    match &args[0] {
        ASTNode::Variable { name, .. } if name == loop_var => {}
        _ => return Err("substring start must be loop_var".to_string()),
    }

    // Check arg[1] is loop_var + <expr>
    match &args[1] {
        ASTNode::BinaryOp {
            operator: BinaryOperator::Add,
            left,
            right,
            ..
        } => {
            // Left must be loop_var
            match left.as_ref() {
                ASTNode::Variable { name, .. } if name == loop_var => {}
                _ => return Err("substring end must be loop_var + <expr>".to_string()),
            }

            // Right determines mode
            match right.as_ref() {
                // Fixed: substring(i, i + 1)
                ASTNode::Literal {
                    value: LiteralValue::Integer(1),
                    ..
                } => Ok(false), // Fixed window (ch)

                // Dynamic: substring(i, i + substr.length())
                ASTNode::MethodCall { method, .. } if method == "length" => Ok(true), // Dynamic window (substr)

                // Other patterns not supported
                _ => Err("substring window must be 1 or variable.length()".to_string()),
            }
        }
        _ => Err("substring end must be loop_var + <expr>".to_string()),
    }
}

/// Phase 254 P1: Extract scan-with-init pattern parts from loop AST
///
/// This function analyzes the loop structure and extracts all necessary information
/// for lowering an index_of-style loop to JoinIR.
///
/// # Arguments
///
/// * `condition` - Loop condition AST node
/// * `body` - Loop body statements
/// * `fn_body` - Full function body (needed to check post-loop return)
///
/// # Returns
///
/// * `ExtractDecision::Match(ScanWithInitPlan)` - Successfully extracted the pattern
/// * `ExtractDecision::NotApplicable` - Not a scan-with-init pattern (different pattern)
/// * `ExtractDecision::Contract` - Contract violation
///
/// # P0 Restrictions
///
/// - Loop condition must be `i < s.length()` (forward) or `i >= 0` (reverse)
/// - Step must be `i = i + 1` (forward, step_lit == 1) or `i = i - 1` (reverse, step_lit == -1)
/// - Not-found return must be `-1`
/// - Early return must be `return loop_var`
fn extract_scan_with_init_parts(
    condition: &ASTNode,
    body: &[ASTNode],
    _fn_body: Option<&[ASTNode]>,
) -> ExtractDecision<ScanWithInitPlan> {
    use crate::ast::{BinaryOperator, LiteralValue};

    // 1. Check loop condition: i < s.length() (forward) or i >= 0 (reverse)
    // Phase 258 P0: Also accept i <= s.length() - substr.length() (dynamic needle)
    let (loop_var, haystack_opt, scan_direction) = match condition {
        // Forward (Fixed): i < s.length()
        ASTNode::BinaryOp {
            operator: BinaryOperator::Less,
            left,
            right,
            ..
        } => {
            let loop_var = match left.as_ref() {
                ASTNode::Variable { name, .. } => name.clone(),
                _ => return ExtractDecision::NotApplicable,
            };

            let haystack = match right.as_ref() {
                ASTNode::MethodCall {
                    object, method, ..
                } if method == "length" => match object.as_ref() {
                    ASTNode::Variable { name, .. } => name.clone(),
                    _ => return ExtractDecision::NotApplicable,
                },
                _ => return ExtractDecision::NotApplicable,
            };

            (loop_var, Some(haystack), PlanScanDirection::Forward)
        }
        // Forward (Dynamic): i <= s.length() - substr.length()
        // Phase 258 P0: Accept dynamic needle form for index_of_string
        ASTNode::BinaryOp {
            operator: BinaryOperator::LessEqual,
            left,
            right,
            ..
        } => {
            let loop_var = match left.as_ref() {
                ASTNode::Variable { name, .. } => name.clone(),
                _ => return ExtractDecision::NotApplicable,
            };

            // Right side must be: s.length() - substr.length()
            let haystack = match right.as_ref() {
                ASTNode::BinaryOp {
                    operator: BinaryOperator::Subtract,
                    left: sub_left,
                    right: sub_right,
                    ..
                } => {
                    // Left of subtraction: s.length()
                    let haystack = match sub_left.as_ref() {
                        ASTNode::MethodCall {
                            object, method, ..
                        } if method == "length" => match object.as_ref() {
                            ASTNode::Variable { name, .. } => name.clone(),
                            _ => return ExtractDecision::NotApplicable,
                        },
                        _ => return ExtractDecision::NotApplicable,
                    };

                    // Right of subtraction: substr.length()
                    match sub_right.as_ref() {
                        ASTNode::MethodCall { method, .. } if method == "length" => {
                            // Valid: s.length() - substr.length()
                            haystack
                        }
                        _ => return ExtractDecision::NotApplicable,
                    }
                }
                _ => return ExtractDecision::NotApplicable,
            };

            (loop_var, Some(haystack), PlanScanDirection::Forward)
        }
        // Reverse: i >= 0
        ASTNode::BinaryOp {
            operator: BinaryOperator::GreaterEqual,
            left,
            right,
            ..
        } => {
            let loop_var = match left.as_ref() {
                ASTNode::Variable { name, .. } => name.clone(),
                _ => return ExtractDecision::NotApplicable,
            };

            // Check right is Literal(0)
            match right.as_ref() {
                ASTNode::Literal {
                    value: LiteralValue::Integer(0),
                    ..
                } => {}
                _ => return ExtractDecision::NotApplicable,
            }

            // For reverse, haystack will be extracted from substring call in body
            (loop_var, None, PlanScanDirection::Reverse)
        }
        _ => return ExtractDecision::NotApplicable,
    };

    // 2. Find if statement with substring == needle and return loop_var
    // Also extract haystack for reverse scans
    let mut needle_opt = None;
    let mut early_return_expr_opt = None;
    let mut haystack_from_substring_opt = None;
    let mut dynamic_needle_opt: Option<bool> = None; // Phase 258 P0: Track window mode

    for stmt in body {
        if let ASTNode::If {
            condition: if_cond,
            then_body,
            ..
        } = stmt
        {
            // Check if condition is MethodCall(substring) == Variable(needle)
            if let ASTNode::BinaryOp {
                operator: BinaryOperator::Equal,
                left,
                right,
                ..
            } = if_cond.as_ref()
            {
                let substring_side = if matches!(left.as_ref(), ASTNode::MethodCall { method, .. } if method == "substring")
                {
                    left.as_ref()
                } else if matches!(right.as_ref(), ASTNode::MethodCall { method, .. } if method == "substring")
                {
                    right.as_ref()
                } else {
                    continue;
                };

                let needle_side = if std::ptr::eq(substring_side, left.as_ref()) {
                    right.as_ref()
                } else {
                    left.as_ref()
                };

                // Phase 257 P0: Extract haystack from substring call for reverse scan
                if let ASTNode::MethodCall {
                    object, method, ..
                } = substring_side
                {
                    if method == "substring" {
                        if let ASTNode::Variable { name: haystack_name, .. } = object.as_ref() {
                            haystack_from_substring_opt = Some(haystack_name.clone());
                        }
                    }
                }

                // Phase 258 P0: Validate substring arguments and extract window mode
                match extract_substring_window(substring_side, &loop_var) {
                    Ok(dynamic_needle) => {
                        dynamic_needle_opt = Some(dynamic_needle);
                    }
                    Err(_) => {
                        // Not a valid substring pattern, fall through
                        continue;
                    }
                }

                if let ASTNode::Variable { name: needle_name, .. } = needle_side {
                    // Check then_body contains return loop_var
                    if then_body.len() == 1 {
                        if let ASTNode::Return { value, .. } = &then_body[0] {
                            if let Some(ret_val) = value {
                                if let ASTNode::Variable { name: ret_name, .. } = ret_val.as_ref() {
                                    if ret_name == &loop_var {
                                        needle_opt = Some(needle_name.clone());
                                        early_return_expr_opt = Some(ret_val.as_ref().clone());
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    // Phase 273 P2: Return NotApplicable if pattern doesn't match (allow Pattern7 to try)
    let needle = match needle_opt {
        Some(n) => n,
        None => return ExtractDecision::NotApplicable, // Not Pattern6, try next pattern
    };
    let early_return_expr = match early_return_expr_opt {
        Some(e) => e,
        None => return ExtractDecision::NotApplicable, // Not Pattern6, try next pattern
    };

    // Phase 257 P0: Determine haystack based on scan direction
    let haystack = match scan_direction {
        PlanScanDirection::Forward => match haystack_opt {
            Some(value) => value,
            None => {
                return ExtractDecision::contract(
                    "scan-with-init contract: forward scan missing haystack",
                    "use `i < s.length()` or `i <= s.length() - needle.length()` for forward scans",
                );
            }
        },
        PlanScanDirection::Reverse => match haystack_from_substring_opt {
            Some(value) => value,
            None => {
                return ExtractDecision::contract(
                    "scan-with-init contract: reverse scan missing haystack",
                    "use `s.substring(i, i + 1)` (or dynamic) in the match condition",
                );
            }
        },
    };

    // 3. Check for step: i = i + 1 (forward) or i = i - 1 (reverse)
    let mut step_lit_opt = None;

    for stmt in body {
        if let ASTNode::Assignment { target, value, .. } = stmt {
            if let ASTNode::Variable { name: target_name, .. } = target.as_ref() {
                if target_name == &loop_var {
                    if let ASTNode::BinaryOp {
                        operator,
                        left,
                        right,
                        ..
                    } = value.as_ref()
                    {
                        if let ASTNode::Variable { name: left_name, .. } = left.as_ref() {
                            if left_name == &loop_var {
                                if let ASTNode::Literal {
                                    value: LiteralValue::Integer(lit),
                                    ..
                                } = right.as_ref()
                                {
                                    match operator {
                                        BinaryOperator::Add => {
                                            step_lit_opt = Some(*lit);
                                        }
                                        BinaryOperator::Subtract => {
                                            step_lit_opt = Some(-lit);
                                        }
                                        _ => {}
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    let step_lit = match step_lit_opt {
        Some(value) => value,
        None => {
            return ExtractDecision::contract(
                "scan-with-init contract: missing step update",
                "add `i = i + 1` (forward) or `i = i - 1` (reverse) inside the loop",
            );
        }
    };

    // Phase 257 P0: Verify step matches scan direction
    match scan_direction {
        PlanScanDirection::Forward => {
            if step_lit != 1 {
                return ExtractDecision::contract(
                    "scan-with-init contract: forward step must be `i = i + 1`",
                    "change the step update to `i = i + 1`",
                );
            }
        }
        PlanScanDirection::Reverse => {
            if step_lit != -1 {
                return ExtractDecision::contract(
                    "scan-with-init contract: reverse step must be `i = i - 1`",
                    "change the step update to `i = i - 1`",
                );
            }
        }
    }

    // 4. P0: not-found return must be -1 (hardcoded for now)
    let not_found_return_lit = -1;

    // Phase 258 P0: Extract dynamic_needle (default to false for backward compat)
    let dynamic_needle = dynamic_needle_opt.unwrap_or(false);

    ExtractDecision::Match(ScanWithInitPlan {
        loop_var,
        haystack,
        needle,
        step_lit,
        early_return_expr,
        not_found_return_lit,
        scan_direction,
        dynamic_needle,
    })
}

// Phase 273 P0.1: lower() removed (router now uses PlanLowerer::lower_scan_with_init())

// Phase 273 P0.1: cf_loop_pattern6_scan_with_init_impl() removed (migrated to plan/lowerer.rs)
// The implementation is now in PlanLowerer::lower_scan_with_init()
