use std::sync::Arc;

use anyhow::Result;
use dashmap::DashMap;
use rhai::Engine;
use rhai::Position;

use crate::component_syntax::component_reference::ComponentReference;
use crate::component_syntax::component_registry::ComponentRegistry;
use crate::component_syntax::evaluator_factory::EvaluatorFactory;
use crate::component_syntax::parse_component::parse_component;
use crate::rhai_helpers::clsx;
use crate::rhai_helpers::error;
use crate::rhai_helpers::has;

pub trait BuildsEngine {
    fn component_registry(&self) -> Arc<ComponentRegistry>;

    fn prepare_engine(&self, engine: &mut Engine) -> Result<()>;

    fn create_engine(&self) -> Result<Engine> {
        let evaluator_factory = EvaluatorFactory {
            component_registry: self.component_registry().clone(),
        };

        let mut engine = Engine::new();

        engine.set_fail_on_invalid_map_property(true);
        engine.set_max_call_levels(128);
        engine.set_max_expr_depths(256, 256);

        engine.register_fn("clsx", clsx);
        engine.register_fn("error", error);
        engine.register_fn("has", has);

        engine.register_custom_syntax_without_look_ahead_raw(
            "component",
            parse_component,
            true,
            evaluator_factory.create_component_evaluator(),
        );

        self.prepare_engine(&mut engine)?;

        let templates: DashMap<String, ComponentReference> = DashMap::new();

        for entry in &self.component_registry().components {
            let component_reference = entry.value();

            let module_resolver = engine.module_resolver();
            let module = module_resolver.resolve(
                &engine,
                None,
                &component_reference.name,
                Position::NONE,
            )?;

            engine.register_static_module(component_reference.name.clone(), module);

            templates.insert(
                component_reference.name.clone(),
                component_reference.clone(),
            );
        }

        Ok(engine)
    }
}
