import { action, makeObservable } from 'mobx';
import { TagExposure, } from '@wix/bex-utils/@wix/ambassador-os-tags-v1-tag/types';
import { v4 as uuid } from 'uuid';
import { EventEmitter } from 'events';
import { CollectionState } from '../CollectionState';
import { CollectionOptimisticActions } from '../CollectionOptimisticActions';
import { TaskState } from '../TaskState';
import { fqdnToString } from '../../util';
import { WixPatternsError } from '../../services';
import { createTagErrors, getTagsReachedLimitErrorMessage, } from '../../services/tagsService/createTagErrors';
import { TagsService } from '../../services/tagsService';
export const TAG_MAX_LENGTH = 30;
const MAX_TAGS = 1000;
const TAGS_PAGE_SIZE = 100;
// Pre-cursor tag creation ceiling (restored from before pagination, unrelated to page size)
const LEGACY_MAX_TAGS = 100;
export class TagsManagementState {
    constructor(params) {
        this.initTask = new TaskState();
        this.events = new EventEmitter();
        this.overrideShowToast = false;
        this.container = params.container;
        this.fqdn = params.fqdn;
        this.tagsService = new TagsService({
            container: this.container,
        });
        this.shouldAllowTagsPagination =
            !!params.container.internalExperiments?.enabled('specs.cairo.TagsPagination');
        this.collection = new CollectionState({
            ...params.container,
            limit: TAGS_PAGE_SIZE,
            events: {
                onError: (error) => {
                    this._resolvedListTagsError =
                        this.container.errorHandler.getResolvedError?.(error.err);
                },
            },
            paginationMode: this.shouldAllowTagsPagination ? 'cursor' : undefined,
            queryName: [this.fqdnString, 'tags'].join('/'),
            fetchData: async ({ cursor, limit, search }) => {
                if (this.shouldAllowTagsPagination) {
                    const result = await this.tagsService.queryTags({
                        fqdn: this.fqdnString,
                        exposure: TagExposure.PRIVATE,
                        query: {
                            cursorPaging: { limit, cursor: cursor ?? undefined },
                            ...(search
                                ? { filter: { name: { $startsWith: search } } }
                                : {}),
                        },
                    }, {}, {
                        serverErrorOverride: () => ({
                            message: this.container.translate('cairo.tags.collectionPage.popover.loadTags.error.body'),
                            action: {
                                text: this.container.translate('cairo.tags.loadTags.error.CTA'),
                                onClick: () => this.retry(),
                            },
                        }),
                    });
                    return {
                        items: result.items,
                        cursor: result.cursor,
                        hasNext: result.hasNext,
                    };
                }
                const fetchedItems = await this.tagsService.listTags({
                    fqdn: this.fqdnString,
                    exposure: TagExposure.PRIVATE,
                }, {}, {
                    serverErrorOverride: () => ({
                        message: this.container.translate('cairo.tags.collectionPage.popover.loadTags.error.body'),
                        action: {
                            text: this.container.translate('cairo.tags.loadTags.error.CTA'),
                            onClick: () => this.retry(),
                        },
                    }),
                });
                return { items: fetchedItems, total: fetchedItems.length };
            },
            itemKey: ({ id }) => id,
            itemName: ({ name }) => name,
            filters: {},
            showToast: (toastConfig) => {
                if (this.overrideShowToast) {
                    this.events.emit('showToast', toastConfig);
                    return {
                        remove: () => {
                            this.events.emit('hideToast', toastConfig);
                        },
                    };
                }
                if (params?.container?.showToast) {
                    return params.container.showToast(toastConfig);
                }
                return { remove: () => { } };
            },
        });
        this.optimisticActions = new CollectionOptimisticActions({
            container: this.container,
            collection: this.collection,
            orderBy: ({ sort }) => [
                ...(sort || []),
                {
                    fieldName: ({ createdDate }) => (createdDate ?? new Date()).valueOf(),
                    order: 'asc',
                },
            ],
        });
        makeObservable(this, {
            init: action,
            createTag: action,
            updateTag: action,
            deleteTag: action,
            retry: action,
        });
    }
    init() {
        if (!this._isFqdnValid()) {
            return;
        }
        const { collection, optimisticActions } = this;
        const disposers = [collection.init(), optimisticActions.init()];
        this.initTask.runOnce(async () => {
            collection.initTask.runOnce();
            await collection.initTask.status.promise;
        });
        return () => {
            disposers.forEach((disposer) => disposer());
        };
    }
    retry() {
        this.collection.initTask.runOnce();
    }
    get items() {
        return this.collection.result.items ?? [];
    }
    get hasMore() {
        return !this.collection.fetchedLastPage;
    }
    async loadMore() {
        await this.collection.fetchNextPageIfNeeded();
    }
    get isError() {
        return this.initTask.status.isError;
    }
    get error() {
        return this._resolvedListTagsError;
    }
    get isLoading() {
        const { status } = this.collection.initTask;
        return status.isIdle || status.isLoading;
    }
    get isSuccessful() {
        const { status } = this.collection.initTask;
        return status.isSuccess;
    }
    get fqdnString() {
        return fqdnToString(this.fqdn);
    }
    getTagById(tagId) {
        return this.collection.result._keyedItemsMap?.get(tagId)?.item;
    }
    getTagNameById(tagId) {
        return this.getTagById(tagId)?.name;
    }
    _isFqdnValid() {
        return this.fqdn.resource !== undefined;
    }
    _optimisticActionBaseParams(optimisticActionParams, { tagsBIReporter, actionName, origin, tagName, filteredListSize, isFromSearch = false, errorCode, }) {
        const { onError, onTryAgain, onUndo, ...otherOptimisticActionParams } = optimisticActionParams;
        return {
            onError: (error) => {
                tagsBIReporter.onError({
                    tagName,
                    actionName,
                    origin,
                }, error);
                this.container.internalMonitor.reportError({
                    error: new WixPatternsError({
                        errorCode,
                        originalException: error,
                    }),
                    category: 'Tags',
                });
                onError?.(error);
            },
            onTryAgain: () => {
                tagsBIReporter.onTryAgain({
                    actionName,
                    isFromSearch,
                    filteredListSize,
                });
                onTryAgain?.();
            },
            onUndo: () => {
                tagsBIReporter.onUndo({ actionName });
                onUndo?.();
            },
            ...otherOptimisticActionParams,
        };
    }
    get tagsLimit() {
        return this.shouldAllowTagsPagination ? MAX_TAGS : LEGACY_MAX_TAGS;
    }
    hasReachedTagsLimit() {
        return !this.hasMore && this.items.length >= this.tagsLimit;
    }
    showTagsLimitErrorToast(visualType) {
        const excessCount = Math.max(1, this.items.length - this.tagsLimit + 1);
        this.collection.showToast({
            message: getTagsReachedLimitErrorMessage({
                visualType,
                translate: this.container.translate,
                excessCount,
                limit: this.tagsLimit,
            }),
            biName: 'cairo-max-tags-error',
            type: 'ERROR',
        });
    }
    async createTag(params) {
        const { name, origin, tagSubOrigin, filteredListSize, id = uuid(), isFromSearch = false, onSuccess, optimisticActionParams = {}, tagsBIReporter, visualType = 'toast', } = params;
        const actionName = 'Add tag';
        tagsBIReporter.onAction({
            tagName: name,
            isFromSearch,
            actionName,
            origin,
            tagSubOrigin,
        });
        if (this.hasReachedTagsLimit()) {
            return this.showTagsLimitErrorToast(visualType);
        }
        const newTag = {
            id,
            name,
            fqdn: this.fqdnString,
            exposure: TagExposure.PRIVATE,
            createdDate: new Date(),
        };
        this.optimisticActions.createOne(newTag, {
            submit: async () => {
                const serverCreatedTag = await this.tagsService.createTag({
                    tag: newTag,
                }, createTagErrors({
                    translate: this.container.translate,
                    visualType,
                    retryFn: () => this.createTag(params),
                    excessCount: Math.max(1, this.items.length - this.tagsLimit + 1),
                    limit: this.tagsLimit,
                }));
                await onSuccess?.(serverCreatedTag);
                if (this.shouldAllowTagsPagination) {
                    // intentional fire-and-forget: optimistic state is already applied
                    void this.collection.refreshAllPages();
                }
                return [serverCreatedTag];
            },
            ...this._optimisticActionBaseParams(optimisticActionParams, {
                tagsBIReporter,
                actionName,
                origin,
                filteredListSize,
                tagName: name,
                isFromSearch,
                errorCode: 'CreateTagFailed',
            }),
        });
    }
    async updateTag({ tagToUpdate, previousName, origin, tagSubOrigin, filteredListSize, isFromSearch = false, optimisticActionParams = {}, tagsBIReporter, }) {
        const actionName = 'Rename tag';
        tagsBIReporter.onAction({
            actionName,
            previousName,
            tagName: tagToUpdate.name,
            isFromSearch,
            origin,
            tagSubOrigin,
        });
        this.optimisticActions.updateOne({ ...tagToUpdate, revision: (+tagToUpdate.revision + 1).toString() }, {
            submit: async () => {
                const updatedTag = await this.tagsService.updateTag({
                    tag: tagToUpdate,
                });
                if (this.shouldAllowTagsPagination) {
                    // intentional fire-and-forget: optimistic state is already applied
                    void this.collection.refreshAllPages();
                }
                return updatedTag;
            },
            ...this._optimisticActionBaseParams(optimisticActionParams, {
                tagsBIReporter,
                actionName,
                origin,
                filteredListSize,
                tagName: previousName,
                isFromSearch,
                errorCode: 'UpdateTagFailed',
            }),
        });
    }
    async deleteTag({ tagToDelete, origin, tagSubOrigin, filteredListSize, isFromSearch = true, optimisticActionParams = {}, tagsBIReporter, }) {
        const tagName = tagToDelete.name;
        const actionName = 'Delete tag';
        tagsBIReporter.onAction({
            actionName,
            tagName,
            isFromSearch,
            origin,
            tagSubOrigin,
        });
        await this.optimisticActions.deleteOne(tagToDelete, {
            submit: async () => {
                const result = await this.tagsService.deleteTag({
                    tagId: tagToDelete.id,
                });
                if (this.shouldAllowTagsPagination) {
                    // intentional fire-and-forget: optimistic state is already applied
                    void this.collection.refreshAllPages();
                }
                return result;
            },
            ...this._optimisticActionBaseParams(optimisticActionParams, {
                tagsBIReporter,
                actionName,
                origin,
                tagName,
                filteredListSize,
                isFromSearch,
                errorCode: 'RemoveTagFailed',
            }),
        });
    }
}
//# sourceMappingURL=TagsManagementState.js.map