// target -> Proxy
const cachedRaw = new WeakMap();
// Proxy -> target
const cachedProxies = new WeakMap();
export function createReactive(target, onChange, path = '') {
    if (cachedRaw.has(target)) {
        // return target
        return cachedRaw.get(target); // Return existing proxy
    }
    if (cachedProxies.has(target)) {
        return target;
    }
    const handler = {
        get(target, prop, receiver) {
            const value = Reflect.get(target, prop, receiver);
            if (typeof value === 'object' && value !== null) {
                const finalPath = path ? `${path}.${String(prop)}` : String(prop);
                return createReactive(value, onChange, finalPath);
            }
            return value;
        },
        set(target, prop, value, receiver) {
            const oldValue = Reflect.get(target, prop, receiver);
            const success = Reflect.set(target, prop, value); // do not set the receiver here
            // he receiver is the proxy itself and if we set the value on the proxy, it will
            // create an infinite loop.
            if (success && oldValue !== value) {
                // Check if the old value was proxied and remove it from the cache
                if (typeof oldValue === 'object' && oldValue !== null) {
                    const oldProxy = cachedRaw.get(oldValue);
                    if (oldProxy) {
                        cachedRaw.delete(oldValue);
                        cachedProxies.delete(oldProxy);
                    }
                }
                onChange({
                    path: path ? `${path}.${String(prop)}` : String(prop),
                    value,
                    oldValue,
                });
            }
            return success;
        },
        deleteProperty(target, prop) {
            const oldValue = Reflect.get(target, prop);
            const success = Reflect.deleteProperty(target, prop);
            if (success) {
                // Check if the old value was proxied and remove it from the cache
                if (typeof oldValue === 'object' && oldValue !== null) {
                    const oldProxy = cachedRaw.get(oldValue);
                    if (oldProxy) {
                        cachedRaw.delete(oldValue);
                        cachedProxies.delete(oldProxy);
                    }
                }
                onChange({
                    path: path ? `${path}.${String(prop)}` : String(prop),
                    value: undefined,
                    oldValue,
                });
            }
            return success;
        },
    };
    const proxy = new Proxy(target, handler);
    cachedRaw.set(target, proxy);
    cachedProxies.set(proxy, target);
    return proxy;
}
/**
 * Removes the proxy and cleans up the cached data.
 *
 * @param reactiveObject The reactive object to unhook.
 * @returns true if the object was reactive and was unhooked, false otherwise.
 */
export function unhookReactive(reactiveObject) {
    const raw = cachedProxies.get(reactiveObject);
    if (!raw) {
        return false; // Not a reactive object
    }
    cachedRaw.delete(raw);
    cachedProxies.delete(reactiveObject);
    return true;
}
//# sourceMappingURL=reactive.js.map