/*
 * Copyright 2020-2025 VMware, Inc. All rights reserved. VMware Confidential
 */

import { Inject, Injectable, NgModuleRef } from "@angular/core";
import { Route, Router } from "@angular/router";
import { COMPONENT_SCOPE_PREFIX } from "../../../../lib-interfaces";
import { ExtensionPointManifest } from "../extensibility.model";
import { ExtensionComponentRegistration, ExtensionComponentRegistry } from "../extension-component.registry";
import { ExtensionContainerGuard } from "../extension-container/extension-container-guard";
import { ExtensionContainerComponent } from "../extension-container/extension-container.component";
import { ExtensionPointHandler } from "../extension-point.handler";
import { ExternalExtensionLoaderState } from "../loader/extension-loader.model";
import { getHashedString } from "../loader/external/create-external-extension-route.step";
import { HYPER_PLUGIN_CONFIG, HyperpluginConfig } from "../../../../lib-interfaces";
import { ExtensionPointRoutingHandler } from "../extension-point-routing.handler";

/**
 * This scope is intended to be injected in the main navigation
 */
export const TOP_LEVEL_NAV_SCOPE = `${COMPONENT_SCOPE_PREFIX}:primary-navigation`;

/**
 * Top level nav manifest type (constant value)
 */
export const TOP_LEVEL_NAV_TYPE = "navigation:primary";

/**
 * Top level nav manifest type (type value)
 */
export type TOP_LEVEL_NAV_TYPE = "navigation:primary";

/**
 * The manifest versions this is supported by.
 */
export const SUPPORTED_VERSIONS: string[] = ["9.5.0"];

/**
 * Top level nav extension point manifests have the type of "primary-navigation".
 * They declare a single component, which should be injected onto main navigation.
 */
export interface TopLevelNavExtensionPointManifest extends ExtensionPointManifest {
    readonly type: TOP_LEVEL_NAV_TYPE;
    readonly module: string;
    readonly icon?: string;
}

/**
 * This extension point handler supports extension manifests of version 9.1.0 and of type
 * "primary-navigation". It will register the plugin modules directly in the main navigation
 * via the plugins manager service.
 */
@Injectable()
export class TopLevelNavExtensionPointHandler implements ExtensionPointHandler {
    constructor(
        @Inject(HYPER_PLUGIN_CONFIG) private HYPER_PLUGIN_CONFIG: HyperpluginConfig,
        private extensionPointRoutingHandler: ExtensionPointRoutingHandler,
        private extensionComponentRegistry: ExtensionComponentRegistry,
        private router: Router,
    ) {}

    supports(version: string, type: string): boolean {
        return SUPPORTED_VERSIONS.indexOf(version) != -1 && type == TOP_LEVEL_NAV_TYPE;
    }

    bootstrap(
        extensionBundle: any,
        extensionAngularModule: NgModuleRef<any>,
        extensionPointManifest: TopLevelNavExtensionPointManifest,
        ordering: number,
        state: ExternalExtensionLoaderState
    ): void {
        if (!extensionPointManifest.module && !extensionPointManifest.component) {
            throw new Error("Primary navigation exptension point module/component is not defined in manifest.");
        }

        if (extensionPointManifest.module && extensionPointManifest.component) {
            throw new Error("Primary navigation exptension point only module or component can be defined in manifest, not both.");
        }

        const vcdRoute = state.routes[`${state.spec.id}:${extensionPointManifest.urn}`];

        if (!vcdRoute) {
            throw new Error(`Primary navigation extension point '${extensionPointManifest.name}'
            defined a module but no route was found.`);
        }

        try {
            this.registerDynamicRoutesInStaticModule(state.manifest.vendor, vcdRoute);
        } catch (error) {
            throw Error(`Unable to register dynamic route ${vcdRoute}
            for extension point ${extensionPointManifest.name} in statc module.`);
        }

        const registration = new ExtensionComponentRegistration(
            TOP_LEVEL_NAV_SCOPE,
            extensionAngularModule,
            null,
            null,
            [],
            ordering,
            extensionPointManifest,
            state.manifest.locales,
            vcdRoute,
            getHashedString(state.manifest.vendor),
            null,
            state.spec.resourcePath,
        );
        this.extensionComponentRegistry.registerExtensionComponent(registration);
    }

    private registerDynamicRoutesInStaticModule(vendor: string,
        vcdRoute: string): void {
        const extensibleRoute = this.HYPER_PLUGIN_CONFIG.routes.filter((route) => {
            return route.type === TOP_LEVEL_NAV_TYPE;
        })[0];

        if (!extensibleRoute) {
            return;
        }

        const extensibleRouteSegments = extensibleRoute.path.split("/");
        const mainPath = extensibleRouteSegments[0];
        const childSegments = extensibleRouteSegments.slice(1);

        let mainRoute = this.router.config.find((route) => {
            return route.path === mainPath;
        });

        this.extensionPointRoutingHandler.findRouteChain(mainRoute, childSegments).subscribe((pluginsContainerRouter) => {  
            if (!pluginsContainerRouter.children) {
                pluginsContainerRouter.children = [];
            }
    
            const alreadyExistingRouteVendorRoute = pluginsContainerRouter.children.find((route) => {
                return route.path === getHashedString(vendor);
            });
    
            if (!alreadyExistingRouteVendorRoute) {
                const newRoute: Route = {
                    path: getHashedString(vendor),
                    children: [
                        {
                            path: "",
                            pathMatch: "full",
                            redirectTo: vcdRoute,
                        },
                        {
                            path: vcdRoute,
                            component: ExtensionContainerComponent,
                            data: {
                                vcdRoute,
                                fullHeight: extensibleRoute.fullHeight
                            },
                            canDeactivate: [ExtensionContainerGuard],
                            children: [
                                {
                                    path: "**",
                                    component: ExtensionContainerComponent,
                                    canDeactivate: [ExtensionContainerGuard],
                                },
                            ],
                        },
                    ],
                };
    
                pluginsContainerRouter.children.push(newRoute);
                return;
            }
    
            alreadyExistingRouteVendorRoute.children.push({
                path: vcdRoute,
                component: ExtensionContainerComponent,
                data: {
                    vcdRoute,
                    fullHeight: extensibleRoute.fullHeight,
                },
                canDeactivate: [ExtensionContainerGuard],
                children: [
                    {
                        path: "**",
                        component: ExtensionContainerComponent,
                        canDeactivate: [ExtensionContainerGuard],
                    },
                ],
            });
        });
    }
}
