// Expression lowering split from builder.rs to keep files lean
use super::{MirInstruction, ValueId};
use crate::ast::{
    ASTNode, AssignStmt, BinaryExpr, CallExpr, FieldAccessExpr, MethodCallExpr, ReturnStmt,
};
use crate::mir::builder::observe::types as type_trace;

impl super::MirBuilder {
    // Main expression dispatcher
    pub(super) fn build_expression_impl(&mut self, ast: ASTNode) -> Result<ValueId, String> {
        // Track current source span for downstream instruction emission
        self.metadata_ctx.set_current_span(ast.span());
        if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
            if matches!(ast, ASTNode::Loop { .. }) {
                eprintln!("[build_expression_impl] === ENTRY === processing Loop node");
            }
        }
        match ast {
            // Control flow constructs (formerly in exprs_legacy)
            ASTNode::Program { statements, .. } => {
                // Sequentially lower statements and return last value (or Void)
                self.cf_block(statements)
            }
            ASTNode::ScopeBox { body, .. } => self.cf_block(body),
            ASTNode::Print { expression, .. } => self.build_print_statement(*expression),
            ASTNode::If {
                condition,
                then_body,
                else_body,
                ..
            } => {
                use crate::ast::Span;
                let then_node = ASTNode::Program {
                    statements: then_body,
                    span: Span::unknown(),
                };
                let else_node = else_body.map(|b| ASTNode::Program {
                    statements: b,
                    span: Span::unknown(),
                });
                self.cf_if(*condition, then_node, else_node)
            }
            ASTNode::Loop {
                condition, body, ..
            } => {
                if std::env::var("NYASH_LOOPFORM_DEBUG").is_ok() {
                    eprintln!("[exprs.rs:35] FIRST Loop pattern matched");
                }
                self.cf_loop(*condition, body)
            }
            ASTNode::While {
                condition, body, ..
            } => {
                // Desugar Stage-3 while into legacy loop(condition) { body }
                self.cf_loop(*condition, body)
            }
            ASTNode::TryCatch {
                try_body,
                catch_clauses,
                finally_body,
                ..
            } => self.cf_try_catch(try_body, catch_clauses, finally_body),
            ASTNode::Throw { expression, .. } => self.cf_throw(*expression),

            // Regular expressions
            ASTNode::Literal { value, .. } => self.build_literal(value),

            node @ ASTNode::BinaryOp { .. } => {
                // Use BinaryExpr for clear destructuring (no behavior change)
                let e = BinaryExpr::try_from(node).expect("ASTNode::BinaryOp must convert");
                self.build_binary_op(*e.left, e.operator, *e.right)
            }

            ASTNode::UnaryOp {
                operator, operand, ..
            } => {
                match operator {
                    // Phase 285W-Syntax-0: weak <expr> → WeakNew
                    crate::ast::UnaryOperator::Weak => {
                        let box_val = self.build_expression_impl(*operand)?;
                        self.emit_weak_new(box_val)
                    }
                    // Traditional unary operators
                    _ => {
                        let op_string = match operator {
                            crate::ast::UnaryOperator::Minus => "-".to_string(),
                            crate::ast::UnaryOperator::Not => "not".to_string(),
                            crate::ast::UnaryOperator::BitNot => "~".to_string(),
                            crate::ast::UnaryOperator::Weak => unreachable!("handled above"),
                        };
                        self.build_unary_op(op_string, *operand)
                    }
                }
            }

            ASTNode::Variable { name, .. } => self.build_variable_access(name.clone()),

            ASTNode::Me { .. } => self.build_me_expression(),

            node @ ASTNode::MethodCall { .. } => {
                let m = MethodCallExpr::try_from(node).expect("ASTNode::MethodCall must convert");
                if (m.method == "is" || m.method == "as") && m.arguments.len() == 1 {
                    if let Some(type_name) = Self::extract_string_literal(&m.arguments[0]) {
                        let obj_val = self.build_expression_impl(*m.object.clone())?;
                        let ty = Self::parse_type_name_to_mir(&type_name);
                        let dst = self.next_value_id();
                        let op = if m.method == "is" {
                            crate::mir::TypeOpKind::Check
                        } else {
                            crate::mir::TypeOpKind::Cast
                        };
                        self.emit_instruction(MirInstruction::TypeOp {
                            dst,
                            op,
                            value: obj_val,
                            ty,
                        })?;
                        return Ok(dst);
                    }
                }
                self.build_method_call(*m.object.clone(), m.method.clone(), m.arguments.clone())
            }

            ASTNode::FromCall {
                parent,
                method,
                arguments,
                ..
            } => self.build_from_expression(parent.clone(), method.clone(), arguments.clone()),

            node @ ASTNode::Assignment { .. } => {
                // Use AssignStmt wrapper for clearer destructuring (no behavior change)
                let stmt = AssignStmt::try_from(node).expect("ASTNode::Assignment must convert");
                if let ASTNode::FieldAccess { object, field, .. } = stmt.target.as_ref() {
                    self.build_field_assignment(*object.clone(), field.clone(), *stmt.value.clone())
                } else if let ASTNode::Index { target, index, .. } = stmt.target.as_ref() {
                    self.build_index_assignment(
                        *target.clone(),
                        *index.clone(),
                        *stmt.value.clone(),
                    )
                } else if let ASTNode::Variable { name, .. } = stmt.target.as_ref() {
                    self.build_assignment(name.clone(), *stmt.value.clone())
                } else {
                    Err("Complex assignment targets not yet supported".to_string())
                }
            }

            // Phase 152-A: Grouped assignment expression (x = expr)
            // Stage-3 only. Value/type same as rhs, side effect assigns to lhs.
            // Reuses existing build_assignment logic, returns the SSA ValueId.
            ASTNode::GroupedAssignmentExpr { lhs, rhs, .. } => {
                self.build_assignment(lhs.clone(), *rhs.clone())
            }

            ASTNode::Index { target, index, .. } => {
                self.build_index_expression(*target.clone(), *index.clone())
            }

            node @ ASTNode::FunctionCall { .. } => {
                let c = CallExpr::try_from(node).expect("ASTNode::FunctionCall must convert");
                self.build_function_call(c.name, c.arguments)
            }

            ASTNode::Call {
                callee, arguments, ..
            } => self.build_indirect_call_expression(*callee.clone(), arguments.clone()),

            ASTNode::QMarkPropagate { expression, .. } => {
                self.build_qmark_propagate_expression(*expression.clone())
            }

            ASTNode::MatchExpr {
                scrutinee,
                arms,
                else_expr,
                ..
            } => self.build_peek_expression(*scrutinee.clone(), arms.clone(), *else_expr.clone()),

            ASTNode::Lambda { params, body, .. } => {
                self.build_lambda_expression(params.clone(), body.clone())
            }

            node @ ASTNode::Return { .. } => {
                // Use ReturnStmt wrapper for consistent access (no behavior change)
                let stmt = ReturnStmt::try_from(node).expect("ASTNode::Return must convert");
                self.build_return_statement(stmt.value.clone())
            }

            // Control flow: break/continue are handled inside LoopBuilder context
            ASTNode::Local {
                variables,
                initial_values,
                ..
            } => self.build_local_statement(variables.clone(), initial_values.clone()),

            ASTNode::BoxDeclaration {
                name,
                methods,
                is_static,
                fields,
                constructors,
                weak_fields,
                ..
            } => {
                if is_static && name == "Main" {
                    // Special entry box: materialize main() as Program and lower others as static functions
                    self.build_static_main_box(name.clone(), methods.clone())
                } else if is_static {
                    // In App mode (Main/main present), static boxes are lowered in lower_root().
                    // Here we only handle Script/Test mode or non-root contexts.
                    let is_app_mode = self.root_is_app_mode.unwrap_or(false);
                    if is_app_mode {
                        // Already lowered by lifecycle pass; return Void as a pure declaration.
                        let void_val = crate::mir::builder::emission::constant::emit_void(self);
                        Ok(void_val)
                    } else {
                        // Generic static box: lower all static methods into standalone MIR functions (BoxName.method/N)
                        // Note: Metadata clearing is now handled by BoxCompilationContext (箱理論)
                        // See lifecycle.rs for context creation and builder_calls.rs for context swap
                        // Phase 285LLVM-1.1: Register static box (no fields)
                        self.comp_ctx.register_user_box(name.clone());
                        for (method_name, method_ast) in methods.clone() {
                            if let ASTNode::FunctionDeclaration { params, body, .. } = method_ast {
                                let func_name = format!(
                                    "{}.{}{}",
                                    name,
                                    method_name,
                                    format!("/{}", params.len())
                                );
                                self.lower_static_method_as_function(
                                    func_name,
                                    params.clone(),
                                    body.clone(),
                                )?;
                                // Index static method for fallback resolution of bare calls
                                self.comp_ctx
                                    .static_method_index
                                    .entry(method_name.clone())
                                    .or_insert_with(Vec::new)
                                    .push((name.clone(), params.len()));
                            }
                        }
                        // Return void for declaration context
                        let void_val = crate::mir::builder::emission::constant::emit_void(self);
                        Ok(void_val)
                    }
                } else {
                    // Instance box: register type and lower instance methods/ctors as functions
                    // Phase 285LLVM-1.1: Register with field information for LLVM harness
                    self.comp_ctx.register_user_box_with_fields(name.clone(), fields.clone());
                    self.build_box_declaration(
                        name.clone(),
                        methods.clone(),
                        fields.clone(),
                        weak_fields.clone(),
                    )?;
                    for (ctor_key, ctor_ast) in constructors.clone() {
                        if let ASTNode::FunctionDeclaration { params, body, .. } = ctor_ast {
                            let func_name = format!("{}.{}", name, ctor_key);
                            self.lower_method_as_function(
                                func_name,
                                name.clone(),
                                params.clone(),
                                body.clone(),
                            )?;
                        }
                    }
                    for (method_name, method_ast) in methods.clone() {
                        if let ASTNode::FunctionDeclaration {
                            params,
                            body,
                            is_static,
                            ..
                        } = method_ast
                        {
                            if !is_static {
                                let func_name = format!(
                                    "{}.{}{}",
                                    name,
                                    method_name,
                                    format!("/{}", params.len())
                                );
                                self.lower_method_as_function(
                                    func_name,
                                    name.clone(),
                                    params.clone(),
                                    body.clone(),
                                )?;
                            }
                        }
                    }
                    let void_val = crate::mir::builder::emission::constant::emit_void(self);
                    Ok(void_val)
                }
            }

            node @ ASTNode::FieldAccess { .. } => {
                let f = FieldAccessExpr::try_from(node).expect("ASTNode::FieldAccess must convert");
                self.build_field_access(*f.object.clone(), f.field.clone())
            }

            ASTNode::New {
                class, arguments, ..
            } => self.build_new_expression(class.clone(), arguments.clone()),

            ASTNode::ArrayLiteral { elements, .. } => {
                let arr_id = self.next_value_id();
                self.emit_instruction(MirInstruction::NewBox {
                    dst: arr_id,
                    box_type: "ArrayBox".to_string(),
                    args: vec![],
                })?;
                // Explicit birth() to satisfy runtime invariant (NewBox→birth)
                self.emit_instruction(MirInstruction::BoxCall {
                    dst: None,
                    box_val: arr_id,
                    method: "birth".to_string(),
                    method_id: None,
                    args: vec![],
                    effects: super::EffectMask::MUT,
                })?;
                self.type_ctx
                    .value_origin_newbox
                    .insert(arr_id, "ArrayBox".to_string());
                self.type_ctx
                    .value_types
                    .insert(arr_id, super::MirType::Box("ArrayBox".to_string()));
                // TypeRegistry + trace for deterministic debug
                self.comp_ctx
                    .type_registry
                    .record_newbox(arr_id, "ArrayBox".to_string());
                self.comp_ctx
                    .type_registry
                    .record_type(arr_id, super::MirType::Box("ArrayBox".to_string()));
                type_trace::origin("newbox:ArrayLiteral", arr_id, "ArrayBox");
                type_trace::ty(
                    "newbox:ArrayLiteral",
                    arr_id,
                    &super::MirType::Box("ArrayBox".to_string()),
                );
                for e in elements {
                    let v = self.build_expression_impl(e)?;
                    self.emit_instruction(MirInstruction::BoxCall {
                        dst: None,
                        box_val: arr_id,
                        method: "push".to_string(),
                        method_id: None,
                        args: vec![v],
                        effects: super::EffectMask::MUT,
                    })?;
                }
                Ok(arr_id)
            }
            ASTNode::MapLiteral { entries, .. } => {
                let map_id = self.next_value_id();
                self.emit_instruction(MirInstruction::NewBox {
                    dst: map_id,
                    box_type: "MapBox".to_string(),
                    args: vec![],
                })?;
                // Explicit birth() to satisfy runtime invariant (NewBox→birth)
                self.emit_instruction(MirInstruction::BoxCall {
                    dst: None,
                    box_val: map_id,
                    method: "birth".to_string(),
                    method_id: None,
                    args: vec![],
                    effects: super::EffectMask::MUT,
                })?;
                self.type_ctx
                    .value_origin_newbox
                    .insert(map_id, "MapBox".to_string());
                self.type_ctx
                    .value_types
                    .insert(map_id, super::MirType::Box("MapBox".to_string()));
                self.comp_ctx
                    .type_registry
                    .record_newbox(map_id, "MapBox".to_string());
                self.comp_ctx
                    .type_registry
                    .record_type(map_id, super::MirType::Box("MapBox".to_string()));
                type_trace::origin("newbox:MapLiteral", map_id, "MapBox");
                type_trace::ty(
                    "newbox:MapLiteral",
                    map_id,
                    &super::MirType::Box("MapBox".to_string()),
                );
                for (k, expr) in entries {
                    // const string key
                    let k_id = crate::mir::builder::emission::constant::emit_string(self, k);
                    let v_id = self.build_expression_impl(expr)?;
                    self.emit_instruction(MirInstruction::BoxCall {
                        dst: None,
                        box_val: map_id,
                        method: "set".to_string(),
                        method_id: None,
                        args: vec![k_id, v_id],
                        effects: super::EffectMask::MUT,
                    })?;
                }
                Ok(map_id)
            }

            ASTNode::Nowait {
                variable,
                expression,
                ..
            } => self.build_nowait_statement(variable.clone(), *expression.clone()),

            ASTNode::AwaitExpression { expression, .. } => {
                self.build_await_expression(*expression.clone())
            }

            // UsingStatement: namespace resolution is done at parser/runner level.
            // No MIR emission needed - just return void.
            ASTNode::UsingStatement { .. } => {
                Ok(crate::mir::builder::emission::constant::emit_void(self))
            }

            _ => Err(format!("Unsupported AST node type: {:?}", ast)),
        }
    }

    fn infer_index_target_class(&self, target_val: ValueId) -> Option<String> {
        if let Some(cls) = self.type_ctx.value_origin_newbox.get(&target_val) {
            return Some(cls.clone());
        }
        self.type_ctx
            .value_types
            .get(&target_val)
            .and_then(|ty| match ty {
                super::MirType::Box(name) => Some(name.clone()),
                super::MirType::String => Some("String".to_string()),
                super::MirType::Integer => Some("Integer".to_string()),
                super::MirType::Float => Some("Float".to_string()),
                _ => None,
            })
    }

    fn format_index_target_kind(class_hint: Option<&String>) -> String {
        class_hint
            .map(|s| s.as_str())
            .filter(|s| !s.is_empty())
            .unwrap_or("unknown")
            .to_string()
    }

    pub(super) fn build_index_expression(
        &mut self,
        target: ASTNode,
        index: ASTNode,
    ) -> Result<ValueId, String> {
        let target_val = self.build_expression(target)?;
        let class_hint = self.infer_index_target_class(target_val);

        match class_hint.as_deref() {
            Some("ArrayBox") => {
                let index_val = self.build_expression(index)?;
                let dst = self.next_value_id();
                self.emit_box_or_plugin_call(
                    Some(dst),
                    target_val,
                    "get".to_string(),
                    None,
                    vec![index_val],
                    super::EffectMask::READ,
                )?;
                Ok(dst)
            }
            Some("MapBox") => {
                let index_val = self.build_expression(index)?;
                let dst = self.next_value_id();
                self.emit_box_or_plugin_call(
                    Some(dst),
                    target_val,
                    "get".to_string(),
                    None,
                    vec![index_val],
                    super::EffectMask::READ,
                )?;
                Ok(dst)
            }
            _ => Err(format!(
                "index operator is only supported for Array/Map (found {})",
                Self::format_index_target_kind(class_hint.as_ref())
            )),
        }
    }

    pub(super) fn build_index_assignment(
        &mut self,
        target: ASTNode,
        index: ASTNode,
        value: ASTNode,
    ) -> Result<ValueId, String> {
        let target_val = self.build_expression(target)?;
        let class_hint = self.infer_index_target_class(target_val);

        match class_hint.as_deref() {
            Some("ArrayBox") => {
                let index_val = self.build_expression(index)?;
                let value_val = self.build_expression(value)?;
                self.emit_box_or_plugin_call(
                    None,
                    target_val,
                    "set".to_string(),
                    None,
                    vec![index_val, value_val],
                    super::EffectMask::MUT,
                )?;
                Ok(value_val)
            }
            Some("MapBox") => {
                let index_val = self.build_expression(index)?;
                let value_val = self.build_expression(value)?;
                self.emit_box_or_plugin_call(
                    None,
                    target_val,
                    "set".to_string(),
                    None,
                    vec![index_val, value_val],
                    super::EffectMask::MUT,
                )?;
                Ok(value_val)
            }
            _ => Err(format!(
                "index assignment is only supported for Array/Map (found {})",
                Self::format_index_target_kind(class_hint.as_ref())
            )),
        }
    }
}
