import { h, Host, } from "@stencil/core";
import { PlatformServiceName, } from "@limetech/lime-web-components";
import { differenceInMinutes } from "date-fns/differenceInMinutes";
import { HighlightFeedItemHandler } from "./highlight-item/highlight-item.handler";
const MINUTES_OF_PROXIMITY = 5;
const ITEM_HIGHLIGHT_DURATION = 3000;
/**
 * Feed
 * Feed is a timeline that displays its children (feed items) in a chronological order.
 *
 * The feed automatically groups items into days, and for each group,
 * it shows the dates as a heading.
 *
 * Also, when a user or author creates several feed items in a close timespan,
 * the feed automatically bundles these items together, removing some repetitive and
 * unnecessary visual details of the bundled posts, from the timeline.
 *
 * @exampleComponent limebb-example-feed-basic
 * @exampleComponent limebb-example-feed-author
 * @exampleComponent limebb-example-feed-author-picture
 * @exampleComponent limebb-example-feed-item-heading
 * @exampleComponent limebb-example-feed-item-long-heading
 * @exampleComponent limebb-example-feed-unpromoted-actions
 * @exampleComponent limebb-example-feed-promoted-actions
 * @exampleComponent limebb-example-feed-relations
 * @exampleComponent limebb-example-feed-tall-item
 * @exampleComponent limebb-example-feed-helper-text
 * @exampleComponent limebb-example-feed-thumbnails
 * @exampleComponent limebb-example-feed-load-more
 * @exampleComponent limebb-example-feed-reverse-sorting
 * @exampleComponent limebb-example-feed-highlight-an-item
 * @exampleComponent limebb-example-feed-error
 * @exampleComponent limebb-example-feed-item-types
 * @exampleComponent limebb-example-feed-item-color-coding
 * @exampleComponent limebb-example-feed-new-items
 * @beta
 */
