import { getRelationProperty, hasHasManyRelation, hasLabel, } from "../../util/limetype";
import { AggregateOperator, PlatformServiceName, } from "@limetech/lime-web-components";
import { isNonNull } from "../../util/non-null";
/**
 * Get the limetypes that should be available for mentions
 *
 * @param limetypes all limetypes
 * @returns mention limetypes
 */
export function getMentionLimetypes(limetypes) {
    return Object.values(limetypes).filter((limetype) => {
        return (hasLabel(limetype, 'user') || hasHasManyRelation(limetype, 'user'));
    });
}
const MAX_MENTION_SEARCH_RESULTS = 10;
export class MentionsService {
    get limeObjectService() {
        return this.platform.get(PlatformServiceName.LimeObjectRepository);
    }
    get queryService() {
        return this.platform.get(PlatformServiceName.Query);
    }
    get viewFactoryRegistry() {
        return this.platform.get(PlatformServiceName.ViewFactoryRegistry);
    }
    get translator() {
        var _a;
        return (_a = this.platform) === null || _a === void 0 ? void 0 : _a.get(PlatformServiceName.Translate);
    }
    get triggerCharacter() {
        return this._triggerCharacter;
    }
    get triggerHandler() {
        return this._triggerHandler;
    }
    constructor(platform, context, searchableLimetypes) {
        this.platform = platform;
        this.context = context;
        this.searchableLimetypes = searchableLimetypes;
        this.groupCounts = {};
        this._triggerCharacter = '@';
        this._triggerHandler = {
            searcher: (query) => this.searcher(query),
            inserter: (triggerFunction, item) => this.inserter(triggerFunction, item),
            emptySearchMessage: 'Start typing a name...',
            noItemsFoundMessage: 'No results for your search...',
            nodeDefinition: {
                customElement: {
                    tagName: 'limebb-mention',
                    attributes: ['limetype', 'objectid', 'href'],
                },
                mapAttributes: (item) => ({
                    limetype: item.value.getLimetype().name,
                    objectid: item.value.id,
                    href: `object/${this.context.limetype}/${this.context.id}`,
                }),
            },
        };
        this.searcher = async (query) => {
            if (!query) {
                return [];
            }
            const { objects } = await this.limeObjectService.search(query, this.searchableLimetypes, MAX_MENTION_SEARCH_RESULTS);
            return this.createSearchListItems(objects);
        };
        this.inserter = async (triggerFunction, item) => {
            var _a;
            const nodeDefinition = this.triggerHandler.nodeDefinition;
            if (!nodeDefinition) {
                return;
            }
            triggerFunction.insert({
                node: {
                    tagName: nodeDefinition.customElement.tagName,
                    attributes: nodeDefinition.mapAttributes(item),
                },
                children: [(_a = item.value) === null || _a === void 0 ? void 0 : _a.descriptive],
            });
        };
    }
    async initialize() {
        await this.loadGroupCounts();
    }
    async loadGroupCounts() {
        const groupCountsLoads = this.searchableLimetypes
            .filter((limetype) => !hasLabel(limetype, 'user') &&
            hasHasManyRelation(limetype, 'user'))
            .map((limetype) => this.getGroupCounts(limetype));
        for (const groupCount of await Promise.all(groupCountsLoads)) {
            Object.assign(this.groupCounts, groupCount);
        }
    }
    async getGroupCounts(limetype) {
        var _a;
        const coworkerProperty = getRelationProperty(limetype, 'user');
        const limetypeName = limetype.name;
        try {
            const result = await this.queryService.execute({
                limetype: limetypeName,
                responseFormat: {
                    object: {
                        _id: null,
                        [coworkerProperty.name]: {
                            count: {
                                aggregate: {
                                    op: AggregateOperator.Count,
                                },
                            },
                        },
                    },
                },
            });
            if (!((_a = result.objects) === null || _a === void 0 ? void 0 : _a.length)) {
                return { [limetypeName]: {} };
            }
            const groupCount = this.createGroupCount(result.objects, coworkerProperty.name);
            return { [limetypeName]: groupCount };
        }
        catch (error) {
            console.error(`Error fetching group count for limetype: ${limetype.name}`, error);
            return { [limetypeName]: {} };
        }
    }
    createGroupCount(objects, coworkerPropertyName) {
        const groupCount = {};
        for (const object of objects) {
            const { _id: id, [coworkerPropertyName]: aggregateProperty } = object;
            groupCount[id] = aggregateProperty.count;
        }
        return groupCount;
    }
    createSearchListItems(searchObjects = []) {
        const items = searchObjects.map((object) => this.createSearchListItem(object));
        return items.filter(isNonNull);
    }
    createSearchListItem(object) {
        const createSearchItem = this.viewFactoryRegistry.getFactory('search');
        const limeobject = this.limeObjectService.getObject(object._limetype, object._id);
        if (!limeobject) {
            return null;
        }
        const searchListItem = createSearchItem(limeobject, this.context);
        const primaryComponent = this.getGroupCountComponent(limeobject);
        return Object.assign(Object.assign({}, searchListItem), (primaryComponent ? { primaryComponent: primaryComponent } : {}));
    }
    getGroupCountComponent(limeobject) {
        var _a;
        const limetype = limeobject.getLimetype();
        const groupCount = (_a = this.groupCounts[limetype.name]) === null || _a === void 0 ? void 0 : _a[limeobject.id];
        const coworkerProperty = getRelationProperty(limetype, 'user');
        if (groupCount === undefined || !coworkerProperty) {
            return;
        }
        const coworkerLimetype = coworkerProperty.relation.getLimetype();
        return {
            name: 'limebb-mention-group-counter',
            props: {
                count: groupCount,
                limetype: coworkerLimetype,
                helperLabel: this.translator.get('webclient.notification-center.members-will-be-notified'),
            },
        };
    }
}
