/* eslint-disable no-new-func */
import { __assign, __read, __rest, __spreadArray, __values } from "tslib";
import { isEmptyObj, isSlot } from '../common';
import { deserializeAttributes, deserializeValue, PropBindType, ActionType, deserializeDynamicMap, EComponentType, processCommonStyle2CSSProperties, } from '@cloudbase/cals';
import { get as lodashGet } from 'lodash-es';
import { runtime } from './index';
export function getComponentsInfo(dependencies) {
    var outputObj = {};
    dependencies.forEach(function (_a) {
        var materialName = _a.name, components = _a.components, isComposite = _a.isComposite;
        if (isComposite) {
            components.forEach(function (component) {
                var lowCodes = component.lowCodes, componentInstances = component.componentInstances, compItem = __rest(component, ["lowCodes", "componentInstances"]);
                var sourceKey = "".concat(materialName, ":").concat(compItem.name);
                outputObj[sourceKey] = __assign({ isComposite: isComposite }, compItem);
            });
        }
        else {
            components.forEach(function (_a) {
                var name = _a.name, meta = _a.meta, emitEvents = _a.emitEvents, _b = _a.events, events = _b === void 0 ? undefined : _b, isContainer = _a.isContainer, compConfig = _a.compConfig, dataForm = _a.dataForm;
                outputObj["".concat(materialName, ":").concat(name)] = __assign(__assign({ isComposite: isComposite }, meta), { events: events, emitEvents: emitEvents, isContainer: isContainer, compConfig: compConfig, dataForm: dataForm });
            });
        }
    });
    return outputObj;
}
function getInputProps(componentsMetaMap) {
    var outputObj = {};
    var _loop_1 = function (key) {
        var component = componentsMetaMap[key];
        var sourceKey = key;
        if (component.isComposite) {
            var compItem_1 = component;
            Object.keys(compItem_1.dataForm || {}).forEach(function (key) {
                var _a;
                var _b, _c;
                var inputProps = ((_b = compItem_1.dataForm[key]) === null || _b === void 0 ? void 0 : _b.inputProp) || ((_c = compItem_1.dataForm[key]) === null || _c === void 0 ? void 0 : _c.syncProp);
                if (inputProps) {
                    outputObj[sourceKey] = __assign((_a = {}, _a[key] = inputProps, _a), (outputObj[sourceKey] || {}));
                }
            });
        }
        else {
            var inputProps = (component === null || component === void 0 ? void 0 : component.inputProps) || (component === null || component === void 0 ? void 0 : component.syncProps);
            if (inputProps) {
                outputObj[sourceKey] = inputProps;
            }
        }
    };
    for (var key in componentsMetaMap) {
        _loop_1(key);
    }
    return outputObj;
}
/**
 * utils end
 */
function splitDynamicMap(data) {
    var staticData = {};
    var dataBinds = [];
    for (var key in data) {
        var meta = data[key];
        if (!meta.type || meta.type === PropBindType.static) {
            staticData[key] = meta.value;
        }
        else if (meta.type === PropBindType.slot) {
            continue;
        }
        else {
            dataBinds.push({
                type: meta.type,
                bindDataPath: meta.value,
                propertyPath: key,
            });
        }
    }
    return { staticData: staticData, dataBinds: dataBinds };
}
function walkThroughCals(cals, process) {
    var e_1, _a;
    var _b, _c;
    var _d = cals.items, items = _d === void 0 ? [] : _d;
    cals.extra = cals.extra || {};
    if (((_b = cals === null || cals === void 0 ? void 0 : cals.directives) === null || _b === void 0 ? void 0 : _b[':if']) === false) {
        cals.items = [];
    }
    var filteredItems = [];
    try {
        for (var items_1 = __values(items), items_1_1 = items_1.next(); !items_1_1.done; items_1_1 = items_1.next()) {
            var item = items_1_1.value;
            if (((_c = item === null || item === void 0 ? void 0 : item.directives) === null || _c === void 0 ? void 0 : _c[':if']) === false) {
                continue;
            }
            filteredItems.push(item);
            item.extra = item.extra || {};
            item.extra.__parent = cals;
            walkThroughCals(item, process);
        }
    }
    catch (e_1_1) { e_1 = { error: e_1_1 }; }
    finally {
        try {
            if (items_1_1 && !items_1_1.done && (_a = items_1.return)) _a.call(items_1);
        }
        finally { if (e_1) throw e_1.error; }
    }
    cals.items = filteredItems;
    process(cals);
    return cals;
}
/**
 * widgets 不包括page 的 widges
 */
