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

import { InjectionToken, Injector, ModuleWithProviders, NgModule } from "@angular/core";
import { ExtensibilityModule, extensionComponentRegistryFactory } from "./extensibility-framework/common/extensibility/extensibility.module";
import { ResourceModule } from "./extensibility-framework/extensions/vdcs/resources/resource.module";
import { PluginsManagerService } from "./extensibility-framework/common/extensibility/plugins-manager/plugins-manager.service";
import { ExternalExtensionLoader } from "./extensibility-framework/common/extensibility/loader/external/external-extension.loader";
import { ALL_EXTENSIONS_LOADED, CONFIG, ExtensionSpec, HAS_EXTENSIONS, HYPER_PLUGIN_STATE, HyperPluginState, PLUGINS_MENUS, REGISTERED_EXTENSION_POINTS, UI_PLUGINS_MANIFEST_CACHE } from "./extensibility-framework/common/extensibility/extensibility.model";
import { I18nModule } from "@vcd/i18n";
import { HyperPluginRoutesStorage } from "./hyper-plugin-routes-storage.service";
import { HYPER_PLUGIN_CONFIG, HyperpluginConfig } from "./lib-interfaces";
import { RouteConfigLoadEnd, Router } from "@angular/router";
import { filter } from "rxjs";
import { CORE_UI_CUSTOM_EVENTS, ForceCoreUIRouterNavigationEventDetails } from "./extensibility-framework/common/extensibility/extension-container/core-ui-events";
import { ExtensionPointService } from "./consumer/extension-points.service";
import { PluginUtilService } from "./extensibility-framework/common/extensibility/plugin-util.service";
import { AngularAppsExtensionPointHandler } from "./extensibility-framework/common/extensibility/angular-apps/angular-apps.extension-point";
import { ApplicationsExtensionPointHandler } from "./extensibility-framework/common/extensibility/applications/applications.extension-point";
import { CreateIpBindingsExtensionPointHandler } from "./extensibility-framework/common/extensibility/create-ip-bindings/create-ip-bindings.extension-point";
import { CreateOrgExtensionPointHandler } from "./extensibility-framework/common/extensibility/create-org/create-org.extension-point";
import { CreateVAppExtensionPointHandler } from "./extensibility-framework/common/extensibility/create-vapp/create-vapp.extension-point";
import { CreateVMExtensionPointHandler } from "./extensibility-framework/common/extensibility/create-vm/create-vm.extension-point";
import { DatacenterOverviewExtensionPointHandler } from "./extensibility-framework/common/extensibility/datacenter-overview/datacenter-overview.extension-point";
import { DetailsContainerExtensionPointHandler } from "./extensibility-framework/common/extensibility/details-container/details-container.extension-point";
import { EntityActionExtensionPointHandler } from "./extensibility-framework/common/extensibility/entity-action/entity-action.extension-point";
import { ExtensionComponentRegistry } from "./extensibility-framework/common/extensibility/extension-component.registry";
import { ExtensionPointOrchcestrator } from "./extensibility-framework/common/extensibility/extension-point-orchestrator/extension-point.orchestrator";
import { ExtensionPointRoutingHandler } from "./extensibility-framework/common/extensibility/extension-point-routing.handler";
import { BootstrapExtensionPointsStep } from "./extensibility-framework/common/extensibility/loader/external/bootstrap-extension-points.step";
import { CreateExternalExtensionRouteStep } from "./extensibility-framework/common/extensibility/loader/external/create-external-extension-route.step";
import { LoadExternalExtensionBundleStep } from "./extensibility-framework/common/extensibility/loader/external/load-external-extension-bundle.step";
import { LoadExternalExtensionI18nStep } from "./extensibility-framework/common/extensibility/loader/external/load-external-extension-i18n.step";
import { LoadExternalExtensionManifestStep } from "./extensibility-framework/common/extensibility/loader/external/load-external-extension-manifest.step";
import { RegisterExternalExtensionRoutesStep } from "./extensibility-framework/common/extensibility/loader/external/register-external-extension-routes.step";
import { RegisterExternalExtensionTranslationsStep } from "./extensibility-framework/common/extensibility/loader/external/register-external-extension-translations.step";
import { NetworkDetailsExtensionPointHandler } from "./extensibility-framework/common/extensibility/network-details/network-details.extension-point";
import { NetworkingExtensionPointHandler } from "./extensibility-framework/common/extensibility/networking/networking.extension-point";
import { TopLevelNavExtensionPointHandler } from "./extensibility-framework/common/extensibility/primary-navigation/primary-navigation.extension-point";
import { BOOTSTRAP_DETAILS, EXTENSION_POINT_HANDLER } from "./extensibility-framework/common/tokens";
import { BootstrapDetails } from "./extensibility-framework/common/models/bootstrap-details";
import { ServicesOverviewrExtensionPointHandler } from "./extensibility-framework/common/extensibility/services-overview/services-overview.extension-point";
import { AuthTokenHolderService } from "./extensibility-framework/common/services/auth-token-holder.service";
import { VcdAuthTokenHolderService } from "./extensibility-framework/vcd-auth-token-holder.service";
import { VCFServicesExtensionPointHandler } from "./extensibility-framework/common/extensibility/vcf-services/vcf-services.extension-point";
import { GeneralExtensibilityService } from "./consumer/general-extensibility.service";

