use std::{
    collections::HashMap,
    rc::Rc,
    sync::{LazyLock, Mutex},
};

use super::{Environment, Node, NodeEnum, Precedence, node_ref::NodeRef};
use crate::environment::EnvironmentInternalSymbolKey;

#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct Symbol {
    value: EnvironmentInternalSymbolKey,
}

impl Node for Symbol {
    fn evaluate(&self, env: &mut Environment) -> Result<Rc<NodeEnum>, String> {
        if let Some(value) = env.get(&self.value) {
            // TODO: Detect cyclic references and make sure they get
            // deleted at some point
            Ok(value.clone())
        } else {
            Ok(Rc::new(self.clone().into()))
        }
    }

    fn as_string(&self, env: Option<&Environment>) -> String {
        if let Some(env) = env {
            if let Some(value) = env.get(&self.value) {
                value.as_string(Some(env))
            } else {
                env.id_to_str(&self.value)
                    .cloned()
                    .unwrap_or(format!("{{#SYMBOL {}}}", self.value))
            }
        } else {
            format!("{{#SYMBOL {}}}", self.value)
        }
    }

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

impl Symbol {
    pub fn new(value: EnvironmentInternalSymbolKey) -> Self {
        Self { value }
    }

    pub fn new_from_str(str: impl Into<String>, env: &mut Environment) -> Self {
        let str = str.into();
        if let Some(value) = env.str_to_id(&str) {
            Self::new(value.clone())
        } else {
            let id = env.get_new_id();
            env.insert_str_to_id(str.clone(), id);
            Self::new(id)
        }
    }

    pub fn get_value(&self) -> EnvironmentInternalSymbolKey {
        self.value
    }
}
