//! ReturnValueLowererBox: Return value lowering SSOT (Phase 138 P0)
//!
//! ## Responsibility
//!
//! Lower return values (variable, literal, expr) to ValueId for Normalized shadow paths
//!
//! ## Supported Patterns (Phase 136-137)
//!
//! - Variable: env lookup → ValueId
//! - Integer literal: Const generation → ValueId
//! - Add expr: (var|int) + int → BinOp(Add, lhs, rhs) → ValueId
//!
//! ## Fallback
//!
//! Out-of-scope patterns return `Ok(None)` for legacy routing
//!
//! ## Usage
//!
//! - Phase 138 P0: loop_true_break_once.rs
//! - Phase 139 P0: post_if_post_k.rs (planned)

use crate::ast::{ASTNode, BinaryOperator, LiteralValue};
use crate::mir::control_tree::step_tree::AstNodeHandle;
use crate::mir::join_ir::{BinOpKind, ConstValue, JoinInst, MirLikeInst};
use crate::mir::ValueId;
use std::collections::BTreeMap;

/// Box-First: Return value lowering for Normalized shadow
pub struct ReturnValueLowererBox;

impl ReturnValueLowererBox {
    /// Lower return value to ValueId
    ///
    /// Phase 136-137: Support variable, integer literal, and add expression
    ///
    /// Returns:
    /// - Ok(Some(vid)): Successfully lowered to ValueId
    /// - Ok(None): Out of scope (fallback to legacy routing)
    /// - Err(_): Internal error (should not happen for valid AST)
    ///
    /// Note: Does NOT return Err for unsupported patterns - returns Ok(None) instead
    pub fn lower_to_value_id(
        value_ast: &Option<AstNodeHandle>,
        body: &mut Vec<JoinInst>,
        next_value_id: &mut u32,
        env: &BTreeMap<String, ValueId>,
    ) -> Result<Option<ValueId>, String> {
        match value_ast {
            None => {
                // void return
                Ok(Some(ValueId(0))) // Dummy - caller handles void separately
            }
            Some(ast_handle) => {
                match ast_handle.0.as_ref() {
                    // Variable: lookup in env
                    ASTNode::Variable { name, .. } => {
                        if let Some(&vid) = env.get(name) {
                            Ok(Some(vid))
                        } else {
                            // Variable not in env - out of scope
                            Ok(None)
                        }
                    }
                    // Integer literal: generate Const instruction (Phase 136)
                    ASTNode::Literal { value: LiteralValue::Integer(i), .. } => {
                        let const_vid = ValueId(*next_value_id);
                        *next_value_id += 1;

                        body.push(JoinInst::Compute(MirLikeInst::Const {
                            dst: const_vid,
                            value: ConstValue::Integer(*i),
                        }));

                        Ok(Some(const_vid))
                    }
                    // Phase 137: BinaryOp (x + 2) support
                    ASTNode::BinaryOp { operator, left, right, .. } => {
                        Self::lower_binary_op(operator, left, right, body, next_value_id, env)
                    }
                    _ => {
                        // Other return value types - out of scope
                        Ok(None)
                    }
                }
            }
        }
    }

