//! Condition Expression Lowerer
//!
//! This module provides the core logic for lowering AST condition expressions
//! to JoinIR instructions. It handles comparisons, logical operators, and
//! arithmetic expressions.
//!
//! ## Design Philosophy
//!
//! **Single Responsibility**: This module ONLY performs AST → JoinIR lowering.
//! It does NOT:
//! - Manage variable environments (that's condition_env.rs)
//! - Extract variables from AST (that's condition_var_extractor.rs)
//! - Manage HOST ↔ JoinIR bindings (that's inline_boundary.rs)

use crate::ast::{ASTNode, BinaryOperator, LiteralValue, UnaryOperator};
use crate::mir::join_ir::{BinOpKind, CompareOp, ConstValue, JoinInst, MirLikeInst, UnaryOp};
use crate::mir::ValueId;

use super::condition_env::ConditionEnv;
use super::loop_body_local_env::LoopBodyLocalEnv; // Phase 92 P2-2: Body-local support
use super::method_call_lowerer::MethodCallLowerer;

/// Lower an AST condition to JoinIR instructions
///
/// # Arguments
///
/// * `cond_ast` - AST node representing the boolean condition
/// * `alloc_value` - ValueId allocator function
/// * `env` - ConditionEnv for variable resolution (JoinIR-local ValueIds)
/// * `body_local_env` - Phase 92 P2-2: Optional body-local variable environment
///
/// # Returns
///
/// * `Ok((ValueId, Vec<JoinInst>))` - Condition result ValueId and evaluation instructions
/// * `Err(String)` - Lowering error message
///
/// # Supported Patterns
///
/// - Comparisons: `i < n`, `x == y`, `a != b`, `x <= y`, `x >= y`, `x > y`
/// - Logical: `a && b`, `a || b`, `!cond`
/// - Variables and literals
///
/// # Phase 92 P2-2: Body-Local Variable Support
///
/// When lowering conditions that reference body-local variables (e.g., `ch == '\\'`
/// in escape patterns), the `body_local_env` parameter provides name → ValueId
/// mappings for variables defined in the loop body.
///
/// Variable resolution priority:
/// 1. ConditionEnv (loop parameters, captured variables)
/// 2. LoopBodyLocalEnv (body-local variables like `ch`)
///
/// # Example
///
/// ```ignore
/// let mut env = ConditionEnv::new();
/// env.insert("i".to_string(), ValueId(0));
/// env.insert("end".to_string(), ValueId(1));
///
/// let mut body_env = LoopBodyLocalEnv::new();
/// body_env.insert("ch".to_string(), ValueId(5)); // Phase 92 P2-2
///
/// let mut value_counter = 2u32;
/// let mut alloc_value = || {
///     let id = ValueId(value_counter);
///     value_counter += 1;
///     id
/// };
///
/// // Lower condition: ch == '\\'
/// let (cond_value, cond_insts) = lower_condition_to_joinir(
///     condition_ast,
///     &mut alloc_value,
///     &env,
///     Some(&body_env), // Phase 92 P2-2: Body-local support
/// )?;
/// ```
pub fn lower_condition_to_joinir(
    cond_ast: &ASTNode,
    alloc_value: &mut dyn FnMut() -> ValueId,
    env: &ConditionEnv,
    body_local_env: Option<&LoopBodyLocalEnv>, // Phase 92 P2-2
) -> Result<(ValueId, Vec<JoinInst>), String> {
    let mut instructions = Vec::new();
    let result_value = lower_condition_recursive(cond_ast, alloc_value, env, body_local_env, &mut instructions)?;
    Ok((result_value, instructions))
}

/// Convenience wrapper: lower a condition without body-local support.
pub fn lower_condition_to_joinir_no_body_locals(
    cond_ast: &ASTNode,
    alloc_value: &mut dyn FnMut() -> ValueId,
    env: &ConditionEnv,
) -> Result<(ValueId, Vec<JoinInst>), String> {
    lower_condition_to_joinir(cond_ast, alloc_value, env, None)
}

