/*
 * ******************************************************************
 * Copyright (c) 2024 Broadcom. All Rights Reserved.
 * Broadcom Confidential. The term "Broadcom" refers to Broadcom Inc.
 * and/or its subsidiaries.
 * ******************************************************************
 */
import { Inject, Injectable } from '@angular/core';
import { ExtensionComponentRegistration, ExtensionComponentRegistry } from '../extensibility-framework/common/extensibility/extension-component.registry';
import { TOP_LEVEL_NAV_SCOPE, TOP_LEVEL_NAV_TYPE, TopLevelNavExtensionPointManifest } from '../extensibility-framework/common/extensibility/primary-navigation/primary-navigation.extension-point';
import { PluginUtilService } from "../extensibility-framework/common/extensibility/plugin-util.service";
import { CORE_UI_CUSTOM_EVENTS, PluginsMenuesEventDetails } from "../extensibility-framework/common/extensibility/extension-container/core-ui-events";
import { BehaviorSubject, catchError, map, Observable, of } from "rxjs";
import { TextBasedExtensionNavRegistration } from "../extensibility-framework/common/models/extension";
import { HYPER_PLUGIN_STATE, HyperPluginState, PLUGINS_MENUS } from "../extensibility-framework/common/extensibility/extensibility.model";
import { ServicesOverviewExtensionPointManifest } from "../extensibility-framework/common/extensibility/services-overview/services-overview.extension-point.interfaces";
import { SERVICES_OVERVIEW_COMPONENT_SCOPE, SERVICES_OVERVIEW_TYPE } from "../extensibility-framework/common/extensibility/services-overview/services-overview.extension-point.constants";
import { HttpClient } from "@angular/common/http";
import { BOOTSTRAP_DETAILS } from "../extensibility-framework/common/tokens";
import { BootstrapDetails } from "../extensibility-framework/common/models/bootstrap-details";
import { AuthTokenHolderService } from "../extensibility-framework/common/services/auth-token-holder.service";
import { SERVICES_VCF_SCOPE, SERVICES_VCF_TYPE } from "../extensibility-framework/common/extensibility/vcf-services/vcf-services.constants";
import { VCFServicesExtensionPointManifest } from "../extensibility-framework/common/extensibility/vcf-services/vcf-services.interfaces";

@Injectable()
export class ExtensionPointService {
    private legacyMenusSbj = new BehaviorSubject<TextBasedExtensionNavRegistration[]>([]);
    private legacyMenusObs = this.legacyMenusSbj.asObservable();
    private hyperPluginState: HyperPluginState = window[HYPER_PLUGIN_STATE];

