use std::rc::Rc;

use super::{Environment, Node, NodeEnum, Precedence, empty::Empty};

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

impl Node for Assign {
    fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
        if let NodeEnum::Symbol(symbol) = self.left.as_ref() {
            let name = env
                .id_to_str(&symbol.get_value())
                .expect("Unknown symbol")
                .clone();
            let right = self.right.evaluate(env)?;

            env.insert(symbol.get_value(), right.clone());
            Ok(Empty::new(format!(
                "{name} := {}",
                right.as_string(Some(env))
            )))
        } else {
            Err(format!(
                "Cannot assign to a {}",
                self.left.as_string(Some(env))
            ))
        }
    }

    fn as_string(&self, env: Option<&Environment>) -> String {
        format!(
            "{} := {}",
            self.left.as_string(env),
            self.right.as_string(env)
        )
    }

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

impl Assign {
    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.left.clone()
    }
}
