import { ConditionalModalState, WixPatternsError, } from '@wix/bex-core';
import { computed, makeObservable, observable, runInAction, action, } from 'mobx';
import { getAsyncJob } from '@wix/bex-utils/@wix/ambassador-infra-asyncjobs-v1-async-job/http';
export const POLL_DELAY = 500;
export const MILLIS_FOR_SHOWING_SUCCESS = 1000;
export const MAX_JOB_RETRIES_COUNT = 4;
export class TagsBulkAssignAsyncState {
    constructor(params) {
        this.asyncJobProgressModalState = new ConditionalModalState();
        this.job = null;
        this.isDuringApplyChanges = false;
        this.currentInvocationParams = null;
        this.updatingTagsToast = null;
        this.getJobRetriesCount = 0;
        this.openAsyncJobProgressModal = () => {
            this.asyncJobProgressModalState.modal.open();
        };
        this.closeAsyncJobProgressModal = () => {
            this.asyncJobProgressModalState.modal.close();
            if (this.isJobInProgress) {
                this.updatingTagsToast =
                    this.container.showToast?.({
                        type: 'STANDARD',
                        message: this.container.translate('cairo.tags.bulkItemUpdate.updatingTags.toast'),
                        action: {
                            text: this.container.translate('cairo.tags.bulkItemUpdate.updatingTags.toast.CTA'),
                            onClick: async () => {
                                this.openAsyncJobProgressModal();
                            },
                            removeToastOnClick: true,
                        },
                        timeout: 'NONE',
                        biName: 'cairo-updating-bulk-assign-tags-async',
                    }) || null;
            }
        };
        const { collectionState, optimisticActions, container, tagsBIReporter, asyncBulkUpdateTags, } = params;
        this.collectionState = collectionState;
        this.optimisticActions = optimisticActions;
        this.container = container;
        this.tagsBIReporter = tagsBIReporter;
        this.asyncBulkUpdateTags = asyncBulkUpdateTags;
        makeObservable(this, {
            job: observable.ref,
            isDuringApplyChanges: observable,
            init: action,
            applyChanges: action,
            pollJobStatus: action,
            canBulkAssignAsync: computed,
            isJobCompleted: computed,
            isJobFailed: computed,
            isJobEnded: computed,
            isJobInProgress: computed,
            updatedItemsPercentage: computed,
        });
    }
    init() {
        const { onBeforeUnload } = this.container;
        const onBeforeUnloadSubscription = onBeforeUnload?.(action((event) => {
            if (this.isDuringApplyChanges) {
                event.preventDefault();
            }
        }));
        const disposers = [
            () => {
                onBeforeUnloadSubscription?.remove();
            },
            () => {
                if (this.timeout) {
                    window.clearTimeout(this.timeout);
                }
            },
        ];
        return () => {
            disposers.forEach((disposer) => disposer());
        };
    }
    async applyChanges({ newAssignedTagIds, newUnassignedTagIds, }) {
        this.isDuringApplyChanges = true;
        const { bulkSelect, result } = this.collectionState;
        const { allSelected, selectedValues, uncheckedValues } = bulkSelect;
        const params = {
            allSelected,
            selectedValues,
            uncheckedValues,
            query: result.originQuery,
            assignTags: { privateTags: { tagIds: newAssignedTagIds } },
            unassignTags: { privateTags: { tagIds: newUnassignedTagIds } },
        };
        await this.triggerBulkAssign(params);
    }
    async triggerBulkAssign(params) {
        if (!this.asyncBulkUpdateTags) {
            throw new Error('tried to bulk assign tags async without asyncBulkUpdateTags prop given');
        }
        this.currentInvocationParams = params;
        try {
            await this.startTagUpdateProcess(params);
        }
        catch (error) {
            this.showFailToCreateJobToast(error);
            this.resetStateParams();
        }
    }
    async startTagUpdateProcess(params) {
        this.openAsyncJobProgressModal();
        const { jobId } = await this.asyncBulkUpdateTags(params);
        await this.pollJobStatus(jobId);
    }
    async pollJobStatus(jobId) {
        let job = null;
        try {
            job = await this.fetchJobStatus(jobId);
        }
        catch (error) {
            this.handlePollingError(error);
            return;
        }
        runInAction(() => {
            this.job = job;
            if (this.isJobInProgress || !job) {
                this.scheduleNextPoll(jobId);
            }
            else if (this.isJobEnded) {
                this.handleJobEnd();
            }
        });
    }
    handlePollingError(error) {
        this.asyncJobProgressModalState.modal.close();
        this.showFailedToPollToast(error);
        this.resetStateParams();
    }
    async fetchJobStatus(jobId) {
        try {
            const { data: { job }, } = await this.container.errorHandler.withErrorHandler(() => this.container.httpClient.request(getAsyncJob({ jobId })), {});
            this.getJobRetriesCount = 0;
            return job;
        }
        catch (error) {
            if (this.getJobRetriesCount >= MAX_JOB_RETRIES_COUNT) {
                throw new WixPatternsError({
                    originalException: error,
                    errorCode: 'BulkAssignTagsFailed',
                });
            }
            this.container.errorHandler.reportRetryAttempt?.(error);
            this.getJobRetriesCount++;
            return null;
        }
    }
    scheduleNextPoll(jobId) {
        this.timeout = window.setTimeout(() => {
            runInAction(() => this.pollJobStatus(jobId));
        }, POLL_DELAY);
    }
    handleJobEnd() {
        this.timeout = window.setTimeout(() => {
            runInAction(() => {
                this.scheduleJobCompletionHandling();
            });
        }, MILLIS_FOR_SHOWING_SUCCESS);
    }
    scheduleJobCompletionHandling() {
        const lastInvocationParams = this.currentInvocationParams;
        const { showToast, translate: t } = this.container;
        if (this.isJobCompleted) {
            this.handleJobSuccess(lastInvocationParams);
        }
        else if (this.isJobFailed) {
            showToast?.({
                type: 'ERROR',
                message: t('cairo.tags.bulkItemUpdate.toast.error', {
                    total: this.job?.counts?.failCount,
                }),
                biName: 'cairo-update-bulk-assign-tags-async-job-status-fail',
            });
        }
        this.resetStateParams();
    }
    handleJobSuccess(lastInvocationParams) {
        const failCount = this.job?.counts?.failCount;
        if (failCount) {
            this.showPartialFailureToast({ failCount, lastInvocationParams });
        }
        this.showJobCompletedSuccessfullyToast();
        this.refreshCollection();
    }
    resetStateParams() {
        this.getJobRetriesCount = 0;
        this.job = null;
        this.currentInvocationParams = null;
        this.isDuringApplyChanges = false;
        this.updatingTagsToast?.remove();
        this.updatingTagsToast = null;
        this.closeAsyncJobProgressModal();
    }
    showPartialFailureToast({ failCount, lastInvocationParams, }) {
        this.container.showToast?.({
            type: 'WARNING',
            message: this.container.translate('cairo.tags.bulkItemUpdate.toast.error', { total: failCount }),
            action: {
                text: this.container.translate('cairo.tags.bulkItemUpdate.toast.error.CTA'),
                onClick: async () => {
                    if (lastInvocationParams) {
                        await this.triggerBulkAssign(lastInvocationParams);
                    }
                },
                removeToastOnClick: true,
            },
            biName: 'cairo-update-bulk-assign-tags-async-fail',
        });
    }
    showJobCompletedSuccessfullyToast() {
        this.container.showToast?.({
            type: 'SUCCESS',
            message: this.container.translate('cairo.tags.bulkItemUpdate.toast.success'),
            biName: 'cairo-update-bulk-assign-tags-async-success',
        });
    }
    showFailToCreateJobToast(error) {
        const { errorHandler, showToast, translate: t } = this.container;
        const resolvedError = errorHandler?.getResolvedError?.(error);
        showToast?.({
            type: 'ERROR',
            message: resolvedError?.message ??
                t('cairo.tags.bulkItemUpdate.toast.error', {
                    total: this.collectionState.bulkSelect.selectedCountOrTotal,
                }),
            action: resolvedError?.action,
            biName: 'cairo-update-bulk-assign-tags-async-create-job-fail',
            details: resolvedError?.requestId
                ? { requestId: resolvedError.requestId }
                : undefined,
        });
    }
    showFailedToPollToast(error) {
        const { errorHandler, showToast } = this.container;
        const resolvedError = errorHandler?.getResolvedError?.(error);
        showToast?.({
            type: 'ERROR',
            message: resolvedError?.message,
            action: resolvedError?.action,
            biName: 'cairo-update-bulk-assign-tags-async-poll-fail',
            details: resolvedError?.requestId
                ? { requestId: resolvedError.requestId }
                : undefined,
        });
    }
    refreshCollection() {
        this.collectionState.refreshAllPages();
    }
    get isJobCompleted() {
        return this.job?.status === 'FINISHED';
    }
    get isJobFailed() {
        return this.job?.status === 'FAILED';
    }
    get isJobEnded() {
        return this.isJobCompleted || this.isJobFailed;
    }
    get isJobInProgress() {
        return (this.job?.status === 'PROCESSING' || this.job?.status === 'INITIALIZED');
    }
    get canBulkAssignAsync() {
        return !!this.asyncBulkUpdateTags;
    }
    get updatedItemsPercentage() {
        if (!this.job) {
            return 0;
        }
        const alreadyUpdatedItemsCount = (this.job.counts?.successCount || 0) + (this.job.counts?.failCount || 0);
        const totalUpdatingItemsCount = this.job?.counts?.total ||
            this.collectionState.bulkSelect.selectedCountOrTotal;
        return Math.floor(100 * (alreadyUpdatedItemsCount / totalUpdatingItemsCount));
    }
}
//# sourceMappingURL=TagsBulkAssignAsyncState.js.map