    constructor(
        private extensionComponentRegistry: ExtensionComponentRegistry,
        private pluginUtilService: PluginUtilService,
        private http: HttpClient,
        @Inject(BOOTSTRAP_DETAILS) private BOOTSTRAP_DETAILS: BootstrapDetails,
        private authTokenHolderService: AuthTokenHolderService,
    ) {
        /**
         * Watch for legacy primart navigation events from UI plugins.
         * Each ui plugin emit different set of primary navigation menu items.
         * 
         * Legacy primary navigation menu items can not be sorted by priority,
         * there is no information which menu item from which UI plugin has arrived.
         * 
         * Legacy primary navigation menu items are always last in the list of menu items.
         */
        window.addEventListener(CORE_UI_CUSTOM_EVENTS.PLUGINS_MENUES as any, (e: CustomEvent<PluginsMenuesEventDetails>) => {
            this.hyperPluginState[PLUGINS_MENUS] = [...this.hyperPluginState[PLUGINS_MENUS], ...e.detail.pluginMenus as TextBasedExtensionNavRegistration[]];
            const plugins = this.extensionComponentRegistry.getRegisteredComponents(TOP_LEVEL_NAV_SCOPE);
            this.getExtensionPointPriority().pipe(
                map((data) => {
                    // If no priority list exists
                    if (!data || !data.extensionPointRegistrations || !data.extensionPointRegistrations[TOP_LEVEL_NAV_TYPE]) {
                        const menuItems = plugins.map((plugin) => {
                            const manifest = plugin.manifest as TopLevelNavExtensionPointManifest;
                            let iconPath = null;
                            if (manifest.icon) {
                                iconPath = `${plugin.resourcePath}/${manifest.icon}`;
                            }

                            const menuItem: TextBasedExtensionNavRegistration = {
                                icon: iconPath,
                                path: `${plugin.hashedVendor}/${plugin.vcdRoute}`,
                                nameText: this.pluginUtilService.translate(plugin.manifest.name, plugin.translations),
                                descriptionText: this.pluginUtilService.translate(plugin.manifest.description, plugin.translations),
                            };

                            return menuItem;
                        });
                        this.legacyMenusSbj.next([...menuItems, ...this.hyperPluginState[PLUGINS_MENUS]]);
                        return;
                    }

                    const sortedPlugins = this.sortPluginsByPriority(data.extensionPointRegistrations[TOP_LEVEL_NAV_TYPE], plugins);

                    const menuItems = sortedPlugins.map((plugin) => {
                        const manifest = plugin.manifest as TopLevelNavExtensionPointManifest;
                        let iconPath = null;
                        if (manifest.icon) {
                            iconPath = `${plugin.resourcePath}/${manifest.icon}`;
                        }

                        const menuItem: TextBasedExtensionNavRegistration = {
                            icon: iconPath,
                            path: `${plugin.hashedVendor}/${plugin.vcdRoute}`,
                            nameText: this.pluginUtilService.translate(plugin.manifest.name, plugin.translations),
                            descriptionText: this.pluginUtilService.translate(plugin.manifest.description, plugin.translations),
                        };

                        return menuItem;
                    });

                    this.legacyMenusSbj.next([...menuItems, ...this.hyperPluginState[PLUGINS_MENUS]]);
                }),
                catchError((error: unknown) => {
                    console.error(error);

                    const menuItems = plugins.map((plugin) => {
                        const manifest = plugin.manifest as TopLevelNavExtensionPointManifest;
                        let iconPath = null;
                        if (manifest.icon) {
                            iconPath = `${plugin.resourcePath}/${manifest.icon}`;
                        }

                        const menuItem: TextBasedExtensionNavRegistration = {
                            icon: iconPath,
                            path: `${plugin.hashedVendor}/${plugin.vcdRoute}`,
                            nameText: this.pluginUtilService.translate(plugin.manifest.name, plugin.translations),
                            descriptionText: this.pluginUtilService.translate(plugin.manifest.description, plugin.translations),
                        };

                        return menuItem;
                    });
                    this.legacyMenusSbj.next([...menuItems, ...this.hyperPluginState[PLUGINS_MENUS]]);

                    return of(error)
                })
            ).subscribe();
        });
    }

    getPrimaryNavigationMenuItems(): Observable<TextBasedExtensionNavRegistration[]> {
        const plugins = this.extensionComponentRegistry.getRegisteredComponents(TOP_LEVEL_NAV_SCOPE);

        this.getExtensionPointPriority().pipe(
            map((data) => {
                // If no priority list exists
                if (!data || !data.extensionPointRegistrations || !data.extensionPointRegistrations[TOP_LEVEL_NAV_TYPE]) {
                    const menuItems = plugins.map((plugin) => {
                        const manifest = plugin.manifest as TopLevelNavExtensionPointManifest;
                        let iconPath = null;
                        if (manifest.icon) {
                            iconPath = `${plugin.resourcePath}/${manifest.icon}`;
                        }

                        const menuItem: TextBasedExtensionNavRegistration = {
                            icon: iconPath,
                            path: `${plugin.hashedVendor}/${plugin.vcdRoute}`,
                            nameText: this.pluginUtilService.translate(plugin.manifest.name, plugin.translations),
                            descriptionText: this.pluginUtilService.translate(plugin.manifest.description, plugin.translations),
                        };

                        return menuItem;
                    });
                    this.legacyMenusSbj.next([...menuItems, ...this.hyperPluginState[PLUGINS_MENUS]]);
                    return;
                }

                const sortedPlugins = this.sortPluginsByPriority(data.extensionPointRegistrations[TOP_LEVEL_NAV_TYPE], plugins);
                const menuItems = sortedPlugins.map((plugin) => {
                    const manifest = plugin.manifest as TopLevelNavExtensionPointManifest;
                    let iconPath = null;
                    if (manifest.icon) {
                        iconPath = `${plugin.resourcePath}/${manifest.icon}`;
                    }

                    const menuItem: TextBasedExtensionNavRegistration = {
                        icon: iconPath,
                        path: `${plugin.hashedVendor}/${plugin.vcdRoute}`,
                        nameText: this.pluginUtilService.translate(plugin.manifest.name, plugin.translations),
                        descriptionText: this.pluginUtilService.translate(plugin.manifest.description, plugin.translations),
                    };

                    return menuItem;
                });
                this.legacyMenusSbj.next([...menuItems, ...this.hyperPluginState[PLUGINS_MENUS]]);
            }),
            catchError((error: unknown) => {
                console.error(error);

                const menuItems = plugins.map((plugin) => {
                    const manifest = plugin.manifest as TopLevelNavExtensionPointManifest;
                    let iconPath = null;
                    if (manifest.icon) {
                        iconPath = `${plugin.resourcePath}/${manifest.icon}`;
                    }

                    const menuItem: TextBasedExtensionNavRegistration = {
                        icon: iconPath,
                        path: `${plugin.hashedVendor}/${plugin.vcdRoute}`,
                        nameText: this.pluginUtilService.translate(plugin.manifest.name, plugin.translations),
                        descriptionText: this.pluginUtilService.translate(plugin.manifest.description, plugin.translations),
                    };

                    return menuItem;
                });
                this.legacyMenusSbj.next([...menuItems, ...this.hyperPluginState[PLUGINS_MENUS]]);

                return of(error)
            })
        ).subscribe();

        return this.legacyMenusObs;
    }

