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

import { Injectable, NgModuleFactory, NgModuleRef } from "@angular/core";
import { Route, Router } from "@angular/router";
import { PLUGINS_ROUTE } from "../../../app/plugin-container/plugin-container.module";
import { COMPONENT_SCOPE_PREFIX } from "../details-container/details-container.extension-point";
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";

/**
 * 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;
}

/**
 * This extension point handler supports extension manifests of version 9.1.0 and of type
 * "tprimary-navigation". It will register the plugin modules directly in the main navigation
 * via the plugins manager service.
 */
@Injectable()
export class TopLevelNavExtensionPointHandler implements ExtensionPointHandler {
    constructor(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) {
            throw new Error("Primary navigation exptension point module is not defined in manifest.");
        }

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

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

        // if (!angularFactory) {
        //     throw new Error(`Primary navigation extension point '${extensionPointManifest.name}'
        //     defined a module but the factory wasn't crearted.`);
        // }

        try {
            this.registerDynamicRoutesInStaticModule(state.manifest.vendor, vcdRoute, angularFactory);
            this.router.resetConfig(this.router.config);
        } 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,
            angularFactory,
            null,
            [],
            ordering,
            extensionPointManifest,
            state.manifest.locales,
            vcdRoute,
            getHashedString(state.manifest.vendor)
        );
        this.extensionComponentRegistry.registerExtensionComponent(registration);
    }

    private registerDynamicRoutesInStaticModule(
        vendor: string,
        vcdRoute: string,
        angularFactory: NgModuleFactory<any>
    ): void {
        const pluginsContainerRouter = this.router.config.find((route) => {
            return route.path === PLUGINS_ROUTE;
        });

        if (!pluginsContainerRouter.children) {
            pluginsContainerRouter.children = [];
        }

        const alreadyExistingVendorRoute = pluginsContainerRouter.children.find((route) => {
            return route.path === getHashedString(vendor);
        });

        if (!alreadyExistingVendorRoute) {
            const newRoute: Route = {
                path: getHashedString(vendor),
                children: [
                    {
                        path: "",
                        pathMatch: "full",
                        redirectTo: vcdRoute,
                    },
                    {
                        path: vcdRoute,
                        component: ExtensionContainerComponent,
                        data: {
                            vcdRoute,
                        },
                        canDeactivate: [ExtensionContainerGuard],
                        children: [
                            {
                                path: "**",
                                component: ExtensionContainerComponent,
                                canDeactivate: [ExtensionContainerGuard],
                            },
                        ],
                    },
                ],
            };

            pluginsContainerRouter.children.push(newRoute);
            return;
        }

        alreadyExistingVendorRoute.children.push({
            path: vcdRoute,
            component: ExtensionContainerComponent,
            data: {
                vcdRoute,
            },
            canDeactivate: [ExtensionContainerGuard],
            children: [
                {
                    path: "**",
                    component: ExtensionContainerComponent,
                    canDeactivate: [ExtensionContainerGuard],
                },
            ],
        });
    }
}
