use std::collections::HashMap;
use std::iter;
use std::rc::Rc;
use_RefCell!();
use pax_runtime_api::pax_value::ToFromPaxAny;
use pax_runtime_api::CoercionRules;
use pax_runtime_api::{
    borrow, borrow_mut, use_RefCell, ImplToFromPaxAny, PaxValue, Property, ToPaxValue, Variable,
};
use crate::api::Layer;
use crate::{
    BaseInstance, ExpandedNode, InstanceFlags, InstanceNode, InstantiationArgs, RuntimeContext,
};
pub struct RepeatInstance {
    pub base: BaseInstance,
}
impl ImplToFromPaxAny for RepeatProperties {}
#[derive(Default)]
pub struct RepeatProperties {
    pub source_expression: Property<PaxValue>,
    pub iterator_i_symbol: Option<String>,
    pub iterator_elem_symbol: Option<String>,
}
impl ToPaxValue for RepeatProperties {
    fn to_pax_value(self) -> PaxValue {
        PaxValue::Object(
            vec![
                (
                    "source_expression".to_string(),
                    self.source_expression.to_pax_value(),
                ),
                (
                    "iterator_i_symbol".to_string(),
                    self.iterator_i_symbol.to_pax_value(),
                ),
                (
                    "iterator_elem_symbol".to_string(),
                    self.iterator_elem_symbol.to_pax_value(),
                ),
            ]
            .into_iter()
            .collect(),
        )
    }
}
pub struct RepeatItem {
    pub elem: Property<PaxValue>,
    pub i: Property<usize>,
}
impl ToPaxValue for RepeatItem {
    fn to_pax_value(self) -> PaxValue {
        PaxValue::Object(
            vec![
                ("elem".to_string(), self.elem.get().to_pax_value()),
                ("i".to_string(), self.i.get().to_pax_value()),
            ]
            .into_iter()
            .collect(),
        )
    }
}
impl InstanceNode for RepeatInstance {
    fn instantiate(args: InstantiationArgs) -> Rc<Self>
    where
        Self: Sized,
    {
        Rc::new(Self {
            base: BaseInstance::new(
                args,
                InstanceFlags {
                    invisible_to_slot: true,
                    invisible_to_raycasting: true,
                    layer: Layer::DontCare,
                    is_component: false,
                },
            ),
        })
    }
    fn resolve_debug(
        &self,
        f: &mut std::fmt::Formatter,
        _expanded_node: Option<&ExpandedNode>,
    ) -> std::fmt::Result {
        f.debug_struct("Repeat").finish()
    }
    fn base(&self) -> &BaseInstance {
        &self.base
    }
    fn update(self: Rc<Self>, _expanded_node: &Rc<ExpandedNode>, _context: &Rc<RuntimeContext>) {}
    fn handle_mount(
        self: Rc<Self>,
        expanded_node: &Rc<ExpandedNode>,
        context: &Rc<RuntimeContext>,
    ) {
        let weak_ref_self = Rc::downgrade(expanded_node);
        let cloned_self = Rc::clone(&self);
        let cloned_context = Rc::clone(context);
        let source_expression =
            expanded_node.with_properties_unwrapped(|properties: &mut RepeatProperties| {
                properties.source_expression.clone()
            });
        let i_symbol =
            expanded_node.with_properties_unwrapped(|properties: &mut RepeatProperties| {
                properties.iterator_i_symbol.clone()
            });
        let elem_symbol =
            expanded_node.with_properties_unwrapped(|properties: &mut RepeatProperties| {
                properties.iterator_elem_symbol.clone()
            });
        let deps = [source_expression.untyped()];
        let last_length = Rc::new(RefCell::new(0));
        expanded_node
            .children
            .replace_with(Property::computed_with_name(
                move || {
                    let Some(cloned_expanded_node) = weak_ref_self.upgrade() else {
                        panic!("ran evaluator after expanded node dropped (repeat elem)")
                    };
                    let source = source_expression.get();
                    let source_len = if let PaxValue::Range(start, end) = source {
                        (isize::try_coerce(*end).unwrap() - isize::try_coerce(*start).unwrap())
                            as usize
                    } else if let PaxValue::Vec(v) = source {
                        v.len()
                    } else {
                        unreachable!("source must be a vec");
                    };
                    if source_len == *borrow!(last_length) {
                        return cloned_expanded_node.children.get();
                    }
                    *borrow_mut!(last_length) = source_len;
                    let template_children = cloned_self.base().get_instance_children();
                    let children_with_envs = iter::repeat(template_children)
                        .take(source_len)
                        .enumerate()
                        .flat_map(|(i, children)| {
                            let property_i = Property::new(i);
                            let cp_source_expression = source_expression.clone();
                            let property_elem = Property::computed_with_name(
                                move || {
                                    let source = cp_source_expression.get();
                                    if let PaxValue::Range(start, _) = source {
                                        let start = isize::try_coerce(*start).unwrap();
                                        let elem = (start + i as isize).to_pax_value();
                                        elem
                                    } else if let PaxValue::Vec(v) = source {
                                        v[i].clone()
                                    } else {
                                        unreachable!("source must be a vec");
                                    }
                                },
                                &[source_expression.untyped()],
                                "repeat elem",
                            );
                            let new_repeat_item = Rc::new(RefCell::new(
                                RepeatItem {
                                    i: property_i.clone(),
                                    elem: property_elem.clone(),
                                }
                                .to_pax_value()
                                .to_pax_any(),
                            ));
                            let mut scope: HashMap<String, Variable> = HashMap::new();
                            if let Some(ref i_symbol) = i_symbol {
                                scope.insert(
                                    i_symbol.clone(),
                                    Variable::new_from_typed_property(property_i),
                                );
                            }
                            if let Some(ref elem_symbol) = elem_symbol {
                                scope.insert(
                                    elem_symbol.clone(),
                                    Variable::new_from_typed_property(property_elem),
                                );
                            }
                            let new_env = cloned_expanded_node.stack.push(scope, &new_repeat_item);
                            borrow!(children)
                                .clone()
                                .into_iter()
                                .zip(iter::repeat(new_env))
                        });
                    let ret = cloned_expanded_node.generate_children(
                        children_with_envs,
                        &cloned_context,
                        &cloned_expanded_node.parent_frame,
                    );
                    ret
                },
                &deps,
                &format!("repeat_children (node id: {})", expanded_node.id.0),
            ));
    }
}