    getServicesOverviewMenuItems(): Observable<TextBasedExtensionNavRegistration[]> {
        const servicesOverviewPlugins = this.extensionComponentRegistry.getRegisteredComponents(SERVICES_OVERVIEW_COMPONENT_SCOPE);

        return this.getExtensionPointPriority().pipe(
            map((data) => {
                // If no priority list exists
                if (!data || !data.extensionPointRegistrations || !data.extensionPointRegistrations[SERVICES_OVERVIEW_TYPE]) {
                    const servicesOverviewMenuItems = servicesOverviewPlugins.map((plugin) => {
                        const result: TextBasedExtensionNavRegistration = {
                            icon: (plugin.manifest as ServicesOverviewExtensionPointManifest).iconShape,
                            path: `${plugin.hashedVendor}/${plugin.vcdRoute}`,
                            nameText: this.pluginUtilService.translate(plugin.manifest.name, plugin.translations),
                            descriptionText: this.pluginUtilService.translate(plugin.manifest.description, plugin.translations),
                        };

                        return result;
                    });
                    return servicesOverviewMenuItems;
                }

                const sortedPlugins = this.sortPluginsByPriority(data.extensionPointRegistrations[SERVICES_OVERVIEW_TYPE], servicesOverviewPlugins);
                const menuItems = sortedPlugins.map((plugin) => {
                    const result: TextBasedExtensionNavRegistration = {
                        icon: (plugin.manifest as ServicesOverviewExtensionPointManifest).iconShape,
                        path: `${plugin.hashedVendor}/${plugin.vcdRoute}`,
                        nameText: this.pluginUtilService.translate(plugin.manifest.name, plugin.translations),
                        descriptionText: this.pluginUtilService.translate(plugin.manifest.description, plugin.translations),
                    };

                    return result;
                });
                return menuItems;
            }),
            catchError((error: unknown) => {
                console.error(error);

                const menuItems = servicesOverviewPlugins.map((plugin) => {
                    const result: TextBasedExtensionNavRegistration = {
                        icon: (plugin.manifest as ServicesOverviewExtensionPointManifest).iconShape,
                        path: `${plugin.hashedVendor}/${plugin.vcdRoute}`,
                        nameText: this.pluginUtilService.translate(plugin.manifest.name, plugin.translations),
                        descriptionText: this.pluginUtilService.translate(plugin.manifest.description, plugin.translations),
                    };

                    return result;
                });

                return of(menuItems)
            })
        );
    }