export class Feed {
    constructor() {
        /**
         * List of items to display in the feed.
         */
        this.items = [];
        /**
         * Number of minutes to take into account, when bundling several
         * feed-items that are created by the same author in a close timespan.
         */
        this.minutesOfProximity = MINUTES_OF_PROXIMITY;
        /**
         * The total number of items available
         */
        this.totalCount = 0;
        /**
         * Defines the chronological sorting of the array of items which are
         * provided to the feed. It is used for visual grouping & bundling logic,
         * as well as rendering the last visited timestamp in the right place.
         * - 'ASC'  : Items are provided oldest -> newest (newest last).
         * Often used when the UI shows newer items near the bottom and older ones above.
         * - 'DESC' : Items are provided newest -> oldest (newest first).
         * Often used when the UI places the latest item at the top.
         *
         * :::important
         * This component does not re-sort the provided `items`.
         * It only renders them in the order received from the consumer.
         * :::
         */
        this.direction = 'ASC';
        this.isBundled = (item, index) => {
            var _a, _b, _c, _d, _e, _f;
            if (index === 0) {
                return false;
            }
            const previousAuthorId = (_b = (_a = this.items[index - 1]) === null || _a === void 0 ? void 0 : _a.author) === null || _b === void 0 ? void 0 : _b.id;
            const currentAuthorId = (_c = item === null || item === void 0 ? void 0 : item.author) === null || _c === void 0 ? void 0 : _c.id;
            if (previousAuthorId !== currentAuthorId) {
                return false;
            }
            const previousItemIcon = (_e = (_d = this.items[index - 1]) === null || _d === void 0 ? void 0 : _d.icon) === null || _e === void 0 ? void 0 : _e.name;
            const currentItemIcon = (_f = item === null || item === void 0 ? void 0 : item.icon) === null || _f === void 0 ? void 0 : _f.name;
            if (previousItemIcon !== currentItemIcon) {
                return false;
            }
            const currentIndexDate = new Date(item.timestamp);
            const previousIndexDate = new Date(this.items[index - 1].timestamp);
            const previousItemDate = new Date(previousIndexDate);
            const currentItemDate = new Date(currentIndexDate);
            if (this.direction === 'DESC') {
                return (differenceInMinutes(previousItemDate, currentItemDate) <=
                    this.minutesOfProximity);
            }
            return (differenceInMinutes(currentItemDate, previousItemDate) <=
                this.minutesOfProximity);
        };
        this.handleLoadMore = (event) => {
            event.stopPropagation();
            this.loadMore.emit();
        };
    }
    connectedCallback() {
        this.highlighter.registerFeed(this.feedItemElement);
    }
    disconnectedCallback() {
        this.highlighter.unregisterFeed(this.feedItemElement);
    }
    render() {
        return (h(Host, { key: '5d1c5d6ff9949229a0bafbdb060a0bc386a8eb03', role: "log", "aria-busy": this.loading ? 'true' : 'false', style: {
                '--limebb-feed-highlight-duration': `${ITEM_HIGHLIGHT_DURATION}ms`,
            } }, this.renderLoadMore(), this.renderEmptyFeedMessage(), this.renderSpinner(), this.renderFeedItems()));
    }
    renderSpinner() {
        if (this.loading) {
            return h("limel-spinner", { size: "small" });
        }
    }
    renderEmptyFeedMessage() {
        if (this.loading) {
            return;
        }
        if (this.items.length === 0) {
            return (h("limebb-empty-state", { heading: this.heading, value: this.emptyStateMessage, icon: {
                    name: 'typewriter_with_paper',
                    color: 'rgb(var(--contrast-800))',
                } }));
        }
    }
    renderFeedItems() {
        return this.groupedItems.map((group) => (h("section", { class: "date-group", key: `date-group-${group.date}` }, h("a", { class: "date-heading" }, group.date), group.items.map(({ item, index }) => (h("limebb-feed-timeline-item", { platform: this.platform, context: this.context, item: item, key: item.id, ui: item === null || item === void 0 ? void 0 : item.ui, helperText: item === null || item === void 0 ? void 0 : item.helperText, hasError: item === null || item === void 0 ? void 0 : item.hasError, isBundled: this.isBundled(item, index), class: this.isNewItem(item) ? 'is-new' : undefined }))))));
    }
    isNewItem(item) {
        if (!this.lastVisitedTimestamp) {
            return false;
        }
        const lastVisited = new Date(this.lastVisitedTimestamp);
        const itemTime = new Date(item.timestamp);
        return itemTime > lastVisited;
    }
    renderLoadMore() {
        if (this.items.length === 0 || this.totalCount <= this.items.length) {
            return;
        }
        return (h("limel-icon-button", { class: "load-more-button", icon: "more", onClick: this.handleLoadMore, elevated: true, label: this.loadMoreLabel }));
    }
    get dateTimeFormatter() {
        return this.platform.get(PlatformServiceName.DateTimeFormatter);
    }
    get loadMoreLabel() {
        return this.translator.get('webclient.load-more');
    }
    get translator() {
        return this.platform.get(PlatformServiceName.Translate);
    }
    /**
     * Groups feed items by calendar date (formatted using date formatter 'date').
     * Returns an array of groups each containing the display date string and
     * the original items with their indices.
     */
    get groupedItems() {
        const groups = [];
        for (let index = 0; index < this.items.length; index++) {
            const item = this.items[index];
            const dateKey = this.dateTimeFormatter.format(item.timestamp, 'date');
            const lastGroup = groups.at(-1);
            if (!lastGroup || lastGroup.date !== dateKey) {
                groups.push({ date: dateKey, items: [{ item, index }] });
            }
            else {
                lastGroup.items.push({ item, index });
            }
        }
        return groups;
    }
    highlightedItemIdChanged(newValue) {
        if (newValue !== undefined) {
            this.highlightItem(newValue);
        }
    }
    highlightItem(id) {
        var _a;
        const itemElement = (_a = this.feedItemElement.shadowRoot) === null || _a === void 0 ? void 0 : _a.querySelector(`limebb-feed-timeline-item[id="${id}"]`);
        if (itemElement) {
            itemElement.setAttribute('aria-current', 'true');
            itemElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
            itemElement.focus();
            setTimeout(() => {
                itemElement.removeAttribute('aria-current');
            }, ITEM_HIGHLIGHT_DURATION);
            return true;
        }
        this.highlightedItemIdNotFound.emit({ id: id });
        return false;
    }
    get highlighter() {
        return this.platform.get(HighlightFeedItemHandler.SERVICE_NAME);
    }
    static get is() { return "limebb-feed"; }
    static get encapsulation() { return "shadow"; }
    static get originalStyleUrls() {
        return {
            "$": ["feed.scss"]
        };
    }
    static get styleUrls() {
        return {
            "$": ["feed.css"]
        };
    }
    static get properties() {
        return {
            "platform": {
                "type": "unknown",
                "mutable": false,
                "complexType": {
                    "original": "LimeWebComponentPlatform",
                    "resolved": "LimeWebComponentPlatform",
                    "references": {
                        "LimeWebComponentPlatform": {
                            "location": "import",
                            "path": "@limetech/lime-web-components",
                            "id": "node_modules::LimeWebComponentPlatform",
                            "referenceLocation": "LimeWebComponentPlatform"
                        }
                    }
                },
                "required": true,
                "optional": false,
                "docs": {
                    "tags": [{
                            "name": "inheritdoc",
                            "text": undefined
                        }],
                    "text": "Reference to the platform service container.\n\nUse this to access all platform services like repositories, HTTP client,\ncommand bus, navigator, and more. The platform instance is shared across\nall components in the same application context."
                },
                "getter": false,
                "setter": false
            },
            "context": {
                "type": "unknown",
                "mutable": false,
                "complexType": {
                    "original": "LimeWebComponentContext",
                    "resolved": "LimeWebComponentContext",
                    "references": {
                        "LimeWebComponentContext": {
                            "location": "import",
                            "path": "@limetech/lime-web-components",
                            "id": "node_modules::LimeWebComponentContext",
                            "referenceLocation": "LimeWebComponentContext"
                        }
                    }
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [{
                            "name": "inheritdoc",
                            "text": undefined
                        }],
                    "text": "The context describing where this component is running.\n\nThe context provides information about the current limetype and record ID\n(if viewing/editing a specific record). Components can use this to load\nrelevant data and understand their operating environment."
                },
                "getter": false,
                "setter": false
            },
            "items": {
                "type": "unknown",
                "mutable": false,
                "complexType": {
                    "original": "FeedItem[]",
                    "resolved": "FeedItem[]",
                    "references": {
                        "FeedItem": {
                            "location": "import",
                            "path": "./feed-item/feed-item.types",
                            "id": "src/components/feed/feed-item/feed-item.types.ts::FeedItem",
                            "referenceLocation": "FeedItem"
                        }
                    }
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "List of items to display in the feed."
                },
                "getter": false,
                "setter": false,
                "defaultValue": "[]"
            },
            "emptyStateMessage": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "The message shown when there are no items in the feed."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "empty-state-message"
            },
            "heading": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string",
                    "resolved": "string",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "The heading of the empty state message."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "heading"
            },
            "loading": {
                "type": "boolean",
                "mutable": false,
                "complexType": {
                    "original": "boolean",
                    "resolved": "boolean",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "True if items have not finished loading."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "loading"
            },
            "minutesOfProximity": {
                "type": "number",
                "mutable": false,
                "complexType": {
                    "original": "number",
                    "resolved": "number",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Number of minutes to take into account, when bundling several\nfeed-items that are created by the same author in a close timespan."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "minutes-of-proximity",
                "defaultValue": "MINUTES_OF_PROXIMITY"
            },
            "totalCount": {
                "type": "number",
                "mutable": false,
                "complexType": {
                    "original": "number",
                    "resolved": "number",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "The total number of items available"
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "total-count",
                "defaultValue": "0"
            },
            "direction": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "'ASC' | 'DESC'",
                    "resolved": "\"ASC\" | \"DESC\"",
                    "references": {}
                },
                "required": false,
                "optional": false,
                "docs": {
                    "tags": [],
                    "text": "Defines the chronological sorting of the array of items which are\nprovided to the feed. It is used for visual grouping & bundling logic,\nas well as rendering the last visited timestamp in the right place.\n- 'ASC'  : Items are provided oldest -> newest (newest last).\nOften used when the UI shows newer items near the bottom and older ones above.\n- 'DESC' : Items are provided newest -> oldest (newest first).\nOften used when the UI places the latest item at the top.\n\n:::important\nThis component does not re-sort the provided `items`.\nIt only renders them in the order received from the consumer.\n:::"
                },
                "getter": false,
                "setter": false,
                "reflect": true,
                "attribute": "direction",
                "defaultValue": "'ASC'"
            },
            "lastVisitedTimestamp": {
                "type": "string",
                "mutable": false,
                "complexType": {
                    "original": "string | Date | null",
                    "resolved": "Date | null | string | undefined",
                    "references": {
                        "Date": {
                            "location": "global",
                            "id": "global::Date"
                        }
                    }
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "The date and time of the last time the user visited the feed.\nWhen provided, the feed will display a divider line between the\nlast visited items (the old items which have a date and time earlier than\n`lastVisitedTimestamp`) and the newer items.\n\nIf there are no newer items, the divider line will not be displayed."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "last-visited-timestamp"
            },
            "highlightedItemId": {
                "type": "any",
                "mutable": false,
                "complexType": {
                    "original": "string | number",
                    "resolved": "number | string | undefined",
                    "references": {}
                },
                "required": false,
                "optional": true,
                "docs": {
                    "tags": [],
                    "text": "The ID of the item to highlight. When this value changes,\nthe feed will attempt to find and highlight the specified item.\nIf the item is not found, the `highlightedItemIdNotFound` event will be emitted."
                },
                "getter": false,
                "setter": false,
                "reflect": false,
                "attribute": "highlighted-item-id"
            }
        };
    }
    static get events() {
        return [{
                "method": "loadMore",
                "name": "loadMore",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when the load more button is clicked"
                },
                "complexType": {
                    "original": "void",
                    "resolved": "void",
                    "references": {}
                }
            }, {
                "method": "highlightedItemIdNotFound",
                "name": "highlightedItemIdNotFound",
                "bubbles": true,
                "cancelable": true,
                "composed": true,
                "docs": {
                    "tags": [],
                    "text": "Emitted when an item with requested `id` to highlight\nis not found in the current set of loaded items"
                },
                "complexType": {
                    "original": "{ id: string | number }",
                    "resolved": "{ id: string | number; }",
                    "references": {}
                }
            }];
    }
    static get elementRef() { return "feedItemElement"; }
    static get watchers() {
        return [{
                "propName": "highlightedItemId",
                "methodName": "highlightedItemIdChanged"
            }];
    }
}
