use crate::filter_builder_methods;
use crate::utils::{create_filter_wrapper, DebounceOptions, FilterOptions, ThrottleOptions};
use default_struct_builder::DefaultBuilder;
use leptos::prelude::*;
use std::cell::RefCell;
use std::rc::Rc;
pub fn watch_with_options<W, T, DFn, CFn>(
    deps: DFn,
    callback: CFn,
    options: WatchOptions,
) -> impl Fn() + Clone + Send + Sync
where
    DFn: Fn() -> W + 'static,
    CFn: Fn(&W, Option<&W>, Option<T>) -> T + Clone + 'static,
    W: Clone + 'static,
    T: Clone + 'static,
{
    let cur_deps_value: Rc<RefCell<Option<W>>> = Rc::new(RefCell::new(None));
    let prev_deps_value: Rc<RefCell<Option<W>>> = Rc::new(RefCell::new(None));
    let prev_callback_value: Rc<RefCell<Option<T>>> = Rc::new(RefCell::new(None));
    let wrapped_callback = {
        let cur_deps_value = Rc::clone(&cur_deps_value);
        let prev_deps_value = Rc::clone(&prev_deps_value);
        let prev_callback_val = Rc::clone(&prev_callback_value);
        move || {
            #[cfg(debug_assertions)]
            let _z = leptos::reactive::diagnostics::SpecialNonReactiveZone::enter();
            let ret = callback(
                cur_deps_value
                    .borrow()
                    .as_ref()
                    .expect("this will not be called before there is deps value"),
                prev_deps_value.borrow().as_ref(),
                prev_callback_val.take(),
            );
            ret
        }
    };
    let filtered_callback =
        create_filter_wrapper(options.filter.filter_fn(), wrapped_callback.clone());
    let effect = Effect::watch(
        deps,
        move |deps_value, previous_deps_value, did_run_before| {
            cur_deps_value.replace(Some(deps_value.clone()));
            prev_deps_value.replace(previous_deps_value.cloned());
            let callback_value = if options.immediate && did_run_before.is_none() {
                Some(wrapped_callback())
            } else {
                filtered_callback().lock().unwrap().take()
            };
            prev_callback_value.replace(callback_value);
        },
        options.immediate,
    );
    move || effect.stop()
                                                                            }
#[derive(DefaultBuilder, Default)]
pub struct WatchOptions {
                    immediate: bool,
        filter: FilterOptions,
}
impl WatchOptions {
    filter_builder_methods!(
                filter
    );
}