use std::rc::Rc;

use crate::environment::{Environment, EnvironmentInternalSymbolKey};

use super::{function::Function, Error, Node, NodeEnum, Precedence};

#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct Closure {
    function: Function,
    captured_variables: Vec<(EnvironmentInternalSymbolKey, Rc<NodeEnum>)>,
}

impl Node for Closure {
    fn evaluate(&self, _env: &mut Environment) -> Result<Rc<NodeEnum>, Error> {
        Ok(Rc::new(self.clone().into()))
    }

    fn as_string(&self, env: Option<&Environment>) -> String {
        if let Some(env) = env {
            let captures = self
                .captured_variables
                .iter()
                .map(|(k, v)| {
                    format!(
                        "{}: {}",
                        env.id_to_str(k).expect("Failed to get variable name"),
                        v.as_string(Some(env))
                    )
                })
                .reduce(|a, b| format!("{}, {}", a, b))
                .unwrap();
            format!(
                "( ({captures}) => {})",
                self.function.as_string(Some(env))
            )
        } else {
            format!("<Closure {}>", self.function.as_string(env))
        }
    }

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

impl Closure {
    pub fn new(
        function: Function,
        captured_variables: Vec<(EnvironmentInternalSymbolKey, Rc<NodeEnum>)>,
    ) -> Rc<NodeEnum> {
        Rc::new(
            Self {
                function,
                captured_variables,
            }
            .into(),
        )
    }

    pub fn get_captured_variables(&self) -> &Vec<(EnvironmentInternalSymbolKey, Rc<NodeEnum>)> {
        &self.captured_variables
    }

    pub fn get_function(&self) -> &Function {
        &self.function
    }
}
