/** @import { ComponentContextLegacy } from '#client' */
import {
    run,
    run_all
} from '../../../shared/utils.js';
import {
    derived
} from '../../reactivity/deriveds.js';
import {
    user_pre_effect,
    user_effect
} from '../../reactivity/effects.js';
import {
    component_context,
    deep_read_state,
    get,
    untrack
} from '../../runtime.js';

/**
 * Legacy-mode only: Call `onMount` callbacks and set up `beforeUpdate`/`afterUpdate` effects
 * @param {boolean} [immutable]
 */
export function init(immutable = false) {
    const context = /** @type {ComponentContextLegacy} */ (component_context);

    const callbacks = context.l.u;
    if (!callbacks) return;

    let props = () => deep_read_state(context.s);

    if (immutable) {
        let version = 0;
        let prev = /** @type {Record<string, any>} */ ({});

        // In legacy immutable mode, before/afterUpdate only fire if the object identity of a prop changes
        const d = derived(() => {
            let changed = false;
            const props = context.s;
            for (const key in props) {
                if (props[key] !== prev[key]) {
                    prev[key] = props[key];
                    changed = true;
                }
            }
            if (changed) version++;
            return version;
        });

        props = () => get(d);
    }

    // beforeUpdate
    if (callbacks.b.length) {
        user_pre_effect(() => {
            observe_all(context, props);
            run_all(callbacks.b);
        });
    }

    // onMount (must run before afterUpdate)
    user_effect(() => {
        const fns = untrack(() => callbacks.m.map(run));
        return () => {
            for (const fn of fns) {
                if (typeof fn === 'function') {
                    fn();
                }
            }
        };
    });

    // afterUpdate
    if (callbacks.a.length) {
        user_effect(() => {
            observe_all(context, props);
            run_all(callbacks.a);
        });
    }
}

/**
 * Invoke the getter of all signals associated with a component
 * so they can be registered to the effect this function is called in.
 * @param {ComponentContextLegacy} context
 * @param {(() => void)} props
 */
function observe_all(context, props) {
    if (context.l.s) {
        for (const signal of context.l.s) get(signal);
    }

    props();
}