/// Recursive helper for condition lowering
///
/// Handles all supported AST node types and emits appropriate JoinIR instructions.
///
/// # Phase 92 P2-2
///
/// Added `body_local_env` parameter to support body-local variable resolution.
fn lower_condition_recursive(
    cond_ast: &ASTNode,
    alloc_value: &mut dyn FnMut() -> ValueId,
    env: &ConditionEnv,
    body_local_env: Option<&LoopBodyLocalEnv>, // Phase 92 P2-2
    instructions: &mut Vec<JoinInst>,
) -> Result<ValueId, String> {
    match cond_ast {
        // Comparison operations: <, ==, !=, <=, >=, >
        ASTNode::BinaryOp {
            operator,
            left,
            right,
            ..
        } => match operator {
            BinaryOperator::Less
            | BinaryOperator::Equal
            | BinaryOperator::NotEqual
            | BinaryOperator::LessEqual
            | BinaryOperator::GreaterEqual
            | BinaryOperator::Greater => {
                lower_comparison(operator, left, right, alloc_value, env, body_local_env, instructions)
            }
            BinaryOperator::And => lower_logical_and(left, right, alloc_value, env, body_local_env, instructions),
            BinaryOperator::Or => lower_logical_or(left, right, alloc_value, env, body_local_env, instructions),
            _ => Err(format!(
                "Unsupported binary operator in condition: {:?}",
                operator
            )),
        },

        // Unary NOT operator
        ASTNode::UnaryOp {
            operator: UnaryOperator::Not,
            operand,
            ..
        } => lower_not_operator(operand, alloc_value, env, body_local_env, instructions),

        // Phase 92 P2-2: Variables - resolve from ConditionEnv or LoopBodyLocalEnv
        ASTNode::Variable { name, .. } => {
            // Priority 1: ConditionEnv (loop parameters, captured variables)
            if let Some(value_id) = env.get(name) {
                return Ok(value_id);
            }
            // Priority 2: LoopBodyLocalEnv (body-local variables like `ch`)
            if let Some(body_env) = body_local_env {
                if let Some(value_id) = body_env.get(name) {
                    return Ok(value_id);
                }
            }
            Err(format!("Variable '{}' not found in ConditionEnv or LoopBodyLocalEnv", name))
        }

        // Literals - emit as constants
        ASTNode::Literal { value, .. } => lower_literal(value, alloc_value, instructions),

        _ => Err(format!("Unsupported AST node in condition: {:?}", cond_ast)),
    }
}

/// Lower a comparison operation (e.g., `i < end`)
fn lower_comparison(
    operator: &BinaryOperator,
    left: &ASTNode,
    right: &ASTNode,
    alloc_value: &mut dyn FnMut() -> ValueId,
    env: &ConditionEnv,
    body_local_env: Option<&LoopBodyLocalEnv>, // Phase 92 P2-2
    instructions: &mut Vec<JoinInst>,
) -> Result<ValueId, String> {
    // Lower left and right sides
    let lhs = lower_value_expression(left, alloc_value, env, body_local_env, instructions)?;
    let rhs = lower_value_expression(right, alloc_value, env, body_local_env, instructions)?;
    let dst = alloc_value();

    let cmp_op = match operator {
        BinaryOperator::Less => CompareOp::Lt,
        BinaryOperator::Equal => CompareOp::Eq,
        BinaryOperator::NotEqual => CompareOp::Ne,
        BinaryOperator::LessEqual => CompareOp::Le,
        BinaryOperator::GreaterEqual => CompareOp::Ge,
        BinaryOperator::Greater => CompareOp::Gt,
        _ => unreachable!(),
    };

    // Emit Compare instruction
    instructions.push(JoinInst::Compute(MirLikeInst::Compare {
        dst,
        op: cmp_op,
        lhs,
        rhs,
    }));

    Ok(dst)
}

/// Lower logical AND operation (e.g., `a && b`)
fn lower_logical_and(
    left: &ASTNode,
    right: &ASTNode,
    alloc_value: &mut dyn FnMut() -> ValueId,
    env: &ConditionEnv,
    body_local_env: Option<&LoopBodyLocalEnv>, // Phase 92 P2-2
    instructions: &mut Vec<JoinInst>,
) -> Result<ValueId, String> {
    // Logical AND: evaluate both sides and combine
    let lhs = lower_condition_recursive(left, alloc_value, env, body_local_env, instructions)?;
    let rhs = lower_condition_recursive(right, alloc_value, env, body_local_env, instructions)?;
    let dst = alloc_value();

    // Emit BinOp And instruction
    instructions.push(JoinInst::Compute(MirLikeInst::BinOp {
        dst,
        op: BinOpKind::And,
        lhs,
        rhs,
    }));

    Ok(dst)
}