    /// Lower binary operation to ValueId (Phase 137)
    fn lower_binary_op(
        operator: &BinaryOperator,
        left: &ASTNode,
        right: &ASTNode,
        body: &mut Vec<JoinInst>,
        next_value_id: &mut u32,
        env: &BTreeMap<String, ValueId>,
    ) -> Result<Option<ValueId>, String> {
        // Phase 137 contract: Add only
        if !matches!(operator, BinaryOperator::Add) {
            return Ok(None); // out of scope
        }

        // Lower LHS (Variable or Integer literal)
        let lhs_vid = match left {
            ASTNode::Variable { name, .. } => {
                // Get from env
                if let Some(&vid) = env.get(name) {
                    vid
                } else {
                    // Variable not in env - out of scope
                    return Ok(None);
                }
            }
            ASTNode::Literal { value: LiteralValue::Integer(i), .. } => {
                // Generate Const for LHS integer literal
                let vid = ValueId(*next_value_id);
                *next_value_id += 1;
                body.push(JoinInst::Compute(MirLikeInst::Const {
                    dst: vid,
                    value: ConstValue::Integer(*i),
                }));
                vid
            }
            _ => {
                // Other LHS types - out of scope
                return Ok(None);
            }
        };

        // Lower RHS (Integer literal only)
        let rhs_vid = match right {
            ASTNode::Literal { value: LiteralValue::Integer(i), .. } => {
                // Generate Const for RHS integer literal
                let vid = ValueId(*next_value_id);
                *next_value_id += 1;
                body.push(JoinInst::Compute(MirLikeInst::Const {
                    dst: vid,
                    value: ConstValue::Integer(*i),
                }));
                vid
            }
            _ => {
                // Other RHS types - out of scope (e.g., return x + y)
                return Ok(None);
            }
        };

        // Generate BinOp Add
        let result_vid = ValueId(*next_value_id);
        *next_value_id += 1;
        body.push(JoinInst::Compute(MirLikeInst::BinOp {
            dst: result_vid,
            op: BinOpKind::Add,
            lhs: lhs_vid,
            rhs: rhs_vid,
        }));

        Ok(Some(result_vid))
    }
}

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

    fn make_span() -> Span {
        Span::unknown()
    }

    #[test]
    fn test_lower_variable() {
        let mut body = vec![];
        let mut next_value_id = 100;
        let mut env = BTreeMap::new();
        env.insert("x".to_string(), ValueId(42));

        let var_ast = AstNodeHandle(Box::new(ASTNode::Variable {
            name: "x".to_string(),
            span: make_span(),
        }));

        let result = ReturnValueLowererBox::lower_to_value_id(
            &Some(var_ast),
            &mut body,
            &mut next_value_id,
            &env,
        )
        .unwrap();

        assert_eq!(result, Some(ValueId(42)));
        assert_eq!(body.len(), 0); // No instructions emitted
    }

    #[test]
    fn test_lower_integer_literal() {
        let mut body = vec![];
        let mut next_value_id = 100;
        let env = BTreeMap::new();

        let int_ast = AstNodeHandle(Box::new(ASTNode::Literal {
            value: LiteralValue::Integer(7),
            span: make_span(),
        }));

        let result = ReturnValueLowererBox::lower_to_value_id(
            &Some(int_ast),
            &mut body,
            &mut next_value_id,
            &env,
        )
        .unwrap();

        assert_eq!(result, Some(ValueId(100)));
        assert_eq!(body.len(), 1); // Const instruction emitted
        assert_eq!(next_value_id, 101);
    }

    #[test]
    fn test_lower_add_var_plus_int() {
        let mut body = vec![];
        let mut next_value_id = 100;
        let mut env = BTreeMap::new();
        env.insert("x".to_string(), ValueId(1));

        let add_ast = AstNodeHandle(Box::new(ASTNode::BinaryOp {
            operator: BinaryOperator::Add,
            left: Box::new(ASTNode::Variable {
                name: "x".to_string(),
                span: make_span(),
            }),
            right: Box::new(ASTNode::Literal {
                value: LiteralValue::Integer(2),
                span: make_span(),
            }),
            span: make_span(),
        }));

        let result = ReturnValueLowererBox::lower_to_value_id(
            &Some(add_ast),
            &mut body,
            &mut next_value_id,
            &env,
        )
        .unwrap();

        assert_eq!(result, Some(ValueId(101))); // BinOp result
        assert_eq!(body.len(), 2); // Const(2) + BinOp
        assert_eq!(next_value_id, 102);
    }

    #[test]
    fn test_lower_add_int_plus_int() {
        let mut body = vec![];
        let mut next_value_id = 100;
        let env = BTreeMap::new();

        let add_ast = AstNodeHandle(Box::new(ASTNode::BinaryOp {
            operator: BinaryOperator::Add,
            left: Box::new(ASTNode::Literal {
                value: LiteralValue::Integer(5),
                span: make_span(),
            }),
            right: Box::new(ASTNode::Literal {
                value: LiteralValue::Integer(3),
                span: make_span(),
            }),
            span: make_span(),
        }));

        let result = ReturnValueLowererBox::lower_to_value_id(
            &Some(add_ast),
            &mut body,
            &mut next_value_id,
            &env,
        )
        .unwrap();

        assert_eq!(result, Some(ValueId(102))); // BinOp result
        assert_eq!(body.len(), 3); // Const(5) + Const(3) + BinOp
        assert_eq!(next_value_id, 103);
    }

    #[test]
    fn test_out_of_scope_subtract() {
        let mut body = vec![];
        let mut next_value_id = 100;
        let mut env = BTreeMap::new();
        env.insert("x".to_string(), ValueId(1));

        let sub_ast = AstNodeHandle(Box::new(ASTNode::BinaryOp {
            operator: BinaryOperator::Subtract,
            left: Box::new(ASTNode::Variable {
                name: "x".to_string(),
                span: make_span(),
            }),
            right: Box::new(ASTNode::Literal {
                value: LiteralValue::Integer(2),
                span: make_span(),
            }),
            span: make_span(),
        }));

        let result = ReturnValueLowererBox::lower_to_value_id(
            &Some(sub_ast),
            &mut body,
            &mut next_value_id,
            &env,
        )
        .unwrap();

        assert_eq!(result, None); // Out of scope
        assert_eq!(body.len(), 0); // No instructions emitted
    }
}
