"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createDataBinds = exports.createEventHanlders = exports.createWidgetProps = exports.generatedDynamicData = exports.extractWidgetProps = 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");
function extractWidgetProps(props, compInfo) {
    var _a, _b;
    const { classList } = props;
    const staticProps = {
        style: (0, cals_1.processCommonStyle2CSSProperties)(props.commonStyle, {
            toRpx: true,
            toRem: false,
        }),
        classList: classList || [],
    };
    const { data = {} } = props;
    Object.assign(staticProps, generatedDynamicData(data, compInfo).staticProps);
    if ((_a = props.directives) === null || _a === void 0 ? void 0 : _a.waForKey) {
        staticProps._waForKey = (_b = props.directives) === null || _b === void 0 ? void 0 : _b.waForKey.value;
    }
    return staticProps;
}
exports.extractWidgetProps = extractWidgetProps;
function generatedDynamicData(data, compInfo) {
    const staticProps = {};
    const boundProps = {};
    for (const key of Object.keys(data)) {
        if (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) {
        try {
            imports += (0, lowcode_generator_1.generateExpressionAlias)(value);
        }
        catch (e) {
            console.error('parse expression error', value, e === null || e === void 0 ? void 0 : e.message);
        }
        expr = value;
    }
    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+./, `${isGlobal ? 'app' : '$page'}.${PREFIX_MAP[type]}.`)
                        .replace(/^comp-\w+/, `$comp.${PREFIX_MAP[type]}`)
                        .replace(/^\$comp_\w+/, `$comp.${PREFIX_MAP[type]}`);
                }
            }
        }
    }
    return { imports, expression: expr };
}
function createWidgetProps(widgets, ctx) {
    const widgetProps = {};
    (0, weapp_1.walkThroughWidgets)(widgets, (id, widget, parentId) => {
        var _a;
        const { xComponent } = widget;
        const xProps = widget.xProps || {};
        if (!xComponent) {
            // skip slot component
            return;
        }
        const materialLib = ctx.materialLibs.find((lib) => lib.name === xComponent.moduleName);
        const miniprogramPlugin = (_a = ctx.miniprogramPlugins) === null || _a === void 0 ? void 0 : _a.find((plugin) => plugin.name === xComponent.moduleName);
        if (!materialLib && !miniprogramPlugin) {
            console.error('Component lib not found', xComponent);
            return;
        }
        const widegetComp = materialLib === null || materialLib === void 0 ? void 0 : materialLib.components.find((comp) => comp.name === xComponent.name);
        if (!widegetComp) {
            console.error('Component not found in lib', widegetComp);
            return;
        }
        widgetProps[id] = extractWidgetProps(xProps, widegetComp);
        widgetProps[id]._parentId = parentId;
        widgetProps[id]._order = widget.xIndex;
        widgetProps[id].widgetType = `${xComponent.moduleName}:${xComponent.name}`;
    });
    return widgetProps;
}
exports.createWidgetProps = createWidgetProps;
function createEventHanlders(widgets, componentApi, ctx, page) {
    let eventHandlers = {};
    const { listeners: pageListeners = [], id: pageId = '' } = page || {};
    (0, weapp_1.walkThroughWidgets)(widgets, (id, widget, parentId) => {
        var _a, _b, _c;
        const { xComponent } = widget;
        const xProps = widget.xProps || {};
        if (!xComponent) {
            // skip slot component
            return;
        }
        const materialLib = ctx.materialLibs.find((lib) => lib.name === xComponent.moduleName);
        const miniprogramPlugin = (_a = ctx.miniprogramPlugins) === null || _a === void 0 ? void 0 : _a.find((plugin) => plugin.name === xComponent.moduleName);
        if (!materialLib && !miniprogramPlugin) {
            console.error('Component lib not found', xComponent);
            return;
        }
        const compProto = materialLib
            ? materialLib.components.find((comp) => comp.name === xComponent.name)
            : (_b = miniprogramPlugin === null || miniprogramPlugin === void 0 ? void 0 : miniprogramPlugin.componentConfigs) === null || _b === void 0 ? void 0 : _b.find((comp) => comp.name === xComponent.name);
        if (!compProto) {
            return;
        }
        const listeners = (xProps.listeners || []).slice();
        // Generate form input value change builtin handler
        const { inputProps, syncProps } = compProto.meta || {};
        const syncConfigs = syncProps || inputProps || {};
        for (const valuProp in syncConfigs) {
            const config = syncConfigs[valuProp];
            const configs = Array.isArray(config) ? config : [config];
            configs.forEach(({ changeEvent, valueFromEvent = 'event.detail.value' }) => {
                listeners.unshift({
                    trigger: changeEvent,
                    jsCode: `event.currentTarget.${valuProp} = ${valueFromEvent}`,
                    type: weapps_core_1.ActionType.Inline,
                    data: {},
                });
            });
        }
        eventHandlers = Object.assign(eventHandlers, getListenersHandlers(listeners, id, componentApi));
        // 如果是数据容器，则生成一个onDataChange事件处理
        if ((_c = compProto === null || compProto === void 0 ? void 0 : compProto.compConfig) === null || _c === void 0 ? void 0 : _c.isDataContainer) {
            const customName = (0, wxml_1.getMpEventHanlderName)(id, 'onDataChange', {});
            eventHandlers[customName] = [
                {
                    key: '',
                    handler: `({event})=>{
          app.utils.set(event.currentTarget._scope, 'dataContext.data', event?.detail?.data);
          app.utils.set(event.currentTarget._scope, 'dataContext.state', event?.detail?.state);
        }`,
                    handlerModule: weapps_core_1.ActionType.Platform,
                    data: {},
                    boundData: {},
                },
            ];
        }
    });
    // 增加页面事件节点 handlers
    eventHandlers = Object.assign(eventHandlers, getListenersHandlers(pageListeners, pageId, componentApi));
    return eventHandlers;
}
exports.createEventHanlders = createEventHanlders;
/**
 * 根据 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;
        const handlerName = (0, wxml_1.getMpEventHanlderName)(id, l.trigger, l);
        eventHandlers[handlerName] = eventHandlers[handlerName] || [];
        const params = generatedDynamicData(l.data);
        let handler = l.handler ? l.handler.name : l.jsCode;
        switch (l.type) {
            case weapps_core_1.ActionType.Platform: {
                handler = `function({data}){ return app.${(_a = l.handler) === null || _a === void 0 ? void 0 : _a.name}(data)}`;
                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.${(_b = l.handler) === null || _b === void 0 ? void 0 : _b.name}({...event.detail, ...data}) }`;
                break;
            }
        }
        eventHandlers[handlerName].push({
            key: l.key || '',
            handler: handler,
            handlerModule: (_c = l.handler) === null || _c === void 0 ? void 0 : _c.moduleName,
            data: params.staticProps,
            boundData: params.boundProps,
            type: l.type,
        });
    });
    return eventHandlers;
}
function createDataBinds(widgets, ctx) {
    const dataBinds = {};
    (0, weapp_1.walkThroughWidgets)(widgets, (id, widget, parentId) => {
        var _a;
        const { xComponent } = widget;
        const xProps = widget.xProps || {};
        if (!xComponent) {
            // skip slot component
            return;
        }
        const materialLib = ctx.materialLibs.find((lib) => lib.name === xComponent.moduleName);
        const miniprogramPlugin = (_a = ctx.miniprogramPlugins) === null || _a === void 0 ? void 0 : _a.find((plugin) => plugin.name === xComponent.moduleName);
        if (!materialLib && !miniprogramPlugin) {
            console.error('Component lib not found', xComponent);
            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);
        setDataBind(dataBinds[id], 'classList', xProps.classListBind);
        setDataBind(dataBinds[id], 'style', xProps.styleBind);
        const { classList, style } = dataBinds[id];
        if (classList) {
            dataBinds[id].classList = {
                ...classList,
                expression: `concatClassList(${classList === null || classList === void 0 ? void 0 : classList.expression}, widgetProps.${id}.classList)`,
            };
        }
        if (style) {
            dataBinds[id].style = {
                ...style,
                expression: `px2rpx({...widgetProps.${id}.style, ...(${style === null || style === void 0 ? void 0 : style.expression})})`,
            };
        }
        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]
        // }
    }
}
