use crate::r#macro::pattern::{AstBuilder, MacroPattern, TemplatePattern};
use nyash_rust::ast::{ASTNode, BinaryOperator, Span};

#[test]
fn template_pattern_matches_and_unquotes() {
    // Build a template: ($x + 1) == $y  (Binary(Equal, Binary(Add, $x, 1), $y))
    let tpl = ASTNode::BinaryOp {
        operator: BinaryOperator::Equal,
        left: Box::new(ASTNode::BinaryOp {
            operator: BinaryOperator::Add,
            left: Box::new(ASTNode::Variable {
                name: "$x".into(),
                span: Span::unknown(),
            }),
            right: Box::new(ASTNode::Literal {
                value: nyash_rust::ast::LiteralValue::Integer(1),
                span: Span::unknown(),
            }),
            span: Span::unknown(),
        }),
        right: Box::new(ASTNode::Variable {
            name: "$y".into(),
            span: Span::unknown(),
        }),
        span: Span::unknown(),
    };
    let pat = TemplatePattern::new(tpl);

    // Target: (a + 1) == b
    let target = ASTNode::BinaryOp {
        operator: BinaryOperator::Equal,
        left: Box::new(ASTNode::BinaryOp {
            operator: BinaryOperator::Add,
            left: Box::new(ASTNode::Variable {
                name: "a".into(),
                span: Span::unknown(),
            }),
            right: Box::new(ASTNode::Literal {
                value: nyash_rust::ast::LiteralValue::Integer(1),
                span: Span::unknown(),
            }),
            span: Span::unknown(),
        }),
        right: Box::new(ASTNode::Variable {
            name: "b".into(),
            span: Span::unknown(),
        }),
        span: Span::unknown(),
    };
    let binds = pat.match_ast(&target).expect("pattern match");
    assert!(binds.contains_key("x"));
    assert!(binds.contains_key("y"));

    // Unquote a template: return $y
    let builder = AstBuilder::new();
    let tpl2 = ASTNode::Return {
        value: Some(Box::new(ASTNode::Variable {
            name: "$y".into(),
            span: Span::unknown(),
        })),
        span: Span::unknown(),
    };
    let out = builder.unquote(&tpl2, &binds);
    match out {
        ASTNode::Return { value: Some(v), .. } => match *v {
            ASTNode::Variable { name, .. } => assert_eq!(name, "b"),
            _ => panic!("expected variable"),
        },
        _ => panic!("expected return"),
    }
}
