import { addEventListener, isHttpError } from '../util';
export class CollectionOptimisticExecutor {
    constructor(params) {
        this.removeToast = null;
        this._actions = [];
        this.showToast = params.showToast;
        this.translate = params.translate;
        this.errorMonitor = params.errorMonitor;
        this.lodash = params.lodash;
        this.container = params.container;
        this.executePending = this.executePending.bind(this);
        this.scheduleExecution = params.batchWait
            ? this.lodash.throttle(this.executePending, params.batchWait, {
                leading: false,
                trailing: true,
            })
            : this.executePending;
    }
    removeAndShowToast(toastConfig) {
        const { removeToast, showToast } = this;
        removeToast?.();
        this.removeToast = showToast(toastConfig).remove;
    }
    rollback(actions, onRollback) {
        this._actions = this._actions.filter((action) => actions.indexOf(action) === -1);
        onRollback();
    }
    getSuccessToast(actions, { onRollback, successToast, showUndoToast, showUndoSuccessToast, onUndo, }) {
        const { container, showToast, translate: t } = this;
        let resolveUndoTimeoutPromise = null;
        let resolveDismissPromise = null;
        const state = {};
        if (showUndoToast) {
            const dismissPromise = new Promise((resolve) => {
                resolveDismissPromise = () => resolve(undefined);
            });
            state.undoToast = {
                timeoutPromises: [
                    new Promise((_resolve) => {
                        resolveUndoTimeoutPromise = _resolve;
                    }),
                    new Promise((resolve) => setTimeout(resolve, 6000)),
                    dismissPromise,
                ],
                removeNavigationListener: addEventListener(container.events, 'navigation', () => ({
                    title: t('cairo.dragAndDrop.leaveSite.modal.title'),
                })),
            };
        }
        if (successToast) {
            const toastConfig = typeof successToast === 'string'
                ? { message: successToast, biName: `cairo-undo` }
                : successToast;
            const userOnCloseClick = toastConfig.onCloseClick;
            this.removeAndShowToast({
                action: showUndoToast
                    ? {
                        text: t('cairo.toast.undo'),
                        uiType: 'LINK',
                        onClick: () => {
                            resolveUndoTimeoutPromise?.({ status: 'undone' });
                            this.rollback(actions, onRollback);
                            onUndo?.();
                            if (showUndoSuccessToast) {
                                showToast({
                                    type: 'STANDARD',
                                    priority: 'LOW',
                                    biName: `cairo-undone`,
                                    message: t('cairo.toast.undone'),
                                });
                            }
                        },
                        removeToastOnClick: true,
                    }
                    : undefined,
                ...toastConfig,
                type: 'SUCCESS',
                onCloseClick: () => {
                    userOnCloseClick?.();
                    resolveDismissPromise?.();
                },
            });
        }
        return state;
    }
    async _execute(params) {
        const { successToastState, submit, errorToast: errorToastFn, onTryAgain, onError, onRollback, eventuallyUpdated, onEventuallyUpdated, actions, } = params;
        const { errorMonitor, translate: t } = this;
        const { undoToast } = successToastState;
        try {
            if (undoToast) {
                const undoResult = await Promise.race(undoToast.timeoutPromises);
                undoToast.removeNavigationListener();
                if (undoResult?.status === 'undone') {
                    return undoResult;
                }
            }
            const submitPromise = await submit(actions);
            const updateServerAfterPromise = typeof eventuallyUpdated === 'number'
                ? await Promise.all([
                    submitPromise,
                    new Promise((resolve) => setTimeout(resolve, eventuallyUpdated)),
                ]).then(() => submitPromise)
                : submitPromise;
            const result = await updateServerAfterPromise;
            await onEventuallyUpdated?.();
            return {
                result,
                status: 'success',
            };
        }
        catch (err) {
            console.error(err);
            errorMonitor.captureException(err);
            this.rollback(actions, onRollback);
            onError?.(err);
            if (errorToastFn) {
                const onRetryClick = () => {
                    onTryAgain?.();
                    this.removeToast?.();
                    this.execute(actions, params);
                };
                const errorToast = errorToastFn(err, { retry: onRetryClick });
                const requestId = isHttpError(err) ? err.requestId : undefined;
                const toastConfig = {
                    action: {
                        uiType: 'LINK',
                        text: t('cairo.toast.retry'),
                        onClick: onRetryClick,
                        removeToastOnClick: true,
                    },
                    ...(typeof errorToast === 'string'
                        ? { message: errorToast, biName: `cairo-error-toast` }
                        : errorToast),
                    type: 'ERROR',
                    details: requestId ? { requestId } : undefined,
                };
                if (toastConfig.action) {
                    toastConfig.action.uiType = 'LINK';
                    toastConfig.action.removeToastOnClick = true;
                }
                this.removeAndShowToast(toastConfig);
            }
            return { status: 'error', err };
        }
    }
    async executePending(params) {
        const { _actions: actions } = this;
        this._actions = [];
        return this._execute({
            ...params,
            actions,
        });
    }
    execute(actions, params) {
        const { showUndoToast, showUndoSuccessToast, successToast, onRollback, onUndo, } = params;
        this._actions = [...this._actions, ...actions];
        const successToastState = this.getSuccessToast(actions, {
            onRollback,
            successToast,
            showUndoToast,
            showUndoSuccessToast,
            onUndo,
        });
        this.scheduleExecution({
            ...params,
            successToastState,
        });
    }
}
//# sourceMappingURL=CollectionOptimisticExecutor.js.map