var __rest = (this && this.__rest) || function (s, e) {
    var t = {};
    for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
        t[p] = s[p];
    if (s != null && typeof Object.getOwnPropertySymbols === "function")
        for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
            if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
                t[p[i]] = s[p[i]];
        }
    return t;
};
import { jsx as _jsx, jsxs as _jsxs } from "@emotion/react/jsx-runtime";
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { merge } from 'lodash-es';
export const ImplWrapper = React.forwardRef((props, ref) => {
    const { component: c, targetSlot, app, children, componentWrapper: ComponentWrapper, services, } = props;
    const { registry, stateManager, globalHandlerMap, apiService } = props.services;
    const Impl = registry.getComponent(c.parsedType.version, c.parsedType.name).impl;
    if (!globalHandlerMap.has(c.id)) {
        globalHandlerMap.set(c.id, {});
    }
    let handlerMap = globalHandlerMap.get(c.id);
    useEffect(() => {
        const handler = (s) => {
            if (s.componentId !== c.id) {
                return;
            }
            if (!handlerMap[s.name]) {
                return;
            }
            handlerMap[s.name](s.parameters);
        };
        apiService.on('uiMethod', handler);
        return () => {
            apiService.off('uiMethod', handler);
            globalHandlerMap.delete(c.id);
        };
    }, []);
    const mergeState = useCallback((partial) => {
        stateManager.store[c.id] = Object.assign(Object.assign({}, stateManager.store[c.id]), partial);
    }, [c.id]);
    const subscribeMethods = useCallback((map) => {
        handlerMap = Object.assign(Object.assign({}, handlerMap), map);
        globalHandlerMap.set(c.id, handlerMap);
    }, []);
    const [traitResults, setTraitResults] = useState(() => {
        return c.traits.map(trait => excecuteTrait(trait, stateManager.deepEval(trait.properties).result));
    });
    function excecuteTrait(trait, traitProperty) {
        const tImpl = registry.getTrait(trait.parsedType.version, trait.parsedType.name).impl;
        return tImpl(Object.assign(Object.assign({}, traitProperty), { componentId: c.id, mergeState,
            subscribeMethods,
            services }));
    }
    useEffect(() => {
        const stops = [];
        const properties = [];
        c.traits.forEach((t, i) => {
            const { result, stop } = stateManager.deepEval(t.properties, ({ result: property }) => {
                const traitResult = excecuteTrait(t, property);
                setTraitResults(oldResults => {
                    const newResults = [...oldResults];
                    newResults[i] = traitResult;
                    return newResults;
                });
                stops.push(stop);
            });
            properties.push(result);
        });
        setTraitResults(c.traits.map((trait, i) => excecuteTrait(trait, properties[i])));
        return () => stops.forEach(s => s());
    }, [c.traits]);
    const propsFromTraits = useMemo(() => {
        return Array.from(traitResults.values()).reduce((prevProps, result) => {
            var _a, _b;
            if (!result.props) {
                return prevProps;
            }
            let effects = (prevProps === null || prevProps === void 0 ? void 0 : prevProps.effects) || [];
            if ((_a = result.props) === null || _a === void 0 ? void 0 : _a.effects) {
                effects = effects === null || effects === void 0 ? void 0 : effects.concat((_b = result.props) === null || _b === void 0 ? void 0 : _b.effects);
            }
            return merge(prevProps, result.props, { effects });
        }, {});
    }, [traitResults]);
    const [evaledComponentProperties, setEvaledComponentProperties] = useState(() => {
        return merge(stateManager.deepEval(c.properties).result, propsFromTraits);
    });
    useEffect(() => {
        const { result, stop } = stateManager.deepEval(c.properties, ({ result: newResult }) => {
            setEvaledComponentProperties(Object.assign({}, newResult));
        });
        setEvaledComponentProperties(Object.assign({}, result));
        return stop;
    }, [c.properties]);
    const mergedProps = Object.assign(Object.assign({}, evaledComponentProperties), propsFromTraits);
    const C = (_jsx(Impl, Object.assign({}, mergedProps, props, { mergeState: mergeState, subscribeMethods: subscribeMethods }), c.id));
    let result = (_jsxs(React.Fragment, { children: [C, children] }, c.id));
    let parentComponent;
    if (targetSlot && app) {
        parentComponent = app.spec.components.find(c => c.id === targetSlot.id);
    }
    if (ComponentWrapper &&
        c.parsedType.name !== 'dummy' &&
        c.parsedType.name !== 'grid_layout') {
        result = (_jsx(ComponentWrapper, Object.assign({ component: c, parentType: (parentComponent === null || parentComponent === void 0 ? void 0 : parentComponent.type) || '' }, { children: result }), void 0));
    }
    if ((parentComponent === null || parentComponent === void 0 ? void 0 : parentComponent.parsedType.name) === 'grid_layout') {
        const { component, services, targetSlot, app, slotsMap, componentWrapper, gridCallbacks } = props, restProps = __rest(props, ["component", "services", "targetSlot", "app", "slotsMap", "componentWrapper", "gridCallbacks"]);
        result = (_jsx("div", Object.assign({ "data-meta-ui-id": c.id, ref: ref }, restProps, { children: result }), c.id));
    }
    return result;
});
//# sourceMappingURL=ImplWrapper.js.map