/**
 * Copyright (c) 2025 Broadcom. All Rights Reserved.
 * Broadcom Confidential. The term "Broadcom" refers to Broadcom Inc.
 * and/or its subsidiaries.
 */
import { NgModuleRef, NgModuleFactory, Type, Provider, ViewContainerRef, ComponentRef, Injector } from "@angular/core";
import { TranslationSet } from "@vcd/i18n";
import { ExtensionPointManifest, AngularAppBundle } from "./extensibility.model";

/**
 * A registration for an extension-provided component.
 */
export class ExtensionComponentRegistration {
    /**
     * Counter value used for the ID of new registrations.
     */
    private static maxId = 0;

    /**
     * This unique ID is used for breaking ties when sorting registrations by ordering.
     */
    readonly id: number;

    /**
     * An arbitrary URN keying for splitting registrations into different types.
     * This is intended to represent an 'area' of the UI where components may be injected,
     * for example the dashboard or VM details.
     */
    readonly scope: string;

    /**
     * The module containing the component we wish to create.
     */
    readonly module: NgModuleRef<any>;

    /**
     * The factory of the extension point module.
     */
    readonly moduleFactory: NgModuleFactory<any>;

    /**
     * The component class itself.
     */
    readonly component: Type<any>;

    /**
     * Any extra providers to create the component with.
     */
    readonly providers: Provider[];

    /**
     * An explicit ordering.  Registrations with the same scope are ordered by this field value, with lower
     * numbers being first.
     */
    readonly ordering: number;

    /**
     * The manifest associated with this registration.
     */
    readonly manifest: ExtensionPointManifest;

    /**
     * Translations
     */
    readonly translations: TranslationSet;

    /**
     * Extension Point Route
     */
    readonly vcdRoute: string;

    /**
     * The hashed version of the vendor used when constructing routes.
     */
    readonly hashedVendor: string;

    /**
     * Details how to setup a separate Angular Application next to vCD Platform.
     *
     * angularAppBundle - Contains lifecycle hooks, such as: bootstrap, activate and de-activate.
     * those hooks are called by the platfrom at a give point of time depending on the conditions.
     * The implementation and the whole complexity is outside the platform.
     *
     * bootstrapped and activated - flags to determine when to de-activate certain Angular Application.
     */
    readonly angularAppDetails: {
        angularAppBundle?: AngularAppBundle<any>;
        bootstrapped?: boolean;
        activated?: boolean;
    };

    readonly resourcePath: string | undefined;

    /**
     * Create a registration.  The maxId counter is used to set the id field, then increased.
     */
    constructor(
        scope: string,
        module: NgModuleRef<any>,
        moduleFactory: NgModuleFactory<any>,
        component: Type<any>,
        providers: Provider[],
        ordering: number,
        manifest: ExtensionPointManifest,
        translations: TranslationSet,
        vcdRoute: string,
        hashedVendor: string,
        angularAppBundle?: AngularAppBundle<any>,
        resourcePath?: string
    ) {
        this.id = ExtensionComponentRegistration.maxId++;
        this.scope = scope;
        this.module = module;
        this.moduleFactory = moduleFactory;
        this.component = component;
        this.providers = providers;
        this.ordering = ordering;
        this.manifest = manifest;
        this.translations = translations;
        this.vcdRoute = vcdRoute;
        this.hashedVendor = hashedVendor;
        this.angularAppDetails = {
            angularAppBundle: angularAppBundle,
            bootstrapped: false,
            activated: false,
        };
        this.resourcePath = resourcePath;
    }

    /**
     * Helper to instantiate the component.
     * @param viewContainerRef Where to place the created component.
     * @param index Optional position to place the component at in the view.
     */
    createComponent(viewContainerRef: ViewContainerRef, index?: number): ComponentRef<any> {
        const componentFactory = this.module.componentFactoryResolver.resolveComponentFactory(this.component);
        const injector = Injector.create({
            providers: this.providers,
            parent: this.module.injector
        });
        return viewContainerRef.createComponent(componentFactory, index, injector);
    }
}