export function getComponentSchema(ctx, page, isComposite, componentsInfoMap, wrapperClass, options) {
    if (isComposite === void 0) { isComposite = false; }
    if (componentsInfoMap === void 0) { componentsInfoMap = {}; }
    var componentInputProps = getInputProps(componentsInfoMap) || {};
    var copyJson = JSON.parse(JSON.stringify(page));
    var compWidgets = {};
    var compDataBinds = {};
    var componentSchemaJson = walkThroughCals(copyJson, function (component) {
        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
        if (isSlot(component)) {
            return;
        }
        var componentName = component.component, module = component.module, _o = component.attributes, attributes = _o === void 0 ? {} : _o;
        var _p = (component.extra || {}).commonStyle, commonStyle = _p === void 0 ? {} : _p;
        var processedCommonStyle = attributes.style ? attributes.style : processCommonStyle2CSSProperties(commonStyle);
        if (processedCommonStyle) {
            component.extra.commonStyle = processedCommonStyle;
        }
        var sourceKey = "".concat(module, ":").concat(componentName);
        var _q = attributes.class, className = _q === void 0 ? '' : _q;
        var deserializeCtx = { page: page };
        var dynamicStyle = component[':style']
            ? deserializeValue(deserializeCtx, ':style', component[':style']).value
            : undefined;
        var dynamicClassList = component[':class']
            ? deserializeValue(deserializeCtx, ':class', component[':class']).value
            : undefined;
        var dynamicIf = ((_a = component.directives) === null || _a === void 0 ? void 0 : _a[':if'])
            ? deserializeValue(deserializeCtx, ':if', (_b = component.directives) === null || _b === void 0 ? void 0 : _b[':if']).value
            : undefined;
        var dynamicFor = ((_c = component.directives) === null || _c === void 0 ? void 0 : _c[':for'])
            ? deserializeValue(deserializeCtx, ':for', (_d = component.directives) === null || _d === void 0 ? void 0 : _d[':for']).value
            : undefined;
        if (dynamicIf != undefined &&
            (dynamicIf.value == undefined || dynamicIf.value === true || dynamicIf.value === false)) {
            dynamicIf = undefined;
        }
        if (dynamicFor) {
            var type = dynamicFor.type, value = dynamicFor.value;
            if (type === PropBindType.expression) {
                dynamicFor.value = value.replace(/^(.*?in\s)/, '');
            }
        }
        var data = deserializeAttributes(deserializeCtx, attributes);
        var _r = splitDynamicMap(data), dataBinds = _r.dataBinds, staticData = _r.staticData;
        var componentInfo = componentsInfoMap[sourceKey];
        if (componentInfo === null || componentInfo === void 0 ? void 0 : componentInfo.compConfig) {
            component.extra.compConfig = componentInfo === null || componentInfo === void 0 ? void 0 : componentInfo.compConfig;
        }
        if (componentInfo === null || componentInfo === void 0 ? void 0 : componentInfo.events) {
            component.extra.emitEvents = componentInfo === null || componentInfo === void 0 ? void 0 : componentInfo.events.map(function (item) { return item.name; });
        }
        else if (componentInfo === null || componentInfo === void 0 ? void 0 : componentInfo.emitEvents) {
            component.extra.emitEvents = componentInfo.emitEvents.map(function (item) { return item.eventName; });
        }
        // 生成 widgets/dataBinds
        if (!isSlot(component) && component.id) {
            var parentComponent = component.extra.__parent;
            var _visible = undefined;
            switch ((_e = component.directives) === null || _e === void 0 ? void 0 : _e[':if']) {
                case true:
                case false: {
                    _visible = (_f = component.directives) === null || _f === void 0 ? void 0 : _f[':if'];
                    break;
                }
                case undefined: {
                    _visible = true;
                    break;
                }
            }
            if (component.type !== 'PAGE') {
                compWidgets[component.id] = __assign(__assign({}, Object.entries(staticData).reduce(function (data, _a) {
                    var _b = __read(_a, 2), propsKey = _b[0], propsValue = _b[1];
                    var value = propsValue;
                    if (value && Array.isArray(value)) {
                        value = value.map(function (_, index) {
                            var _a, _b;
                            var block = (_a = attributes[propsKey]) === null || _a === void 0 ? void 0 : _a[index]; // 因为 value 里的是序列化过的，尤其是选区的是时候
                            var blockInstances = (_b = block === null || block === void 0 ? void 0 : block.selectableBlock) === null || _b === void 0 ? void 0 : _b.listeners;
                            if (blockInstances) {
                                block.selectableBlock.listenerInstances = generateListnerInstances(ctx, blockInstances || [], {
                                    isComposite: isComposite,
                                    looseError: options === null || options === void 0 ? void 0 : options.looseError,
                                });
                                return block;
                            }
                            else {
                                // 普通的非选区的，返回staticData 里的值
                                return _;
                            }
                        });
                    }
                    data[propsKey] = value;
                    return data;
                }, {})), { 
                    /**
                     * 此处新增key，需要同步定义 widgets.ts/widgetKeys 常量
                     */
                    _visible: _visible, style: __assign({}, processedCommonStyle), classList: className.split(/\s/).filter(function (item) { return !!item; }), widgetType: sourceKey, _parentId: parentComponent
                        ? isSlot(parentComponent) || parentComponent.type === 'PAGE'
                            ? (_h = (_g = parentComponent.extra) === null || _g === void 0 ? void 0 : _g.__parent) === null || _h === void 0 ? void 0 : _h.id
                            : parentComponent.id
                        : null, getConfig: function () {
                        var _a;
                        return (_a = componentInfo === null || componentInfo === void 0 ? void 0 : componentInfo.compConfig) !== null && _a !== void 0 ? _a : {};
                    }, _waForKey: (_j = component.directives) === null || _j === void 0 ? void 0 : _j.key });
            }
            var processedDataBinds = __spreadArray([], __read(dataBinds), false);
            if (dynamicIf === null || dynamicIf === void 0 ? void 0 : dynamicIf.value) {
                processedDataBinds.push({
                    type: dynamicIf.type,
                    bindDataPath: dynamicIf.value,
                    propertyPath: '_visible',
                });
            }
            if (dynamicFor === null || dynamicFor === void 0 ? void 0 : dynamicFor.value) {
                processedDataBinds.push({
                    type: dynamicFor.type,
                    bindDataPath: dynamicFor.value,
                    propertyPath: '_waFor',
                });
            }
            if (dynamicStyle === null || dynamicStyle === void 0 ? void 0 : dynamicStyle.value) {
                processedDataBinds.push({
                    type: dynamicStyle.type,
                    bindDataPath: dynamicStyle.value,
                    propertyPath: 'style',
                });
            }
            if (dynamicClassList) {
                processedDataBinds.push({
                    type: dynamicClassList.type,
                    bindDataPath: dynamicClassList.value,
                    propertyPath: 'classList',
                });
            }
            if (processedDataBinds.length > 0) {
                compDataBinds[component.id] = generateDataBinds(ctx, processedDataBinds, {
                    isComposite: isComposite,
                    looseError: options === null || options === void 0 ? void 0 : options.looseError,
                });
            }
        }
        component.listenerInstances = generateListnerInstances(ctx, component.listeners || [], {
            isComposite: isComposite,
            looseError: options === null || options === void 0 ? void 0 : options.looseError,
        });
        delete component.listeners;
        if (!isEmptyObj(attributes)) {
            // 如果是复合组件的根节点，则补充 wrapperClass
            if (isComposite) {
                if (!((_m = (_l = (_k = component === null || component === void 0 ? void 0 : component.extra) === null || _k === void 0 ? void 0 : _k.__parent) === null || _l === void 0 ? void 0 : _l.extra) === null || _m === void 0 ? void 0 : _m.__parent)) {
                    component.attributes = {};
                    component.attributes.class = "".concat(component.attributes.class, " ").concat(wrapperClass);
                }
            }
            // 组件双向绑定
            var syncProps_1 = componentInputProps[sourceKey];
            if (syncProps_1) {
                Object.keys(syncProps_1).forEach(function (syncPropKey) {
                    var syncProp = syncProps_1[syncPropKey];
                    var syncPropArr = syncProp !== null && syncProp !== void 0 ? syncProp : [];
                    // 统一转成数组处理
                    if (!Array.isArray(syncProp)) {
                        syncPropArr = [syncProp];
                    }
                    else {
                        syncPropArr = syncPropArr;
                    }
                    syncPropArr.forEach(function (_a) {
                        // 双向绑定需要优先第一个执行，否则会出现输入框的拼音被打断的问题
                        var changeEvent = _a.changeEvent, valueFromEvent = _a.valueFromEvent;
                        function withContext(context) {
                            var instance = new runtime.Function('context', "return function({ event, forItems }) {\n                  const $for = forItems;\n                  const $index = $for.forIndexes ? $for.forIndexes[$for.forIndexes.length - 1] : undefined;\n                  const wid = ".concat(isComposite ? 'this.widgets' : 'context.$page.widgets', ".").concat(component.id, ";\n                  const widgetData = $for.forIndexes && $for.forIndexes.length > 0 ? context.get(wid, forItems.forIndexes) : wid;\n                  widgetData.").concat(syncPropKey, " = ").concat(valueFromEvent, "\n                }"))(context);
                            return ctx.this ? instance.bind(this) : instance;
                        }
                        component.listenerInstances.unshift({
                            trigger: changeEvent,
                            instanceFunction: withContext(__assign(__assign({}, ctx), { get: lodashGet })),
                        });
                    });
                });
            }
        }
    });
    return {
        widgets: compWidgets,
        dataBinds: compDataBinds,
        componentSchema: componentSchemaJson,
    };
}
// convert data binds to functions for performance & simplicity
export function generateDataBinds(ctx, dataBinds, options) {
    var dataBindFuncs = {};
    dataBinds.forEach(function (bind) {
        var _a;
        var _b;
        if (!bind.bindDataPath) {
            return console.warn('bindDataPath error', bind.propertyPath);
        }
        var databindFunction = function () { };
        var funcCode = "function (){}";
        var alias = "const $for = forItems;\nconst $index = $for.forIndexes ? $for.forIndexes[$for.forIndexes.length - 1] : undefined;";
        try {
            if (bind.type === PropBindType.forItem) {
                funcCode = "function(forItems){ return forItems.".concat(bind.bindDataPath, " } ");
            }
            else if (bind.type === 'scope') {
                funcCode = "function(){ return ({__type: \"scopedValue\", getValue: function($scope){ return $scope.".concat(bind.bindDataPath, "}}); }");
            }
            else if (bind.type === PropBindType.expression) {
                if (options === null || options === void 0 ? void 0 : options.isComposite) {
                    funcCode = " (forItems, $context) => {".concat(alias, " return (\n").concat(bind.bindDataPath.replace(/\$comp/g, 'this.$WEAPPS_COMP').replace(/;$/, '') || 'undefined', "\n)}");
                }
                else {
                    var code = bind.bindDataPath.replace(/;$/, '');
                    funcCode =
                        /\$scope\./.test(code) || code === '$scope'
                            ? "(forItems, event, $context) => ({__type: \"scopedValue\", getValue: ($scope) => { ".concat(alias, " return (\n").concat(code || 'undefined', "\n)}})")
                            : "(forItems, event, $context) => { ".concat(alias, " return (\n").concat(code || 'undefined', "\n)}");
                }
            }
            else if (bind.type === PropBindType.prop) {
                var bindDataPath = bind.bindDataPath;
                var isNegated = bindDataPath.startsWith('!');
                if (isNegated)
                    bindDataPath = bindDataPath.replace(/^!/, '');
                if (options === null || options === void 0 ? void 0 : options.isComposite) {
                    // 复合组件在预览时其实就是page，所以沿用page的变量即可
                    funcCode = "function() { return ".concat(isNegated ? '!' : '').concat('this.$WEAPPS_COMP', ".props.data.").concat(bindDataPath, " }");
                }
                else {
                    // 复合组件在预览时其实就是page，所以沿用page的变量即可
                    funcCode = "function() { return ".concat(isNegated ? '!' : '', "$page.props.data.").concat(bindDataPath, " }");
                }
            }
            else {
                var isGlobal = bind.bindDataPath.startsWith('global.');
                var returnValue = '';
                var PREFIX_MAP = (_a = {},
                    _a[PropBindType.state] = 'state',
                    _a[PropBindType.computed] = 'computed',
                    // [PropBindType.dataVar]: 'dataVar',
                    _a[PropBindType.stateData] = 'dataset.state',
                    _a[PropBindType.paramData] = 'dataset.params',
                    _a);
                switch (bind.type) {
                    case PropBindType.state:
                    case PropBindType.computed:
                    // case PropBindType.dataVar:
                    case PropBindType.stateData:
                    case PropBindType.paramData: {
                        if (isGlobal) {
                            returnValue = bind.bindDataPath.replace(/^global\./, "app.".concat(PREFIX_MAP[bind.type], "."));
                        }
                        else {
                            if (options === null || options === void 0 ? void 0 : options.isComposite) {
                                returnValue = bind.bindDataPath
                                    .replace(/^comp-\w+\./, "this.$WEAPPS_COMP.".concat(PREFIX_MAP[bind.type], "."))
                                    .replace(/^\$\w+_\d+\./, "this.$WEAPPS_COMP.".concat(PREFIX_MAP[bind.type], "."));
                            }
                            else {
                                returnValue = bind.bindDataPath.replace(/^\w+./, "$page.".concat(PREFIX_MAP[bind.type], "."));
                            }
                        }
                        funcCode = "function(){ return ".concat(returnValue, " }");
                        break;
                    }
                }
            }
            var imports = Object.keys(((_b = ctx === null || ctx === void 0 ? void 0 : ctx.app) === null || _b === void 0 ? void 0 : _b.utils) || {}).map(function (key) {
                return "const ".concat(key, " = app.utils[\"").concat(key, "\"]");
            });
            if (imports.length) {
                imports.push('');
            }
            databindFunction = new runtime.Function('context', "const { $page, app, $app } = context || {};\n        ".concat(imports.join(';'), "\n        return ").concat(funcCode))(ctx);
        }
        catch (e) {
            if (options === null || options === void 0 ? void 0 : options.looseError) {
                console.error('generate dynamic data error:', e);
                databindFunction = function () {
                    throw e;
                };
            }
            else {
                throw e;
            }
        }
        dataBindFuncs[bind.propertyPath] = databindFunction;
    });
    return dataBindFuncs;
}
/**
 * dataset 特殊的绑定格式
 * 要求：入参分别为 app ， page
 * 绑定只有变量，参数，表达式的绑定
 */
