use crate::ast::ASTNode;
use crate::mir::control_tree::step_tree_contract_box::StepTreeContractBox;

use super::fact_extractor::extract_facts_from_tree;
use super::signature::collect_node_kinds;
use super::summary::{ast_kind_name, summarize_ast};
use super::types::{AstNodeHandle, StepNode, StepStmtKind, StepTree, StepTreeFeatures, StepTreeSignature};

pub struct StepTreeBuilderBox;

impl StepTreeBuilderBox {
    pub fn build_from_ast(ast: &ASTNode) -> StepTree {
        match ast {
            ASTNode::Program { statements, .. } => Self::build_from_block(statements),
            ASTNode::ScopeBox { body, .. } => Self::build_from_block(body),
            _ => {
                let (node, features) = Self::build_node(ast, 0, 0);
                build_step_tree(node, features)
            }
        }
    }

    pub fn build_from_block(stmts: &[ASTNode]) -> StepTree {
        let mut nodes = Vec::with_capacity(stmts.len());
        let mut features = StepTreeFeatures::default();
        for stmt in stmts {
            let (node, node_features) = Self::build_node(stmt, 0, 0);
            nodes.push(node);
            features = merge_features(features, node_features);
        }
        build_step_tree(StepNode::Block(nodes), features)
    }

    fn build_node(ast: &ASTNode, if_depth: u32, loop_depth: u32) -> (StepNode, StepTreeFeatures) {
        match ast {
            ASTNode::If {
                condition,
                then_body,
                else_body,
                span,
            } => {
                let cond = summarize_ast(condition);
                let cond_ast = AstNodeHandle(condition.clone());
                let (then_node, then_features) =
                    Self::build_block_node(then_body, if_depth + 1, loop_depth);
                let (else_node, else_features) = match else_body {
                    Some(else_body) => {
                        let (node, f) =
                            Self::build_block_node(else_body, if_depth + 1, loop_depth);
                        (Some(Box::new(node)), f)
                    }
                    None => (None, StepTreeFeatures::default()),
                };
                let mut features = StepTreeFeatures {
                    has_if: true,
                    max_if_depth: (if_depth + 1).max(then_features.max_if_depth),
                    ..StepTreeFeatures::default()
                };
                features = merge_features(features, then_features);
                features = merge_features(features, else_features);

                (
                    StepNode::If {
                        cond,
                        cond_ast,
                        then_branch: Box::new(then_node),
                        else_branch: else_node,
                        span: span.clone(),
                    },
                    features,
                )
            }
            ASTNode::Loop {
                condition, body, span, ..
            } => {
                let cond = summarize_ast(condition);
                let cond_ast = AstNodeHandle(condition.clone());
                let (body_node, body_features) =
                    Self::build_block_node(body, if_depth, loop_depth + 1);
                let mut features = StepTreeFeatures {
                    has_loop: true,
                    max_loop_depth: (loop_depth + 1).max(body_features.max_loop_depth),
                    ..StepTreeFeatures::default()
                };
                features = merge_features(features, body_features);
                (
                    StepNode::Loop {
                        cond,
                        cond_ast,
                        body: Box::new(body_node),
                        span: span.clone(),
                    },
                    features,
                )
            }
            ASTNode::ScopeBox { body, span } => {
                let (node, features) = Self::build_block_node(body, if_depth, loop_depth);
                (node.with_span(span.clone()), features)
            }
            ASTNode::Return { value, span } => (
                StepNode::Stmt {
                    kind: StepStmtKind::Return {
                        value_ast: value.as_ref().map(|v| AstNodeHandle(v.clone())),
                    },
                    span: span.clone(),
                },
                StepTreeFeatures {
                    has_return: true,
                    ..StepTreeFeatures::default()
                },
            ),
            ASTNode::Break { span } => (
                StepNode::Stmt {
                    kind: StepStmtKind::Break,
                    span: span.clone(),
                },
                StepTreeFeatures {
                    has_break: true,
                    ..StepTreeFeatures::default()
                },
            ),
            ASTNode::Continue { span } => (
                StepNode::Stmt {
                    kind: StepStmtKind::Continue,
                    span: span.clone(),
                },
                StepTreeFeatures {
                    has_continue: true,
                    ..StepTreeFeatures::default()
                },
            ),
            ASTNode::Local { variables, span, .. } => (
                StepNode::Stmt {
                    kind: StepStmtKind::LocalDecl {
                        vars: variables.clone(),
                    },
                    span: span.clone(),
                },
                StepTreeFeatures::default(),
            ),
            ASTNode::Assignment { span, value, .. } => (
                StepNode::Stmt {
                    kind: StepStmtKind::Assign {
                        target: match ast {
                            ASTNode::Assignment { target, .. } => match target.as_ref() {
                                ASTNode::Variable { name, .. } => Some(name.clone()),
                                _ => None,
                            },
                            _ => None,
                        },
                        // Phase 128: Store value AST for Normalized lowering
                        value_ast: Some(AstNodeHandle(value.clone())),
                    },
                    span: span.clone(),
                },
                StepTreeFeatures::default(),
            ),
            ASTNode::Print { span, .. } => (
                StepNode::Stmt {
                    kind: StepStmtKind::Print,
                    span: span.clone(),
                },
                StepTreeFeatures::default(),
            ),
            other => (
                StepNode::Stmt {
                    kind: StepStmtKind::Other(ast_kind_name(other)),
                    span: other.span(),
                },
                StepTreeFeatures::default(),
            ),
        }
    }

    fn build_block_node(
        stmts: &[ASTNode],
        if_depth: u32,
        loop_depth: u32,
    ) -> (StepNode, StepTreeFeatures) {
        let mut nodes = Vec::with_capacity(stmts.len());
        let mut features = StepTreeFeatures::default();
        for stmt in stmts {
            let (node, node_features) = Self::build_node(stmt, if_depth, loop_depth);
            nodes.push(node);
            features = merge_features(features, node_features);
        }
        (StepNode::Block(nodes), features)
    }
}

fn build_step_tree(root: StepNode, features: StepTreeFeatures) -> StepTree {
    // Phase 120: Facts → Contract → Signature (separated concerns)
    let facts = extract_facts_from_tree(&root, &features);
    let contract = StepTreeContractBox::from_facts(&facts);
    let mut kinds = Vec::new();
    collect_node_kinds(&root, &mut kinds);
    let kinds = kinds.join(",");
    let basis = contract.signature_basis_string(&kinds);
    let signature = StepTreeSignature::from_basis_string(&basis);

    StepTree {
        root,
        features,
        contract,
        signature,
    }
}

fn merge_features(mut a: StepTreeFeatures, b: StepTreeFeatures) -> StepTreeFeatures {
    a.has_if |= b.has_if;
    a.has_loop |= b.has_loop;
    a.has_break |= b.has_break;
    a.has_continue |= b.has_continue;
    a.has_return |= b.has_return;
    a.max_if_depth = a.max_if_depth.max(b.max_if_depth);
    a.max_loop_depth = a.max_loop_depth.max(b.max_loop_depth);
    a
}