export const HYPER_PLUGIN = new InjectionToken("HYPER_PLUGIN");

/**
 * This bootstrap method loads all modules. It is intended to be used as an APP_INITIALIZER to prevent bootstrap
 * until the modules are loaded.
 */
export function bootstrapPlugins(injector: Injector): Promise<boolean> {
	return new Promise<boolean>((resolve, reject) => {
    
    const bootstrapDetails: BootstrapDetails = injector.get<BootstrapDetails>(BOOTSTRAP_DETAILS, null, { optional: true });

    if (!bootstrapDetails) {
        return Promise.resolve(true);
    }

    let extensionSpecs: ExtensionSpec[] = Object.keys(bootstrapDetails.thirdPartyExtensions).map((id) => ({
        id,
        resourcePath: bootstrapDetails.thirdPartyExtensions[id],
    }));

    const externalExtensionLoader = injector.get(ExternalExtensionLoader);
    return externalExtensionLoader
      .loadExternalExtension(extensionSpecs)
      .toPromise()
      .then(() => resolve(true));
  });
}

@NgModule({
    imports: [
        I18nModule.forRoot(),
        ExtensibilityModule,
        ResourceModule,
    ],
    exports: [
        ExtensibilityModule,
        ResourceModule,
    ],
    providers: [
        {
            provide: ExtensionComponentRegistry,
            useFactory: extensionComponentRegistryFactory,
        },
        ExtensionPointService,
        PluginUtilService,
        HyperPluginRoutesStorage,
        ExtensionPointRoutingHandler,
        {
            provide: AuthTokenHolderService,
            useClass: VcdAuthTokenHolderService
        },
        GeneralExtensibilityService,
    ]
})
export class HyperPluginChildModule {
    constructor(
        private hyperPluginRoutesStorage: HyperPluginRoutesStorage,
        private router: Router,
    ) {
        this.router.events.pipe(
            filter((x) => x instanceof RouteConfigLoadEnd)
        ).subscribe((event: RouteConfigLoadEnd) => {
            this.hyperPluginRoutesStorage.checkRouterEvent(event);
        });
    }
    static forChild(): ModuleWithProviders<HyperPluginChildModule> {
        return {
            ngModule: HyperPluginChildModule,
            providers: [
                {
                    provide: HYPER_PLUGIN_CONFIG,
                    useFactory: () => {
                        return window[HYPER_PLUGIN_STATE][CONFIG];
                    },
                },
            ]
        }
    }
}

@NgModule({
    imports: [
        I18nModule.forRoot(),
        ExtensibilityModule,
        ResourceModule,
    ],
    exports: [
        ExtensibilityModule,
        ResourceModule,
    ],
})
export class HyperPluginModule {
    constructor(
        private hyperPluginRoutesStorage: HyperPluginRoutesStorage,
        private router: Router,
        private injector: Injector,
    ) {
        // TODO: Call automatically when bootstrap details are available?
        // this.injector.get(HYPER_PLUGIN);
        this.router.events.pipe(
            filter((x) => x instanceof RouteConfigLoadEnd)
        ).subscribe((event: RouteConfigLoadEnd) => {
            this.hyperPluginRoutesStorage.checkRouterEvent(event);
        });

        window.addEventListener(
            CORE_UI_CUSTOM_EVENTS.FORCE_CORE_UI_ROUTER_NAVIGATION,
            (event: CustomEvent<ForceCoreUIRouterNavigationEventDetails>) => {
                this.router.navigateByUrl(event.detail.url);
            }
        );
    }