/// Lower logical OR operation (e.g., `a || b`)
fn lower_logical_or(
    left: &ASTNode,
    right: &ASTNode,
    alloc_value: &mut dyn FnMut() -> ValueId,
    env: &ConditionEnv,
    body_local_env: Option<&LoopBodyLocalEnv>, // Phase 92 P2-2
    instructions: &mut Vec<JoinInst>,
) -> Result<ValueId, String> {
    // Logical OR: evaluate both sides and combine
    let lhs = lower_condition_recursive(left, alloc_value, env, body_local_env, instructions)?;
    let rhs = lower_condition_recursive(right, alloc_value, env, body_local_env, instructions)?;
    let dst = alloc_value();

    // Emit BinOp Or instruction
    instructions.push(JoinInst::Compute(MirLikeInst::BinOp {
        dst,
        op: BinOpKind::Or,
        lhs,
        rhs,
    }));

    Ok(dst)
}

/// Lower NOT operator (e.g., `!cond`)
fn lower_not_operator(
    operand: &ASTNode,
    alloc_value: &mut dyn FnMut() -> ValueId,
    env: &ConditionEnv,
    body_local_env: Option<&LoopBodyLocalEnv>, // Phase 92 P2-2
    instructions: &mut Vec<JoinInst>,
) -> Result<ValueId, String> {
    let operand_val = lower_condition_recursive(operand, alloc_value, env, body_local_env, instructions)?;
    let dst = alloc_value();

    // Emit UnaryOp Not instruction
    instructions.push(JoinInst::Compute(MirLikeInst::UnaryOp {
        dst,
        op: UnaryOp::Not,
        operand: operand_val,
    }));

    Ok(dst)
}

/// Lower a literal value (e.g., `10`, `true`, `"text"`)
fn lower_literal(
    value: &LiteralValue,
    alloc_value: &mut dyn FnMut() -> ValueId,
    instructions: &mut Vec<JoinInst>,
) -> Result<ValueId, String> {
    let dst = alloc_value();
    let const_value = match value {
        LiteralValue::Integer(n) => ConstValue::Integer(*n),
        LiteralValue::String(s) => ConstValue::String(s.clone()),
        LiteralValue::Bool(b) => ConstValue::Bool(*b),
        LiteralValue::Float(_) => {
            return Err("Float literals not supported in JoinIR conditions yet".to_string());
        }
        _ => {
            return Err(format!(
                "Unsupported literal type in condition: {:?}",
                value
            ));
        }
    };

    instructions.push(JoinInst::Compute(MirLikeInst::Const {
        dst,
        value: const_value,
    }));

    Ok(dst)
}

/// Lower a value expression (for comparison operands, etc.)
///
/// This handles the common case where we need to evaluate a simple value
/// (variable or literal) as part of a comparison.
///
/// # Phase 92 P2-2
///
/// Added `body_local_env` parameter to support body-local variable resolution
/// (e.g., `ch` in `ch == '\\'`).
pub fn lower_value_expression(
    expr: &ASTNode,
    alloc_value: &mut dyn FnMut() -> ValueId,
    env: &ConditionEnv,
    body_local_env: Option<&LoopBodyLocalEnv>, // Phase 92 P2-2
    instructions: &mut Vec<JoinInst>,
) -> Result<ValueId, String> {
    match expr {
        // Phase 92 P2-2: Variables - resolve from ConditionEnv or LoopBodyLocalEnv
        ASTNode::Variable { name, .. } => {
            // Priority 1: ConditionEnv (loop parameters, captured variables)
            if let Some(value_id) = env.get(name) {
                return Ok(value_id);
            }
            // Priority 2: LoopBodyLocalEnv (body-local variables like `ch`)
            if let Some(body_env) = body_local_env {
                if let Some(value_id) = body_env.get(name) {
                    return Ok(value_id);
                }
            }
            Err(format!("Variable '{}' not found in ConditionEnv or LoopBodyLocalEnv", name))
        }

        // Literals - emit as constants
        ASTNode::Literal { value, .. } => lower_literal(value, alloc_value, instructions),

        // Binary operations (for arithmetic in conditions like i + 1 < n)
        ASTNode::BinaryOp {
            operator,
            left,
            right,
            ..
        } => lower_arithmetic_binop(operator, left, right, alloc_value, env, body_local_env, instructions),

        // Phase 224-C: MethodCall support with arguments (e.g., s.length(), s.indexOf(ch))
        ASTNode::MethodCall {
            object,
            method,
            arguments,
            ..
        } => {
            // 1. Lower receiver (object) to ValueId
            let recv_val = lower_value_expression(object, alloc_value, env, body_local_env, instructions)?;

            // 2. Lower method call using MethodCallLowerer (will lower arguments internally)
            MethodCallLowerer::lower_for_condition(
                recv_val,
                method,
                arguments,
                alloc_value,
                env,
                instructions,
            )
        }

        _ => Err(format!(
            "Unsupported expression in value context: {:?}",
            expr
        )),
    }
}

