import { __awaiter } from "tslib";
import { resolveDataBinds, reportEvent } from '../common';
import { set as lodashSet } from 'lodash-es';
import { app } from '../../app/global-api';
import { runtime } from '../adapter';
import { untracked } from 'mobx';
import { generateWidgetAPIContext } from '../widgets';
import { _ACTIONS_KEY } from '@cloudbase/weda-client';
const DEFAULT_MAX_TIMEOUT = 10 * 1000;
class Event {
    constructor({ type = '', detail = undefined, currentTarget = undefined, target = undefined, _isCapturePhase = false, origin, originEvent = undefined, }) {
        function proxyWrapper(target, compareTarget, key) {
            try {
                return new Proxy(target, {
                    get(target, p) {
                        if (p !== 'id') {
                            if ((key === 'currentTarget' || key === 'target') &&
                                p !== '_userWidget' &&
                                target[p] !== (compareTarget === null || compareTarget === void 0 ? void 0 : compareTarget[p])) {
                                // console.log(`@deprecated event.${key}.${String(p)}`);
                                reportEvent(`event.${key}.${String(p)}`);
                            }
                        }
                        return target[p];
                    },
                });
            }
            catch (e) {
                return target;
            }
        }
        this.type = type;
        this.detail = detail;
        this.currentTarget = proxyWrapper(currentTarget, currentTarget === null || currentTarget === void 0 ? void 0 : currentTarget._userWidget, 'currentTarget');
        this.target = proxyWrapper(target, target === null || target === void 0 ? void 0 : target._userWidget, 'target');
        this.origin = origin;
        this.originEvent = originEvent;
        this._isCapturePhase = _isCapturePhase;
        return new Proxy(this, {
            get(target, prop) {
                switch (prop) {
                    case 'name': {
                        console.warn('[deprecated] event.name 将在未来版本放弃支持，请使用 event.type 替代');
                        return target.type;
                    }
                    case 'origin':
                    case 'target':
                    case 'currentTarget': {
                        return target[prop];
                        break;
                    }
                }
                return target[prop];
            },
        });
    }
}
function emitEvent(trigger, listeners = [], args, context = {}, syncCall = false) {
    var _a;
    return __awaiter(this, void 0, void 0, function* () {
        const { $w, pageId = '' } = context || {};
        const targetListeners = listeners.filter((l) => l.trigger === trigger);
        const currentEvent = args === null || args === void 0 ? void 0 : args.event;
        function checkPageActive(pageId, currentPageContext, listener) {
            var _a, _b;
            const { id, __internal__ } = currentPageContext || {};
            if (pageId && id) {
                if (pageId !== id || (__internal__ && !__internal__.active)) {
                    if (!((_b = (_a = listener.trigger) === null || _a === void 0 ? void 0 : _a.includes) === null || _b === void 0 ? void 0 : _b.call(_a, 'onDataChange'))) {
                        console.error(`Action error: [${listener.trigger}:${listener.key}] 页面生命周期结束，链式调用中断`);
                    }
                    return false;
                }
            }
            return true;
        }
        function processListener(listener) {
            var _a, _b, _c, _d;
            return __awaiter(this, void 0, void 0, function* () {
                // 当前非捕获Event，再判断冒泡行为
                if (!(currentEvent === null || currentEvent === void 0 ? void 0 : currentEvent._isCapturePhase) && listener.noPropagation) {
                    ((_a = args === null || args === void 0 ? void 0 : args.originalEvent) === null || _a === void 0 ? void 0 : _a.stopPropagation) && ((_b = args === null || args === void 0 ? void 0 : args.originalEvent) === null || _b === void 0 ? void 0 : _b.stopPropagation());
                }
                // 判断捕获的执行，只有执行的捕获与配置的捕获一致时。才会执行。
                // 为逻辑分支 额外新增处理
                const nextEvents = [
                    new Event(Object.assign(Object.assign({}, currentEvent), { type: '', detail: '', origin: currentEvent, originEvent: undefined })),
                ];
                if (((listener === null || listener === void 0 ? void 0 : listener.isCapturePhase) || false) === ((currentEvent === null || currentEvent === void 0 ? void 0 : currentEvent._isCapturePhase) || false)) {
                    /**
                     * 调用前置校验
                     * 是否仍可调用方法
                     */
                    if (!checkPageActive(pageId, $w === null || $w === void 0 ? void 0 : $w.page, listener)) {
                        return;
                    }
                    if ((listener === null || listener === void 0 ? void 0 : listener.sourceKey) === 'platform:utils.If') {
                        try {
                            const res = yield invokeListener(listener, currentEvent, context);
                            nextEvents[0].type = `${listener.key}.${res ? 'success' : 'fail'}`;
                        }
                        catch (e) {
                            nextEvents[0].type = `${listener.key}.fail`;
                        }
                        nextEvents[0].detail = currentEvent.detail;
                    }
                    else {
                        try {
                            const res = yield invokeListener(listener, currentEvent, context);
                            nextEvents[0].type = `${listener.key}.success`;
                            nextEvents[0].detail = res;
                            if ((listener === null || listener === void 0 ? void 0 : listener.sourceKey) === `platform:${_ACTIONS_KEY.showModal}`) {
                                const showModalNextEvent = new Event(Object.assign(Object.assign({}, currentEvent), { type: '', detail: res, origin: currentEvent, originEvent: undefined }));
                                if (res.cancel) {
                                    showModalNextEvent.type = `${listener.key}.cancel`;
                                }
                                else if (res.confirm) {
                                    showModalNextEvent.type = `${listener.key}.confirm`;
                                }
                                if (showModalNextEvent.type) {
                                    nextEvents.push(showModalNextEvent);
                                }
                            }
                        }
                        catch (e) {
                            if (e.message) {
                                e._target = `${((_c = currentEvent === null || currentEvent === void 0 ? void 0 : currentEvent.currentTarget) === null || _c === void 0 ? void 0 : _c.id) ? `组件${(_d = currentEvent === null || currentEvent === void 0 ? void 0 : currentEvent.currentTarget) === null || _d === void 0 ? void 0 : _d.id}` : ''}[${listener.trigger}事件，响应Id ${listener.key}]`;
                            }
                            nextEvents[0].type = `${listener.key}.fail`;
                            nextEvents[0].detail = e;
                            if (!syncCall) {
                                // 之前 invoke 内部catch 了错误，不会抛错
                                console.error(`Action error:`, generateErrorDebugMessage(e), e.stack);
                            }
                        }
                    }
                    /**
                     * 调用后置校验
                     * 是否触发后置事件
                     */
                    if (checkPageActive(pageId, $w === null || $w === void 0 ? void 0 : $w.page, listener)) {
                        let emitEventPromise = null;
                        nextEvents.forEach((nextEvent) => {
                            emitEventPromise = emitEvent(nextEvent.type, listeners, Object.assign(Object.assign({}, args), { event: nextEvent }), context, syncCall);
                        });
                        return syncCall ? emitEventPromise : undefined;
                    }
                    else if (syncCall) {
                        // 页面不再 active 且同步调用，此时抛出错误中断
                        throw new Error(`[${listener.trigger}:${listener.key}] 页面生命周期结束，链式调用中断`);
                    }
                }
            });
        }
        if (!targetListeners.length && typeof trigger === 'string' && /\.fail$/.test(trigger)) {
            const [preEventKey] = trigger.split('.') || [];
            const preListener = listeners.find((item) => item.key === preEventKey);
            /**
             * if 的 否分支， 不进行错误提醒
             */
            if (preListener.sourceKey !== 'platform:utils.If') {
                if (!syncCall) {
                    if (((_a = $w === null || $w === void 0 ? void 0 : $w.env) === null || _a === void 0 ? void 0 : _a.type) !== 'production') {
                        app.showModal({
                            title: `事件响应失败`,
                            content: generateErrorDebugMessage(currentEvent === null || currentEvent === void 0 ? void 0 : currentEvent.detail),
                            showCancel: false,
                        });
                    }
                    else {
                        app.showToast({
                            title: `事件响应失败：${generateErrorDebugMessage(currentEvent === null || currentEvent === void 0 ? void 0 : currentEvent.detail)}`,
                            icon: 'error',
                        });
                    }
                }
                else {
                    throw currentEvent === null || currentEvent === void 0 ? void 0 : currentEvent.detail;
                }
            }
        }
        else {
            const promise = Promise.all(targetListeners.map((listener) => {
                return processListener(listener).then((res) => ({ data: res, error: null }), 
                /**
                 * 按说 processListener 不会报错
                 */
                (error) => ({ data: null, error }));
            }));
            if (syncCall) {
                const listenerRes = yield promise;
                const res = [];
                for (const item of listenerRes) {
                    if (item.error) {
                        throw item.error;
                    }
                    else {
                        res.push(item.data);
                    }
                }
                return res.length ? res[0] : currentEvent.detail;
            }
        }
    });
}
function generateErrorDebugMessage(error) {
    let message = (error === null || error === void 0 ? void 0 : error.message) || '';
    try {
        if (!message) {
            message = JSON.stringify(error);
        }
    }
    catch (e) { }
    return `${(error === null || error === void 0 ? void 0 : error._target) ? `${error === null || error === void 0 ? void 0 : error._target}:` : ''}${message}`;
}
export function generateEmit($w, currentWidget = undefined, // page 节点暂未生成
injectContext = undefined, extraListeners = [], extraForContext = {}) {
    return (trigger, listeners = [], eventData, forItems, originalEvent, scopeContext, dataContext, pageId = '', syncCall = false) => {
        listeners = listeners.concat(extraListeners);
        if (injectContext === null || injectContext === void 0 ? void 0 : injectContext.emit) {
            return injectContext === null || injectContext === void 0 ? void 0 : injectContext.emit(trigger, listeners, eventData, forItems, originalEvent, scopeContext, dataContext);
        }
        else {
            const event = new Event({
                detail: eventData,
                type: trigger,
                target: currentWidget,
                currentTarget: currentWidget,
                originEvent: originalEvent,
            });
            const forContext = Object.assign(Object.assign({}, forItems), extraForContext);
            return emitEvent(event.type, listeners, {
                event,
            }, {
                pageId,
                forContext,
                scopeContext,
                dataContext,
                $w: untracked(() => {
                    return generateWidgetAPIContext($w, currentWidget, forContext, scopeContext);
                }),
            }, syncCall);
        }
    };
}
function invokeListener(listener, event, context) {
    var _a, _b, _c;
    const { $w, forContext, scopeContext = {}, dataContext, paramsContext = {} } = context || {};
    const { key, instanceFunction, data = {}, dataBinds = {} } = listener;
    if (!instanceFunction) {
        try {
            // @ts-ignore
            (_b = (_a = window._aegis) === null || _a === void 0 ? void 0 : _a.error) === null || _b === void 0 ? void 0 : _b.call(_a, 'SYSTEM_ERROR: listener not found', listener);
            console.error('$page current hander', (_c = runtime === null || runtime === void 0 ? void 0 : runtime['$page']) === null || _c === void 0 ? void 0 : _c.handler);
        }
        catch (e) { }
        throw new Error(`${key} handler 未找到'`);
    }
    // ToDo resolve databinds
    const action = instanceFunction;
    const resolvedData = mergeDynamic2StaticData(data, dataBinds, {
        $w,
        forContext,
        codeContext: { event },
        scopeContext,
        dataContext,
        paramsContext,
    });
    return action({
        data: resolvedData,
        event,
        forItems: forContext,
        $w,
    });
}
export function mergeDynamic2StaticData(staticData, dataBinds, context) {
    const { forContext = {}, codeContext = {}, scopeContext = {}, dataContext = {}, $w, paramsContext = {} } = context;
    const resolvedData = Object.assign({}, staticData);
    const resolvedDataBinds = resolveDataBinds(dataBinds, forContext, codeContext, scopeContext, true, dataContext, $w, paramsContext);
    // eslint-disable-next-line no-restricted-syntax
    for (const key in resolvedDataBinds) {
        if (resolvedDataBinds[key] && resolvedDataBinds[key].__type === 'scopedValue') {
            try {
                lodashSet(resolvedData, key, resolvedDataBinds[key].getValue(scopeContext));
            }
            catch (e) {
                lodashSet(resolvedData, key, '');
            }
        }
        else {
            lodashSet(resolvedData, key, resolvedDataBinds[key]);
        }
    }
    return resolvedData;
}
