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

import { Injectable, NgModuleFactory, NgModuleRef } from "@angular/core";
import { Route, Router } from "@angular/router";
import { RESOURCES_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 appliations
 * (i.e. with <ng-container vcdExtensionComponents scope="...">)
 */
export const APPLICATIONS_COMPONENT_SCOPE = `${COMPONENT_SCOPE_PREFIX}:applications`;

/**
 * Applications manifest type (constant value)
 */
export const APPLICATIONS_TYPE = "navigation:applications";

/**
 * Applications manifest type (type value)
 */
export type APPLICATIONS_TYPE = "navigation:applications";

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

/**
 * Applications Section extension point manifests have the type of "applications".
 * They declare a single component, which should be injected onto the applications screen.
 */
export interface ApplicationsExtensionPointManifest extends ExtensionPointManifest {
    readonly type: APPLICATIONS_TYPE;
    readonly component: string;
    readonly module?: string;
    readonly route?: string;
}

/**
 * This extension point handler supports extension manifests of version 9.5.0 and of type
 * "applications".  It will register the defined component with the ExtensionComponentRegistry
 * under the scope of "urn:vmware:vcd:component-scope:applications.  This scope is intended to be
 * injected in the Applications (i.e. with <ng-container vcdExtensionComponents scope="...">)
 */
@Injectable()
export class ApplicationsExtensionPointHandler implements ExtensionPointHandler {
    constructor(private extensionComponentRegistry: ExtensionComponentRegistry, private router: Router) {}

    /**
     * Supports version 9.5.0 and type "applications.
     */
    supports(version: string, type: string): boolean {
        return SUPPORTED_VERSIONS.indexOf(version) != -1 && type == APPLICATIONS_TYPE;
    }

    /**
     * Register the defined component with the ExtensionComponentRegistry under the scope of
     * "urn:vmware:vcd:component-scope:applications".
     */
    bootstrap(
        extensionBundle: any,
        extensionAngularModule: NgModuleRef<any>,
        extensionPointManifest: ApplicationsExtensionPointManifest,
        ordering: number,
        state: ExternalExtensionLoaderState
    ): void {
        if (!extensionPointManifest.component) {
            throw new Error("Applications component not defined in manifest");
        }
        const vcdRoute = state.routes[`${state.spec.id}:${extensionPointManifest.urn}`];
        // const angularFactory = state.angularModuleFactories[`${state.spec.id}:${extensionPointManifest.urn}`];
        const angularFactory = null;

        if (extensionPointManifest.module) {
            if (!vcdRoute) {
                throw new Error(`Applications component '${extensionPointManifest.component}'
                defined a module but no route was found.`);
            }

            // if (!angularFactory) {
            //     throw new Error(`Applications component '${extensionPointManifest.component}'
            //     defined a module but the factory wasn't crearted.`);
            // }

            this.registerRouteDynamically(state.manifest.vendor, vcdRoute, angularFactory, extensionPointManifest);
        }

        // const component = extensionBundle[extensionPointManifest.component];
        const component = null;
        // if (!component) {
        //     throw new Error(
        //         `Applications component '${extensionPointManifest.component}' ` + "not exported by extension bundle"
        //     );
        // }

        const registration = new ExtensionComponentRegistration(
            APPLICATIONS_COMPONENT_SCOPE,
            extensionAngularModule,
            angularFactory,
            component,
            [],
            ordering,
            extensionPointManifest,
            state.manifest.locales,
            extensionPointManifest.module ? vcdRoute : null,
            getHashedString(state.manifest.vendor)
        );
        this.extensionComponentRegistry.registerExtensionComponent(registration);
    }

    private registerRouteDynamically(
        vendor: string,
        vcdRoute: string,
        angularFactory: NgModuleFactory<any>,
        extensionPointManifest: ExtensionPointManifest
    ): void {
        try {
            const applicationsRootRoute = this.router.config.find((route) => {
                return route.path === "applications";
            });

            const globalResourcesPath = applicationsRootRoute.children.find((route) => {
                return route.path === RESOURCES_ROUTE;
            });

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

            const alreadyExistingVendorRoute = globalResourcesPath.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],
                                },
                            ],
                        },
                    ],
                };

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

            alreadyExistingVendorRoute.children.push({
                path: vcdRoute,
                component: ExtensionContainerComponent,
                data: {
                    vcdRoute,
                },
                canDeactivate: [ExtensionContainerGuard],
                children: [
                    {
                        path: "**",
                        component: ExtensionContainerComponent,
                        canDeactivate: [ExtensionContainerGuard],
                    },
                ],
            });
        } catch (e) {
            throw Error(`Unable to register dynamic route ${vcdRoute}
            for extension point ${extensionPointManifest.name} in statc module.`);
        }
    }
}
