"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.findComponentInfo = exports.processRepeaterSchema = exports.createDataBinds = exports.createTemplateEventFlows = exports.createTemplateQuery = exports.generateDataContainerListeners = exports.generateSyncListeners = exports.createEventHandlers = exports.createWidgetProps = exports.generatedDynamicData = void 0;
const weapps_core_1 = require("@cloudbase/lowcode-generator/lib/weapps-core");
const lowcode_generator_1 = require("@cloudbase/lowcode-generator");
const weapp_1 = require("@cloudbase/lowcode-generator/lib/generator/util/weapp");
const wxml_1 = require("./wxml");
const mp_1 = require("@cloudbase/lowcode-generator/lib/generator/config/mp");
const cals_1 = require("@cloudbase/cals");
const config_1 = require("../config");
function extractWidgetProps(props, compInfo, processCssUnit = 'rpx') {
    var _a, _b, _c;
    const { classList, staticResourceAttribute = [], data = {}, style } = props;
    const staticProps = {
        style: style && ((_a = Object.keys(style || {})) === null || _a === void 0 ? void 0 : _a.length)
            ? (0, cals_1.processCSSPropertiesUnit)(style || {}, processCssUnit)
            : {
                ...(0, cals_1.processCommonStyle2CSSProperties)(props.commonStyle || {}, {
                    defaultUnit: processCssUnit,
                }),
            },
        classList: classList || [],
    };
    if (staticResourceAttribute === null || staticResourceAttribute === void 0 ? void 0 : staticResourceAttribute.length) {
        staticProps._staticResourceAttribute = staticResourceAttribute;
    }
    Object.assign(staticProps, generatedDynamicData(data, compInfo).staticProps);
    if ((_b = props.directives) === null || _b === void 0 ? void 0 : _b.waForKey) {
        staticProps._waForKey = (_c = props.directives) === null || _c === void 0 ? void 0 : _c.waForKey.value;
    }
    return staticProps;
}
function generatedDynamicData(data, compInfo, filterBuiltInProps = true) {
    const staticProps = {};
    const boundProps = {};
    for (const key of Object.keys(data)) {
        if (filterBuiltInProps && mp_1.builtinWigetProps.indexOf(key) > -1)
            continue;
        const { type, value } = data[key];
        if (!type || type === 'static') {
            staticProps[key] = value;
            if (value === undefined) {
                /* 为了避免绑定undefined的时候，与组件properties类型声明不匹配
                在小程序基础库2.18后会爆warning
                所以提前给他准备空值
                */
                if ((compInfo === null || compInfo === void 0 ? void 0 : compInfo.dataForm[key]) && mp_1.jsonSchemaType2jsClass[compInfo.dataForm[key].type]) {
                    const globalNamespace = typeof globalThis === 'undefined' ? global : globalThis;
                    const constructorName = mp_1.jsonSchemaType2jsClass[compInfo.dataForm[key].type];
                    const defaultValue = globalNamespace[constructorName]();
                    staticProps[key] = defaultValue;
                }
            }
        }
        else {
            setDataBind(boundProps, key, data[key]);
        }
    }
    return { staticProps, boundProps };
}
exports.generatedDynamicData = generatedDynamicData;
// For JS
function generateDataBindMeta(bind) {
    let expr = '';
    let imports = 'const $for = forItems; const $index=lists?.[0]?.currentIndex;'; // 基础 alias
    const { type, value } = bind;
    if (type === weapps_core_1.PropBindType.forItem) {
        expr = `$for.${value}`;
    }
    else if (type === weapps_core_1.PropBindType.expression) {
        // expr = value ? value.replace(/\/\/[\s\S]*?\r?\n?$/gm, '').replace(/;\s*$/, '') : value;
        expr = value;
        try {
            imports += (0, lowcode_generator_1.generateExpressionAlias)(expr);
        }
        catch (e) {
            console.error('parse expression error', expr, e === null || e === void 0 ? void 0 : e.message);
        }
    }
    else if (type === weapps_core_1.PropBindType.prop) {
        const isNegated = value.startsWith('!');
        const originValue = value.replace(/^!/, '');
        expr = `${isNegated ? '!' : ''}$comp.props.data.${originValue}`;
    }
    else {
        const isGlobal = value.startsWith('global.');
        const isCopositive = value.startsWith('comp-') || value.startsWith('$comp_');
        const PREFIX_MAP = {
            [weapps_core_1.PropBindType.state]: 'state',
            [weapps_core_1.PropBindType.computed]: 'computed',
            [weapps_core_1.PropBindType.dataVar]: 'dataVar',
            [weapps_core_1.PropBindType.stateData]: 'dataset.state',
            [weapps_core_1.PropBindType.paramData]: 'dataset.params',
        };
        switch (type) {
            case weapps_core_1.PropBindType.state:
            case weapps_core_1.PropBindType.computed:
            case weapps_core_1.PropBindType.dataVar:
            case weapps_core_1.PropBindType.stateData:
            case weapps_core_1.PropBindType.paramData: {
                if (isCopositive) {
                    expr = value
                        .replace(/^comp-\w+/, `$comp.${PREFIX_MAP[type]}`)
                        .replace(/^\$comp_\w+/, `$comp.${PREFIX_MAP[type]}`);
                }
                else {
                    expr = value
                        .replace(/^\w+([.[])/, (_, $1) => `${isGlobal ? 'app' : '$page'}.${PREFIX_MAP[type]}${$1}`)
                        .replace(/^comp-\w+/, `$comp.${PREFIX_MAP[type]}`)
                        .replace(/^\$comp_\w+/, `$comp.${PREFIX_MAP[type]}`);
                }
            }
        }
    }
    return { imports, expression: expr ? expr.replace(/;\s*$/, '') : expr };
}
function createWidgetProps(ctx, widgets) {
    const widgetProps = {};
    (0, weapp_1.walkThroughWidgets)(widgets, (id, widget, parentId) => {
        const { xComponent } = widget;
        const xProps = widget.xProps || {};
        if (!xComponent) {
            // skip slot component
            return;
        }
        const widegetComp = findComponentInfo(xComponent, {
            materialLibs: ctx.materialLibs,
            miniprogramPlugins: ctx.miniprogramPlugins,
        });
        if (!widegetComp) {
            if (xComponent.moduleName === config_1.REPEATER.MODULE_NAME &&
                (xComponent.name === config_1.REPEATER.REPEATER_NAME || xComponent.name === config_1.REPEATER.REPEATER_ITEM_NAME)) {
                // skip
            }
            else {
                return;
            }
        }
        widgetProps[id] = extractWidgetProps(xProps, widegetComp, ctx.processCssUnit);
        widgetProps[id]._parentId = parentId;
        widgetProps[id]._order = widget.xIndex;
        widgetProps[id].widgetType = `${xComponent.moduleName}:${xComponent.name}`;
    });
    return widgetProps;
}
exports.createWidgetProps = createWidgetProps;
function createEventHandlers(ctx, widgets, componentApi, page) {
    let eventHandlers = {};
    const { listeners: pageListeners = [], id: pageId = '' } = page || {};
    (0, weapp_1.walkThroughWidgets)(widgets, (id, widget, parentId) => {
        var _a;
        const { xComponent } = widget;
        const xProps = widget.xProps || {};
        if (!xComponent) {
            // skip slot component
            return;
        }
        let compProto;
        if (xComponent.moduleName === config_1.REPEATER.MODULE_NAME &&
            (xComponent.name === config_1.REPEATER.REPEATER_ITEM_NAME || xComponent.name === config_1.REPEATER.REPEATER_NAME)) {
            // 内部虚组件，不校验compProto
        }
        else {
            compProto = findComponentInfo(xComponent, {
                materialLibs: ctx.materialLibs,
                miniprogramPlugins: ctx.miniprogramPlugins,
            });
            if (!compProto) {
                return;
            }
        }
        // Generate form input value change builtin handler
        const { inputProps, syncProps } = (compProto === null || compProto === void 0 ? void 0 : compProto.meta) || {};
        const syncConfigs = syncProps || inputProps || {};
        const listeners = [...generateSyncListeners(syncConfigs), ...(xProps.listeners || [])];
        // 如果是数据容器，则生成一个onDataChange事件处理
        if ((_a = compProto === null || compProto === void 0 ? void 0 : compProto.compConfig) === null || _a === void 0 ? void 0 : _a.isDataContainer) {
            listeners.unshift(...generateDataContainerListeners());
        }
        eventHandlers = Object.assign(eventHandlers, getListenersHandlers(listeners, id, componentApi));
    });
    // 增加页面事件节点 handlers
    eventHandlers = Object.assign(eventHandlers, getListenersHandlers(pageListeners, pageId, componentApi));
    return eventHandlers;
}
exports.createEventHandlers = createEventHandlers;
function generateSyncListeners(syncConfigs) {
    const listeners = [];
    for (const valuProp in syncConfigs) {
        const config = syncConfigs[valuProp];
        const configs = Array.isArray(config) ? config : [config];
        configs.forEach(({ changeEvent, valueFromEvent = 'event.detail.value' }) => {
            listeners.push({
                trigger: changeEvent,
                jsCode: `({ event }) => {event.currentTarget.${valuProp} = ${valueFromEvent};}`,
                type: weapps_core_1.ActionType.Inline,
                data: {},
            });
        });
    }
    return listeners;
}
exports.generateSyncListeners = generateSyncListeners;
function generateDataContainerListeners() {
    return [
        {
            trigger: 'onDataChange',
            jsCode: `({event})=>{
      if(event.currentTarget) {
        $app.utils.set(event.currentTarget._scope, 'dataContext.data', event?.detail?.data);
        $app.utils.set(event.currentTarget._scope, 'dataContext.state', event?.detail?.state);
      }
    }`,
            type: weapps_core_1.ActionType.Inline,
            data: {},
        },
    ];
}
exports.generateDataContainerListeners = generateDataContainerListeners;
function createTemplateQuery(ctx, query = {}) {
    return Object.entries(query).reduce((map, [key, item]) => {
        const { listeners = [], handler, data, ...rest } = item;
        const { staticProps, boundProps } = generatedDynamicData(data || {}, undefined, false);
        map[key] = {
            ...rest,
            handler: `(...args) => $app.${handler === null || handler === void 0 ? void 0 : handler.name}(...args)`,
            data: staticProps,
            dataBinds: boundProps,
            eventHandlers: getListenersHandlers(listeners, item.id, 'this'),
        };
        return map;
    }, {});
}
exports.createTemplateQuery = createTemplateQuery;
function createTemplateEventFlows(ctx, eventFlows = []) {
    return eventFlows.map((item) => {
        const { listeners = [], ...rest } = item;
        return {
            ...rest,
            eventHandlers: getListenersHandlers(listeners, item.id, 'this'),
        };
    });
}
exports.createTemplateEventFlows = createTemplateEventFlows;
/**
 * 根据 listeners 创建 handlers
 * @param listeners {IEventListener[]}
 * @param id {string}
 * @param componentApi {string}
 */
function getListenersHandlers(listeners = [], id, componentApi) {
    if (!id)
        return {};
    const eventHandlers = {};
    listeners.forEach((l) => {
        var _a, _b, _c, _d, _e;
        const handlerName = (0, wxml_1.getMpEventHandlerName)(id, l.trigger, l);
        const sourceKey = `${((_a = l.handler) === null || _a === void 0 ? void 0 : _a.moduleName) || ''}:${((_b = l.handler) === null || _b === void 0 ? void 0 : _b.name) || ''}`;
        eventHandlers[handlerName] = eventHandlers[handlerName] || [];
        const { staticProps, boundProps } = generatedDynamicData(l.data, undefined, false);
        let handler = l.handler ? l.handler.name : l.jsCode;
        switch (l.type) {
            case weapps_core_1.ActionType.Platform: {
                handler = '';
                switch (sourceKey) {
                    case `${weapps_core_1.ActionType.Platform}:callNanoFlow`: {
                        handler = `function({data}){ return $w[data.id]?.trigger?.(data.data)}`;
                        break;
                    }
                    case `${weapps_core_1.ActionType.Platform}:callQuery`: {
                        handler = `function({data}){ return $w[data.id]?.[data.method](data.data)}`;
                        break;
                    }
                    case `${weapps_core_1.ActionType.Platform}:invoke`: {
                        handler = `function({data, $w}){ return $app.invoke({...data, component: $w[data?.component]?._widget});}`;
                        break;
                    }
                    case `${weapps_core_1.ActionType.Platform}:setCurrentPageParams`: {
                        handler = `function({data}) { return $app.utils.getCurrentPage().setParams(data.params);}`;
                        break;
                    }
                    default: {
                        handler = `function({data}){ return $app.${(_c = l.handler) === null || _c === void 0 ? void 0 : _c.name}(data)}`;
                        break;
                    }
                }
                break;
            }
            case weapps_core_1.ActionType.DataSource: {
                handler = `function({data}){ return $app.cloud.callDataSource(data)}`;
                break;
            }
            case weapps_core_1.ActionType.PropEvent: {
                handler = `function({event, data = {}}){ return ${componentApi}.props.events.${(_d = l.handler) === null || _d === void 0 ? void 0 : _d.name}({...event.detail, ...data}) }`;
                break;
            }
        }
        eventHandlers[handlerName].push({
            key: l.key || '',
            handler,
            handlerModule: (_e = l.handler) === null || _e === void 0 ? void 0 : _e.moduleName,
            sourceKey,
            data: staticProps,
            boundData: boundProps,
            type: l.type,
        });
    });
    return eventHandlers;
}
function createDataBinds(ctx, widgets) {
    const dataBinds = {};
    const { processCssUnit = 'rpx' } = ctx;
    (0, weapp_1.walkThroughWidgets)(widgets, (id, widget, parentId) => {
        const { xComponent } = widget;
        const xProps = widget.xProps || {};
        if (!xComponent) {
            // skip slot component
            return;
        }
        if (xComponent.moduleName === config_1.REPEATER.MODULE_NAME &&
            (xComponent.name === config_1.REPEATER.REPEATER_NAME || xComponent.name === config_1.REPEATER.REPEATER_ITEM_NAME)) {
            // skip exist
        }
        else if (!findComponentInfo(xComponent, { materialLibs: ctx.materialLibs, miniprogramPlugins: ctx.miniprogramPlugins })) {
            return;
        }
        dataBinds[id] = {};
        Object.entries(xProps.data || {}).map(([prop, val]) => setDataBind(dataBinds[id], prop, val));
        // eslint-disable-next-line prefer-const
        const { directives = {} } = xProps;
        setDataBind(dataBinds[id], '_waFor', directives.waFor);
        setDataBind(dataBinds[id], '_waIf', directives.waIf);
        if (directives.waDisplay) {
            let { type, value } = directives.waDisplay;
            if (!type || type === 'static') {
                type = weapps_core_1.PropBindType.expression;
                value = `${JSON.stringify(value)}`;
            }
            setDataBind(dataBinds[id], '_waDisplay', {
                ...directives.waDisplay,
                type,
                value,
            });
        }
        setDataBind(dataBinds[id], 'classList', xProps.classListBind);
        setDataBind(dataBinds[id], 'style', xProps.styleBind);
        const { classList, style, _waDisplay } = dataBinds[id];
        if (classList) {
            dataBinds[id].classList = {
                ...classList,
                expression: `concatClassList(\n${classList === null || classList === void 0 ? void 0 : classList.expression}\n, widgetProps.${id}.classList)`,
            };
        }
        if (style) {
            const styleExpression = _waDisplay
                ? `((display)=>{ const style = {...widgetProps.${id}.style, ...(\n${style === null || style === void 0 ? void 0 : style.expression}\n)}; if(!display) {style.display = "none"}; return style })((()=>{${_waDisplay === null || _waDisplay === void 0 ? void 0 : _waDisplay.imports}\n return (\n${_waDisplay === null || _waDisplay === void 0 ? void 0 : _waDisplay.expression}\n)})())`
                : `{...widgetProps.${id}.style, ...(\n${style === null || style === void 0 ? void 0 : style.expression}\n)}`;
            dataBinds[id].style = {
                ...style,
                expression: processCssUnit === 'rpx' ? `px2rpx(${styleExpression})` : styleExpression,
            };
        }
        else if (_waDisplay) {
            const styleExpression = `((display)=>{ const style = {...widgetProps.${id}.style}; if(!display) {style.display = "none"}; return style })((\n${_waDisplay === null || _waDisplay === void 0 ? void 0 : _waDisplay.expression}\n))`;
            dataBinds[id].style = {
                ..._waDisplay,
                expression: processCssUnit === 'rpx' ? `px2rpx(${styleExpression})` : styleExpression,
            };
        }
        if (Object.keys(dataBinds[id]).length === 0) {
            delete dataBinds[id];
        }
    });
    return dataBinds;
}
exports.createDataBinds = createDataBinds;
function setDataBind(target, prop, val) {
    if ((val === null || val === void 0 ? void 0 : val.type) && val.type !== 'static') {
        const meta = generateDataBindMeta(val);
        if (meta.expression) {
            target[prop] = meta;
        }
        // Do not generate propBind since it's bound directly in wxml except for _waFor
        // const propsKeepPropBindInJs = ['_waFor', '_waIf']
        // if (
        //   val.type === PropBindType.prop &&
        //   propsKeepPropBindInJs.indexOf(prop) === -1
        // ) {
        //   delete target[prop]
        // }
    }
}
function processRepeaterSchema(ctx, componentInstanceMap) {
    (0, weapp_1.walkThroughWidgets)(componentInstanceMap, (id, component, parentId) => {
        var _a, _b;
        const { xComponent, xProps, properties = {} } = component;
        if (!xComponent) {
            // skip slot component
            return;
        }
        if (`${xComponent.moduleName}:${xComponent.name}` === `${config_1.REPEATER.MODULE_NAME}:${config_1.REPEATER.REPEATER_NAME}`) {
            const children = Object.values(properties);
            // 子项已经为 item 的不在处理
            if (children.length === 1) {
                const child = children[0];
                const { xComponent: childXComponent } = child;
                if (childXComponent &&
                    `${childXComponent.moduleName}:${childXComponent.name}` ===
                        `${config_1.REPEATER.MODULE_NAME}:${config_1.REPEATER.REPEATER_ITEM_NAME}`) {
                    return;
                }
            }
            // 给 Repeater 组件加一层虚拟项组件，在虚拟项组件上挂 for 循环
            const repeaterItemId = `${id}_item`;
            component.properties = {
                [repeaterItemId]: {
                    xComponent: {
                        moduleName: config_1.REPEATER.MODULE_NAME,
                        name: config_1.REPEATER.REPEATER_ITEM_NAME,
                    },
                    xProps: {
                        data: {},
                        directives: {
                            waFor: (_a = xProps === null || xProps === void 0 ? void 0 : xProps.data) === null || _a === void 0 ? void 0 : _a.data,
                            waForKey: (_b = xProps === null || xProps === void 0 ? void 0 : xProps.data) === null || _b === void 0 ? void 0 : _b.key,
                        },
                        style: {},
                        styleBind: {
                            type: 'static',
                            value: {},
                        },
                        classList: [],
                        classListBind: {
                            type: 'static',
                            value: [],
                        },
                        commonStyle: {},
                        customDataForm: {},
                        staticResourceAttribute: [],
                    },
                    properties,
                },
            };
        }
        else if (`${xComponent.moduleName}:${xComponent.name}` === `${config_1.REPEATER.MODULE_NAME}:${config_1.REPEATER.REPEATER_ITEM_NAME}`) {
            return;
        }
        else if (xComponent && !findComponentInfo(xComponent, ctx)) {
            component.properties = {};
        }
        return;
    });
    return componentInstanceMap;
}
exports.processRepeaterSchema = processRepeaterSchema;
/**
 * 查找符合小程序平台组件的 info
 */
function findComponentInfo(xComponent, { materialLibs, miniprogramPlugins }) {
    var _a, _b;
    if (!xComponent) {
        // skip slot component
        return;
    }
    const materialLib = materialLibs.find((lib) => lib.name === xComponent.moduleName);
    const miniprogramPlugin = miniprogramPlugins === null || miniprogramPlugins === void 0 ? void 0 : miniprogramPlugins.find((plugin) => plugin.name === xComponent.moduleName);
    if (!materialLib && !miniprogramPlugin) {
        console.error('Component lib not found', `${xComponent.moduleName}:${xComponent.name}`);
        return;
    }
    const compProto = materialLib
        ? materialLib.components.find((comp) => comp.name === xComponent.name)
        : (_a = miniprogramPlugin === null || miniprogramPlugin === void 0 ? void 0 : miniprogramPlugin.componentConfigs) === null || _a === void 0 ? void 0 : _a.find((comp) => comp.name === xComponent.name);
    const platform = (_b = compProto === null || compProto === void 0 ? void 0 : compProto.meta) === null || _b === void 0 ? void 0 : _b.platform;
    if (platform && Array.isArray(platform) && platform.length) {
        if (!platform.includes('MP') && !platform.includes('小程序')) {
            console.error('Component lib not found in mp platform', `${xComponent.moduleName}:${xComponent.name}`);
            return;
        }
    }
    return compProto;
}
exports.findComponentInfo = findComponentInfo;