    getVCFServicesMenuItems(): Observable<TextBasedExtensionNavRegistration[]> {
        const vcfServicesPlugins = this.extensionComponentRegistry.getRegisteredComponents(SERVICES_VCF_SCOPE);

        return this.getExtensionPointPriority().pipe(
            map((data) => {
                // If no priority list exists
                if (!data || !data.extensionPointRegistrations || !data.extensionPointRegistrations[SERVICES_VCF_TYPE]) {
                    const vcfServicesMenuItems = vcfServicesPlugins.map((plugin) => {
                        const result: TextBasedExtensionNavRegistration = {
                            icon: (plugin.manifest as VCFServicesExtensionPointManifest).iconShape,
                            path: `${plugin.hashedVendor}/${plugin.vcdRoute}`,
                            nameText: this.pluginUtilService.translate(plugin.manifest.name, plugin.translations),
                            descriptionText: this.pluginUtilService.translate(plugin.manifest.description, plugin.translations),
                        };

                        return result;
                    });
                    return vcfServicesMenuItems;
                }

                const sortedPlugins = this.sortPluginsByPriority(data.extensionPointRegistrations[SERVICES_VCF_TYPE], vcfServicesPlugins);
                const menuItems = sortedPlugins.map((plugin) => {
                    const result: TextBasedExtensionNavRegistration = {
                        icon: (plugin.manifest as VCFServicesExtensionPointManifest).iconShape,
                        path: `${plugin.hashedVendor}/${plugin.vcdRoute}`,
                        nameText: this.pluginUtilService.translate(plugin.manifest.name, plugin.translations),
                        descriptionText: this.pluginUtilService.translate(plugin.manifest.description, plugin.translations),
                    };

                    return result;
                });
                return menuItems;
            }),
            catchError((error: unknown) => {
                console.error(error);

                const menuItems = vcfServicesPlugins.map((plugin) => {
                    const result: TextBasedExtensionNavRegistration = {
                        icon: (plugin.manifest as VCFServicesExtensionPointManifest).iconShape,
                        path: `${plugin.hashedVendor}/${plugin.vcdRoute}`,
                        nameText: this.pluginUtilService.translate(plugin.manifest.name, plugin.translations),
                        descriptionText: this.pluginUtilService.translate(plugin.manifest.description, plugin.translations),
                    };

                    return result;
                });

                return of(menuItems)
            })
        );
    }

    private getExtensionPointPriority(): Observable<ExtensionPointSummary> {
        const API_VERSION = "ALPHA";
        const TENANT_CONTEXT_HEADERS: { [header: string]: string } = {};

        const X_VMWARE_VCLOUD_AUTH_CONTEXT: string = this.authTokenHolderService.authContext;
        const X_VMWARE_VCLOUD_TENANT_CONTEXT: string = this.BOOTSTRAP_DETAILS.cell.orgId;

        if (X_VMWARE_VCLOUD_AUTH_CONTEXT) {
            TENANT_CONTEXT_HEADERS['x-vmware-vcloud-auth-context'] = X_VMWARE_VCLOUD_AUTH_CONTEXT;
        }

        if (X_VMWARE_VCLOUD_TENANT_CONTEXT) {
            TENANT_CONTEXT_HEADERS['x-vmware-vcloud-tenant-context'] = X_VMWARE_VCLOUD_TENANT_CONTEXT;
        }

        return this.http.get<ExtensionPointSummary>(`${this.BOOTSTRAP_DETAILS.cell.address}/cloudapi/extensions/ui/extensionPoints`, {
            headers: {
                Accept: `application/json;version=${API_VERSION};`,
                Authorization: `Bearer ${this.authTokenHolderService.jwt}`,
                ...TENANT_CONTEXT_HEADERS,
            }
        }).pipe(
            catchError((error: unknown) => {
                console.error(error);

                return of({
                    extensionPointRegistrations: {}
                })
            })
        );
    }

    private sortPluginsByPriority(priority: ExtensionPoint[], plugins: ExtensionComponentRegistration[]): ExtensionComponentRegistration[] {
        const pluginIds = priority.filter((priority) => priority.enabled).map((priority) => priority.pluginId.replace("urn:vcloud:uiPlugin:", ""));
        // Create a map of the ids with their index positions from the first list
        const idOrderMap = new Map(pluginIds.map((item, index) => [item, index]));

        // Sort the second list based on the order from the first list
        return plugins.sort((a, b) => {
            const aPluginId = pluginIds.find((pluginId) => {
                return a.resourcePath.includes(pluginId);
            });
            const bPluginId = pluginIds.find((pluginId) => {
                return b.resourcePath.includes(pluginId);
            });

            const indexA = idOrderMap.get(aPluginId) ?? Infinity; // Default to Infinity if not found
            const indexB = idOrderMap.get(bPluginId) ?? Infinity; // Default to Infinity if not found
            return indexA - indexB;
        });
    }
}

interface ExtensionPointSummary {
    extensionPointRegistrations: {
        [extensionPointType: string]: ExtensionPoint[];
    }
}

interface ExtensionPoint {
    extensionPointId: string;
    pluginId: string;
    name: string;
    enabled: boolean;
}