use std::rc::Rc;

use super::{Environment, Node, NodeEnum, Precedence, constant::Constant};

#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct Add {
    left: Rc<NodeEnum>,
    right: Rc<NodeEnum>,
}

impl Node for Add {
    fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
        let evaluated_left = self.left.evaluate(env)?;
        let evaluated_right = self.right.evaluate(env)?;

        match (evaluated_left.as_ref(), evaluated_right.as_ref()) {
            (NodeEnum::Constant(a), NodeEnum::Constant(b)) => {
                Ok(Rc::new(Constant::new(a.get_value() + b.get_value()).into()))
            }
            _ => Err(format!("Invalid Add operation: {:?}", self)),
        }
    }

    fn as_string(&self, env: Option<&Environment>) -> String {
        let left_string = if self.left.precedence() <= self.precedence() {
            format!("({})", self.left.as_string(env))
        } else {
            self.left.as_string(env)
        };
        let right_string = if self.right.precedence() <= self.precedence() {
            format!("({})", self.right.as_string(env))
        } else {
            self.right.as_string(env)
        };
        format!("{}+{}", left_string, right_string)
    }

    fn precedence(&self) -> Precedence {
        Precedence::Term
    }
}

impl Add {
    pub fn new(left: Rc<NodeEnum>, right: Rc<NodeEnum>) -> Self {
        Self {
            left,
            right,
        }
    }

    pub fn get_left(&self) -> Rc<NodeEnum> {
        self.left.clone()
    }

    pub fn get_right(&self) -> Rc<NodeEnum> {
        self.right.clone()
    }
}
