/*
 * ******************************************************************
 * Copyright (c) 2024 Broadcom. All Rights Reserved.
 * Broadcom Confidential. The term "Broadcom" refers to Broadcom Inc.
 * and/or its subsidiaries.
 * ******************************************************************
 */

import { EventEmitter, Inject, Injectable, OnDestroy, Optional, Output } from "@angular/core";
import { MessageFormatTranslationService, TranslationService, TranslationSet } from "@vcd/i18n";
import { BehaviorSubject, Observable, of } from "rxjs";
import { EntityActionComponentContainer } from "../extension-components.directive";
import {
    CORE_UI_CUSTOM_EVENTS,
    GetExtensionItemsEventDetails,
    GetExtensionItemsReturnEventDetails,
} from "../extension-container/core-ui-events";
import { EntityActionExtensionMenuItemClickEvent } from "./entity-action-extension-menu.component";
import {
    EntityActionExtensionMenuEntry,
    EntityActionExtensionMenuItem,
    EntityActionExtensionSubmenu,
    HyperPluginEntityActionExtensionMenuEntryCollection,
} from "./entity-action-extension.component";
import { ActionItem } from "../vcd-common-components-actions";
import { BOOTSTRAP_DETAILS } from "../../tokens";
import { BootstrapDetails } from "../../models/bootstrap-details";

/**
 * To fetch {@link EntityActionExtensionMenuEntry} items asynchronously and map them to {@link ActionItem}. Injected in
 * to the components that want to display plugin actions
 */
@Injectable({
    providedIn: "root",
})
export class EntityActionExtensionService implements OnDestroy {
    /**
     * Emitted by action handler when an action item is clicked
     */
    @Output()
    menuItemClick = new EventEmitter<EntityActionExtensionMenuItemClickEvent>();

    private entityActionsPerHyperPlugin: {
        [entityUrn: string]: {
            [hyperPluginUrn: string]: ActionItem[],
        }
    } = {};

    private subject: Map<string, BehaviorSubject<ActionItem[]>> = new Map();

    constructor(
        @Optional() @Inject(BOOTSTRAP_DETAILS) private BOOTSTRAP_DETAILS: BootstrapDetails,
        private translationService: TranslationService
    ) {
        window.addEventListener(CORE_UI_CUSTOM_EVENTS.GET_EXTENSION_ACTION_ITEMS_RETURN, this.listener.bind(this));
    }

    ngOnDestroy(): void {
        window.removeEventListener(CORE_UI_CUSTOM_EVENTS.GET_EXTENSION_ACTION_ITEMS_RETURN, this.listener.bind(this));
    }

    /**
     * Called when the entity urn or {@link EntityActionComponentContainer}s change. Uses the
     * {@link EntityActionComponentContainer.getMenuEntry} to get menu entry and converts them into {@link ActionItem}
     * @param entityUrn Identifier of the entity for which extension actions have to be fetched
     * @param entityActionExtensionContainers List of wrapper objects containing the logic to fetch extension
     * action item, perform an action when the action item is clicked and also the set of translations required for
     * displaying the extension action text content
     */
    getExtensionActionItems(
        entityUrn: string,
        entityActionExtensionContainers: EntityActionComponentContainer[]
    ): Observable<ActionItem[]> {
        if (!entityUrn || !entityActionExtensionContainers?.length) {
            return of([]);
        }

        this.subject.set(entityUrn, new BehaviorSubject<ActionItem[]>([]));

        window.dispatchEvent(
            new CustomEvent<GetExtensionItemsEventDetails>(CORE_UI_CUSTOM_EVENTS.GET_EXTENSION_ACTION_ITEMS, {
                detail: {
                    entityUrn,
                    entityActionExtensionContainers,
                },
            })
        );

        return this.subject.get(entityUrn).asObservable()
    }

    private listener(event: CustomEvent<GetExtensionItemsReturnEventDetails>): void {
        const entityActionExtensionMenuEntries: HyperPluginEntityActionExtensionMenuEntryCollection[] =
            event.detail.entityActionExtensionMenuEntries;

        entityActionExtensionMenuEntries.forEach((data) => {
            if (!this.entityActionsPerHyperPlugin[data.entityUrn]) {
                this.entityActionsPerHyperPlugin[data.entityUrn] = {};
            }
            this.entityActionsPerHyperPlugin[data.entityUrn][data.component.hyperPluginUrn] = [];
        });

        entityActionExtensionMenuEntries.forEach(
            (data: HyperPluginEntityActionExtensionMenuEntryCollection) => {
                let actionItem: ActionItem;
                if ((data.menuEntry as EntityActionExtensionSubmenu).children?.length) {
                    const subMenu = {
                        ...data.menuEntry,
                        children: [...(data.menuEntry as EntityActionExtensionSubmenu).children],
                    };
                    actionItem = {
                        textKey: this.translate(subMenu.text, data.component.translations),
                        children: subMenu.children.map((menuItem) =>
                            this.getExtensionActionItem(menuItem, data.component, data.entityUrn, event.detail.scope)
                        ),
                        isTranslatable: false,
                    };
                } else {
                    actionItem = this.getExtensionActionItem(
                        data.menuEntry as EntityActionExtensionMenuItem,
                        data.component,
                        data.entityUrn,
                        event.detail.scope
                    );
                }

                this.entityActionsPerHyperPlugin[data.entityUrn][data.component.hyperPluginUrn].push(actionItem);

                return actionItem;
            }
        );

        Object.keys(this.entityActionsPerHyperPlugin).forEach((entityUrn: string) => {
            const actionsToEmit: ActionItem[] = [];

            Object.keys(this.entityActionsPerHyperPlugin[entityUrn]).forEach((hyperPluginUrn: string) => {
                this.entityActionsPerHyperPlugin[entityUrn][hyperPluginUrn].forEach((action: ActionItem) => {
                    actionsToEmit.push(action);
                });
            });

            this.subject.get(entityUrn).next(actionsToEmit);
        });
    }

    private getExtensionActionItem(
        menuItem: EntityActionExtensionMenuItem,
        component: EntityActionComponentContainer,
        entityUrn: string,
        scope: string,
    ): ActionItem {
        return {
            busy: menuItem.busy,
            textKey: this.translate(menuItem.text, component.translations),
            disabled: () => !menuItem.enabled,
            isTranslatable: false,
            handler: () => {
                this.menuItemClick.emit({
                    component: component.componentInstance,
                    menuItemUrn: menuItem.urn,
                    entityUrn: entityUrn,
                    scope: scope,
                });
            },
        };
    }

    private translate(text: string, translations: TranslationSet): string {
        if (!this.isExtensionTranslationPlaceholder(text)) {
            return text;
        }

        const translationString = text.split("%").filter(Boolean)[0];

        if (translations) {
            let locale = "en";
            if (this.BOOTSTRAP_DETAILS) {
                locale = this.BOOTSTRAP_DETAILS.locale;
            }
            const translationService = new MessageFormatTranslationService(locale, "en");
            translationService.registerTranslations(translations);
            return translationService.translate(translationString);
        }

        return this.translationService.translate(translationString);
    }

    private isExtensionTranslationPlaceholder(text: string): boolean {
        return text.startsWith("%") && text.endsWith("%");
    }
}