    static forChild() {
        return HyperPluginChildModule.forChild();
    }

    static forRoot(config: HyperpluginConfig): ModuleWithProviders<HyperPluginModule> {
        let hyperPluginState: HyperPluginState = window[HYPER_PLUGIN_STATE];
        if (!hyperPluginState) {
            hyperPluginState = window[HYPER_PLUGIN_STATE] = {
                [UI_PLUGINS_MANIFEST_CACHE]: {},
                [REGISTERED_EXTENSION_POINTS]: [],
                [PLUGINS_MENUS]: [],
                [HAS_EXTENSIONS]: null,
                [ALL_EXTENSIONS_LOADED]: false,
                [CONFIG]: config,
            }
        }

        return {
            ngModule: HyperPluginModule,
            providers: [
                {
                    provide: HYPER_PLUGIN_CONFIG,
                    useValue: config,
                },
                {
                    provide: HYPER_PLUGIN,
                    useFactory: bootstrapPlugins,
                    deps: [Injector, ExtensionPointService, BOOTSTRAP_DETAILS],
                },
                {
                    provide: HyperPluginRoutesStorage,
                    useClass: HyperPluginRoutesStorage,
                    deps: [HYPER_PLUGIN_CONFIG]
                },
                CreateExternalExtensionRouteStep,
                LoadExternalExtensionBundleStep,
                LoadExternalExtensionI18nStep,
                LoadExternalExtensionManifestStep,
                RegisterExternalExtensionRoutesStep,
                RegisterExternalExtensionTranslationsStep,
                BootstrapExtensionPointsStep,
                ExternalExtensionLoader,
                ExtensionPointRoutingHandler,
                {
                    provide: ExtensionComponentRegistry,
                    useFactory: extensionComponentRegistryFactory,
                },
                {
                    provide: EXTENSION_POINT_HANDLER,
                    multi: true,
                    useClass: DatacenterOverviewExtensionPointHandler,
                },
                {
                    provide: EXTENSION_POINT_HANDLER,
                    multi: true,
                    useClass: ApplicationsExtensionPointHandler,
                },
                {
                    provide: EXTENSION_POINT_HANDLER,
                    multi: true,
                    useClass: EntityActionExtensionPointHandler,
                },
                {
                    provide: EXTENSION_POINT_HANDLER,
                    multi: true,
                    useClass: DetailsContainerExtensionPointHandler,
                },
                {
                    provide: EXTENSION_POINT_HANDLER,
                    multi: true,
                    useClass: TopLevelNavExtensionPointHandler,
                },
                {
                    provide: EXTENSION_POINT_HANDLER,
                    multi: true,
                    useClass: CreateVMExtensionPointHandler,
                },
                {
                    provide: EXTENSION_POINT_HANDLER,
                    multi: true,
                    useClass: CreateVAppExtensionPointHandler,
                },
                {
                    provide: EXTENSION_POINT_HANDLER,
                    multi: true,
                    useClass: AngularAppsExtensionPointHandler,
                },
                {
                    provide: EXTENSION_POINT_HANDLER,
                    multi: true,
                    useClass: CreateOrgExtensionPointHandler,
                },
                {
                    provide: EXTENSION_POINT_HANDLER,
                    multi: true,
                    useClass: NetworkingExtensionPointHandler,
                },
                {
                    provide: EXTENSION_POINT_HANDLER,
                    multi: true,
                    useClass: NetworkDetailsExtensionPointHandler,
                },
                {
                    provide: EXTENSION_POINT_HANDLER,
                    multi: true,
                    useClass: CreateIpBindingsExtensionPointHandler,
                },
                {
                    provide: EXTENSION_POINT_HANDLER,
                    multi: true,
                    useClass: ServicesOverviewrExtensionPointHandler,
                },
                {
                    provide: EXTENSION_POINT_HANDLER,
                    multi: true,
                    useClass: VCFServicesExtensionPointHandler,
                },
                {
                    provide: AuthTokenHolderService,
                    useClass: VcdAuthTokenHolderService
                },
                ExtensionPointOrchcestrator,
                PluginsManagerService,
                ExtensionPointService,
                PluginUtilService,
                GeneralExtensibilityService,
            ],
        }
    }
}