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

import { Injectable, NgModuleRef } from "@angular/core";
import { AngularAppBundle, ExtensionPointManifest } from "../extensibility.model";
import { ExtensionComponentRegistration, ExtensionComponentRegistry } from "../extension-component.registry";
import { ExtensionPointHandler } from "../extension-point.handler";
import { ExternalExtensionLoaderState } from "../loader/extension-loader.model";
import { getHashedString } from "../loader/external/create-external-extension-route.step";

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

export const ANGULAR_APP = "application:angular";
export type ANGULAR_APP = "application:angular";

export const ANGULAR_APP_PRIMARY_NAVIGATION = `${ANGULAR_APP}:apps`;
export type ANGULAR_APP_PRIMARY_NAVIGATION = "application:angular:apps";

/**
 * Seeder extension point is used to bootstrap libraries in the core ui, which then will be used
 * to the UI Plugins.
 */
export interface AngularAppExtensionPointManifest extends ExtensionPointManifest {
    readonly type: ANGULAR_APP_PRIMARY_NAVIGATION;
    readonly urn: string;
}

const AngularAppsExtensionPoint = "Angular Apps Extension Point";

@Injectable()
export class AngularAppsExtensionPointHandler implements ExtensionPointHandler {
    constructor(private extensionComponentRegistry: ExtensionComponentRegistry) {}

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

    bootstrap(
        extensionBundle: AngularAppBundle<{ name: string }>,
        extensionAngularModule: NgModuleRef<any>,
        extensionPointManifest: AngularAppExtensionPointManifest,
        ordering: number,
        state?: ExternalExtensionLoaderState
    ): void {
        if (!extensionBundle) {
            throw new Error(`[${AngularAppsExtensionPoint}] Bundle not provided for ${extensionPointManifest?.urn}!`);
        }

        if (!extensionBundle.ROUTES) {
            throw new Error(
                `[${AngularAppsExtensionPoint}] No Routes Provided by bundle for ${extensionPointManifest?.urn}!`
            );
        }

        if (!extensionBundle.activate) {
            throw new Error(
                `[${AngularAppsExtensionPoint}] No Activation Hook Provided for bundle ${extensionPointManifest?.urn}!`
            );
        }

        if (!extensionBundle.bootstrap) {
            throw new Error(
                `[${AngularAppsExtensionPoint}] No Bootstrap Hook Provided for bundle ${extensionPointManifest?.urn}!`
            );
        }

        if (!extensionBundle.deactivate) {
            throw new Error(
                `[${AngularAppsExtensionPoint}] No De-Activation Hook Provided for bundle ${extensionPointManifest?.urn}!`
            );
        }

        if (!extensionBundle.template) {
            throw new Error(
                `[${AngularAppsExtensionPoint}] No HTML Template Provided for bundle ${extensionPointManifest?.urn}!`
            );
        }

        /**
         * Adds new css var to the html root selector.
         * This css will be used to rescale the plugin rem size for this particular plugin.
         *
         * Rescale means shrink or grow the original plugin rem, in such a way it looks as it's supposed.
         *
         * This css var is used only per plugin, meaning each plugin has it's own rem "scaler".
         */
        if (state.manifest.pluginBasePx) {
            const rootCss: any = document.querySelector(":root");
            // Currently 1rem in Core UI is equal to 20px
            const coreUIBasePxSize = 20;

            let remScalerName: string = state.manifest.urn;
            // Remove all ":" symbols because they are invalid for CSS var name.
            while (remScalerName.includes(":")) {
                remScalerName = remScalerName.replace(":", "-");
            }
            rootCss.style.setProperty(`--${remScalerName}`, state.manifest.pluginBasePx / coreUIBasePxSize);
        }

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