export function generateDataBindForDataset(bind) {
    var _a;
    var expr = '';
    var type = bind.type, bindDataPath = bind.bindDataPath;
    if (type === PropBindType.static || !type) {
        expr = typeof bindDataPath === 'string' ? "'".concat(bindDataPath, "'") : JSON.stringify(bindDataPath);
    }
    else if (type === PropBindType.expression) {
        expr = bindDataPath;
    }
    else {
        var isGlobal = bindDataPath.startsWith('global.');
        var isCopositive = bindDataPath.startsWith('comp-') || bindDataPath.startsWith('$comp_');
        var PREFIX_MAP = (_a = {},
            _a[PropBindType.state] = 'state',
            _a[PropBindType.computed] = 'computed',
            _a[PropBindType.dataVar] = 'dataVar',
            _a[PropBindType.stateData] = 'dataset.state',
            _a[PropBindType.paramData] = 'dataset.params',
            _a);
        switch (type) {
            case PropBindType.state:
            case PropBindType.computed:
            case PropBindType.dataVar:
            case PropBindType.stateData:
            case PropBindType.paramData: {
                if (isCopositive) {
                    expr = bindDataPath
                        .replace(/^comp-\w+/, "$comp.".concat(PREFIX_MAP[type]))
                        .replace(/^\$comp_\w+/, "$comp.".concat(PREFIX_MAP[type]));
                }
                else {
                    expr = bindDataPath
                        .replace(/^\w+./, "".concat(isGlobal ? 'app' : '$page', ".").concat(PREFIX_MAP[type], "."))
                        .replace(/^comp-\w+/, "$comp.".concat(PREFIX_MAP[type]))
                        .replace(/^\$comp_\w+/, "$comp.".concat(PREFIX_MAP[type]));
                }
            }
        }
    }
    return new runtime.Function('app, $page', "const $app = app;\n    const imports = Object.keys(app && app.utils || {}).map(function (key) {\n      return `const ${key} = app.utils[\"${key}\"]`;\n    });\n    if(imports.length) {\n      imports.push('')\n    }\n    const body =  imports.join(';')+`return (\n".concat(expr || 'undefined', "\n)`\n   return new Function(\"\", body)()"));
}
function generateListnerInstances(ctx, listenerInstances, options) {
    var _this = this;
    if (options === void 0) { options = {
        isComposite: false,
    }; }
    var _a = options.isComposite, isComposite = _a === void 0 ? false : _a;
    return listenerInstances.map(function (listener) {
        var _a, _b, _c, _d, _e;
        var generatedListener = {
            key: listener.id,
            trigger: listener.eventName,
            isCapturePhase: !!listener.isCapturePhase,
            noPropagation: listener.noPropagation,
            instanceFunction: function () { },
            data: {},
            dataBinds: {},
        };
        if (listener.type === ActionType.Material) {
            var _f = listener.handler || {}, name_1 = _f.name, module_1 = _f.module;
            generatedListener.instanceFunction = new runtime.Function('context', "const { $page, app, $app, actionsMap = {} } = context || {};\n        return function(...args){ return actionsMap[\"".concat(module_1, ":").concat(name_1, "\"]?.apply?.(null, args)} "))(ctx);
        }
        else if (listener.type === ActionType.PropEvent) {
            if (isComposite) {
                var instance = new runtime.Function('{ data, event, forItems }', "\n          event.detail.data = data;\n          this.props.emit('".concat(
                // TODO: CHECK listener.handler.name 具体是什么
                listener.handler.name || listener.jsCode, "', event.detail);"));
                generatedListener.instanceFunction = ctx.this ? instance.bind(_this) : instance;
            }
            else {
                generatedListener.instanceFunction = new runtime.Function('context', "return (...args) { return context.$page.props.events.".concat(((_a = listener.handler) === null || _a === void 0 ? void 0 : _a.name) || listener.jsCode, ".apply(null, args);}"))(ctx);
            }
        }
        else if (listener.type === ActionType.Inline) {
            // 内联 handler
            var instance = new runtime.Function('{event, domEvent, customEventData, data, forItems}', "return ".concat(listener.jsCode));
            if (isComposite) {
                generatedListener.instanceFunction = ctx.this ? instance.bind(_this) : instance;
            }
            else {
                generatedListener.instanceFunction = instance.bind(ctx.$page);
            }
        }
        else if (listener.type === ActionType.Platform) {
            var name_2 = ((_b = listener.handler) === null || _b === void 0 ? void 0 : _b.name) || listener.jsCode;
            generatedListener.instanceFunction = new runtime.Function('context', "return function({data}){ return context.app.".concat(name_2, "(data) } "))(ctx);
        }
        else if (listener.type === ActionType.DataSource) {
            generatedListener.instanceFunction = new runtime.Function('context', "return function({data}) {return context.app.cloud.callDataSource(data)}")(ctx);
        }
        else {
            // Lowcode action(handler)
            if (isComposite) {
                generatedListener.instanceFunction = new runtime.Function('', "return this.handler.".concat(((_c = listener.handler) === null || _c === void 0 ? void 0 : _c.name) || listener.jsCode, ".bind(this)")).bind(ctx.this)();
            }
            else {
                var name_3 = ((_d = listener.handler) === null || _d === void 0 ? void 0 : _d.name) || listener.jsCode;
                generatedListener.instanceFunction = new runtime.Function('context', "return context.$page.handler.".concat(name_3))(ctx);
            }
        }
        var processedParams = deserializeDynamicMap({
            page: { id: ctx.$page.id, type: EComponentType.PAGE },
        }, (_e = listener.handler) === null || _e === void 0 ? void 0 : _e.params);
        var _g = splitDynamicMap(processedParams), staticData = _g.staticData, dataBinds = _g.dataBinds;
        generatedListener.data = staticData;
        generatedListener.dataBinds = generateDataBinds(ctx, dataBinds, options);
        return generatedListener;
    });
}
//# sourceMappingURL=generate.js.map