/** @import { ActionPayload } from '#client' */
import {
    effect,
    render_effect
} from '../../reactivity/effects.js';
import {
    safe_not_equal
} from '../../reactivity/equality.js';
import {
    deep_read_state,
    untrack
} from '../../runtime.js';

/**
 * @template P
 * @param {Element} dom
 * @param {(dom: Element, value?: P) => ActionPayload<P>} action
 * @param {() => P} [get_value]
 * @returns {void}
 */
export function action(dom, action, get_value) {
    effect(() => {
        var payload = untrack(() => action(dom, get_value ? .()) || {});

        if (get_value && payload ? .update) {
            var inited = false;
            /** @type {P} */
            var prev = /** @type {any} */ ({}); // initialize with something so it's never equal on first run

            render_effect(() => {
                var value = get_value();

                // Action's update method is coarse-grained, i.e. when anything in the passed value changes, update.
                // This works in legacy mode because of mutable_source being updated as a whole, but when using $state
                // together with actions and mutation, it wouldn't notice the change without a deep read.
                deep_read_state(value);

                if (inited && safe_not_equal(prev, value)) {
                    prev = value;
                    /** @type {Function} */
                    (payload.update)(value);
                }
            });

            inited = true;
        }

        if (payload ? .destroy) {
            return () => /** @type {Function} */ (payload.destroy)();
        }
    });
}