/// Lower an arithmetic binary operation (e.g., `i + 1`)
fn lower_arithmetic_binop(
    operator: &BinaryOperator,
    left: &ASTNode,
    right: &ASTNode,
    alloc_value: &mut dyn FnMut() -> ValueId,
    env: &ConditionEnv,
    body_local_env: Option<&LoopBodyLocalEnv>, // Phase 92 P2-2
    instructions: &mut Vec<JoinInst>,
) -> Result<ValueId, String> {
    let lhs = lower_value_expression(left, alloc_value, env, body_local_env, instructions)?;
    let rhs = lower_value_expression(right, alloc_value, env, body_local_env, instructions)?;
    let dst = alloc_value();

    let bin_op = match operator {
        BinaryOperator::Add => BinOpKind::Add,
        BinaryOperator::Subtract => BinOpKind::Sub,
        BinaryOperator::Multiply => BinOpKind::Mul,
        BinaryOperator::Divide => BinOpKind::Div,
        BinaryOperator::Modulo => BinOpKind::Mod,
        _ => {
            return Err(format!(
                "Unsupported binary operator in expression: {:?}",
                operator
            ));
        }
    };

    instructions.push(JoinInst::Compute(MirLikeInst::BinOp {
        dst,
        op: bin_op,
        lhs,
        rhs,
    }));

    Ok(dst)
}

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

    /// Helper to create a test ConditionEnv with variables
    fn create_test_env() -> ConditionEnv {
        let mut env = ConditionEnv::new();
        // Register test variables (using JoinIR-local ValueIds)
        env.insert("i".to_string(), ValueId(0));
        env.insert("end".to_string(), ValueId(1));
        env
    }

    #[test]
    fn test_simple_comparison() {
        let env = create_test_env();
        let mut value_counter = 2u32; // Start after i=0, end=1
        let mut alloc_value = || {
            let id = ValueId(value_counter);
            value_counter += 1;
            id
        };

        // AST: i < end
        let ast = ASTNode::BinaryOp {
            operator: BinaryOperator::Less,
            left: Box::new(ASTNode::Variable {
                name: "i".to_string(),
                span: Span::unknown(),
            }),
            right: Box::new(ASTNode::Variable {
                name: "end".to_string(),
                span: Span::unknown(),
            }),
            span: Span::unknown(),
        };

        let result = lower_condition_to_joinir_no_body_locals(&ast, &mut alloc_value, &env);
        assert!(result.is_ok(), "Simple comparison should succeed");

        let (_cond_value, instructions) = result.unwrap();
        assert_eq!(
            instructions.len(),
            1,
            "Should generate 1 Compare instruction"
        );
    }

    #[test]
    fn test_comparison_with_literal() {
        let env = create_test_env();
        let mut value_counter = 2u32;
        let mut alloc_value = || {
            let id = ValueId(value_counter);
            value_counter += 1;
            id
        };

        // AST: i < 10
        let ast = ASTNode::BinaryOp {
            operator: BinaryOperator::Less,
            left: Box::new(ASTNode::Variable {
                name: "i".to_string(),
                span: Span::unknown(),
            }),
            right: Box::new(ASTNode::Literal {
                value: LiteralValue::Integer(10),
                span: Span::unknown(),
            }),
            span: Span::unknown(),
        };

        let result = lower_condition_to_joinir_no_body_locals(&ast, &mut alloc_value, &env);
        assert!(result.is_ok(), "Comparison with literal should succeed");

        let (_cond_value, instructions) = result.unwrap();
        // Should have: Const(10), Compare
        assert_eq!(instructions.len(), 2, "Should generate Const + Compare");
    }

    #[test]
    fn test_logical_or() {
        let mut env = ConditionEnv::new();
        env.insert("a".to_string(), ValueId(2));
        env.insert("b".to_string(), ValueId(3));

        let mut value_counter = 4u32;
        let mut alloc_value = || {
            let id = ValueId(value_counter);
            value_counter += 1;
            id
        };

        // AST: a < 5 || b < 5
        let ast = ASTNode::BinaryOp {
            operator: BinaryOperator::Or,
            left: Box::new(ASTNode::BinaryOp {
                operator: BinaryOperator::Less,
                left: Box::new(ASTNode::Variable {
                    name: "a".to_string(),
                    span: Span::unknown(),
                }),
                right: Box::new(ASTNode::Literal {
                    value: LiteralValue::Integer(5),
                    span: Span::unknown(),
                }),
                span: Span::unknown(),
            }),
            right: Box::new(ASTNode::BinaryOp {
                operator: BinaryOperator::Less,
                left: Box::new(ASTNode::Variable {
                    name: "b".to_string(),
                    span: Span::unknown(),
                }),
                right: Box::new(ASTNode::Literal {
                    value: LiteralValue::Integer(5),
                    span: Span::unknown(),
                }),
                span: Span::unknown(),
            }),
            span: Span::unknown(),
        };

        let result = lower_condition_to_joinir_no_body_locals(&ast, &mut alloc_value, &env);
        assert!(result.is_ok(), "OR expression should succeed");

        let (_cond_value, instructions) = result.unwrap();
        // Should have: Const(5), Compare(a<5), Const(5), Compare(b<5), BinOp(Or)
        assert_eq!(instructions.len(), 5, "Should generate proper OR chain");
    }

    #[test]
    fn test_not_operator() {
        let env = create_test_env();
        let mut value_counter = 2u32;
        let mut alloc_value = || {
            let id = ValueId(value_counter);
            value_counter += 1;
            id
        };

        // AST: !(i < end)
        let ast = ASTNode::UnaryOp {
            operator: UnaryOperator::Not,
            operand: Box::new(ASTNode::BinaryOp {
                operator: BinaryOperator::Less,
                left: Box::new(ASTNode::Variable {
                    name: "i".to_string(),
                    span: Span::unknown(),
                }),
                right: Box::new(ASTNode::Variable {
                    name: "end".to_string(),
                    span: Span::unknown(),
                }),
                span: Span::unknown(),
            }),
            span: Span::unknown(),
        };

        let result = lower_condition_to_joinir_no_body_locals(&ast, &mut alloc_value, &env);
        assert!(result.is_ok(), "NOT operator should succeed");

        let (_cond_value, instructions) = result.unwrap();
        // Should have: Compare, UnaryOp(Not)
        assert_eq!(instructions.len(), 2, "Should generate Compare + Not");
    }

    /// Phase 92 P4 Level 2: Test body-local variable resolution
    ///
    /// This test verifies that conditions can reference body-local variables
    /// (e.g., `ch == '\\'` in escape sequence patterns).
    ///
    /// Variable resolution priority:
    /// 1. ConditionEnv (loop parameters, captured variables)
    /// 2. LoopBodyLocalEnv (body-local variables like `ch`)
    #[test]
    fn test_body_local_variable_resolution() {
        // Setup ConditionEnv with loop variable
        let mut env = ConditionEnv::new();
        env.insert("i".to_string(), ValueId(100));

        // Setup LoopBodyLocalEnv with body-local variable
        let mut body_local_env = LoopBodyLocalEnv::new();
        body_local_env.insert("ch".to_string(), ValueId(200));

        let mut value_counter = 300u32;
        let mut alloc_value = || {
            let id = ValueId(value_counter);
            value_counter += 1;
            id
        };

        // AST: ch == "\\"
        let ast = ASTNode::BinaryOp {
            operator: BinaryOperator::Equal,
            left: Box::new(ASTNode::Variable {
                name: "ch".to_string(),
                span: Span::unknown(),
            }),
            right: Box::new(ASTNode::Literal {
                value: LiteralValue::String("\\".to_string()),
                span: Span::unknown(),
            }),
            span: Span::unknown(),
        };

        // Phase 92 P2-2: Use lower_condition_to_joinir with body_local_env
        let result = lower_condition_to_joinir(&ast, &mut alloc_value, &env, Some(&body_local_env));
        assert!(
            result.is_ok(),
            "Body-local variable resolution should succeed"
        );

        let (cond_value, instructions) = result.unwrap();
        // Should have: Const("\\"), Compare(ch == "\\")
        assert_eq!(
            instructions.len(),
            2,
            "Should generate Const + Compare for body-local variable"
        );

        // Verify the comparison uses the body-local variable's ValueId(200)
        if let Some(JoinInst::Compute(MirLikeInst::Compare { lhs, .. })) = instructions.get(1) {
            assert_eq!(
                *lhs,
                ValueId(200),
                "Compare should use body-local variable ValueId(200)"
            );
        } else {
            panic!("Expected Compare instruction at position 1");
        }

        assert!(cond_value.0 >= 300, "Result should use newly allocated ValueId");
    }

    /// Phase 92 P4 Level 2: Test variable resolution priority (ConditionEnv takes precedence)
    ///
    /// When a variable exists in both ConditionEnv and LoopBodyLocalEnv,
    /// ConditionEnv should take priority.
    #[test]
    fn test_variable_resolution_priority() {
        // Setup both environments with overlapping variable "x"
        let mut env = ConditionEnv::new();
        env.insert("x".to_string(), ValueId(100)); // ConditionEnv priority

        let mut body_local_env = LoopBodyLocalEnv::new();
        body_local_env.insert("x".to_string(), ValueId(200)); // Should be shadowed

        let mut value_counter = 300u32;
        let mut alloc_value = || {
            let id = ValueId(value_counter);
            value_counter += 1;
            id
        };

        // AST: x == 42
        let ast = ASTNode::BinaryOp {
            operator: BinaryOperator::Equal,
            left: Box::new(ASTNode::Variable {
                name: "x".to_string(),
                span: Span::unknown(),
            }),
            right: Box::new(ASTNode::Literal {
                value: LiteralValue::Integer(42),
                span: Span::unknown(),
            }),
            span: Span::unknown(),
        };

        let result = lower_condition_to_joinir(&ast, &mut alloc_value, &env, Some(&body_local_env));
        assert!(result.is_ok(), "Variable resolution should succeed");

        let (_cond_value, instructions) = result.unwrap();

        // Verify the comparison uses ConditionEnv's ValueId(100), not LoopBodyLocalEnv's ValueId(200)
        if let Some(JoinInst::Compute(MirLikeInst::Compare { lhs, .. })) = instructions.get(1) {
            assert_eq!(
                *lhs,
                ValueId(100),
                "ConditionEnv should take priority over LoopBodyLocalEnv"
            );
        } else {
            panic!("Expected Compare instruction at position 1");
        }
    }

    /// Phase 92 P4 Level 2: Test error handling for undefined variables
    ///
    /// Variables not found in either environment should produce clear error messages.
    #[test]
    fn test_undefined_variable_error() {
        let env = ConditionEnv::new();
        let body_local_env = LoopBodyLocalEnv::new();

        let mut value_counter = 300u32;
        let mut alloc_value = || {
            let id = ValueId(value_counter);
            value_counter += 1;
            id
        };

        // AST: undefined_var == 42
        let ast = ASTNode::BinaryOp {
            operator: BinaryOperator::Equal,
            left: Box::new(ASTNode::Variable {
                name: "undefined_var".to_string(),
                span: Span::unknown(),
            }),
            right: Box::new(ASTNode::Literal {
                value: LiteralValue::Integer(42),
                span: Span::unknown(),
            }),
            span: Span::unknown(),
        };

        let result = lower_condition_to_joinir(&ast, &mut alloc_value, &env, Some(&body_local_env));
        assert!(result.is_err(), "Undefined variable should fail");

        let err = result.unwrap_err();
        assert!(
            err.contains("undefined_var"),
            "Error message should mention the undefined variable name"
        );
        assert!(
            err.contains("not found"),
            "Error message should indicate variable was not found"
        );
    }
}
