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

import { Inject, Injectable } from "@angular/core";
import { Observable, of, throwError as _throw } from "rxjs";
import { EXTENSION_POINT_HANDLER } from "../../../tokens";
import { ExtensionPointManifest } from "../../extensibility.model";
import { ExtensionPointHandler } from "../../extension-point.handler";
import { ExtensionLoaderStep, ExternalExtensionLoaderState } from "../extension-loader.model";
import { ExternalExtensionLoaderErrorObservable } from "./external-extension-loader-error";

@Injectable()
export class BootstrapExtensionPointsStep implements ExtensionLoaderStep<ExternalExtensionLoaderState> {
    constructor(@Inject(EXTENSION_POINT_HANDLER) private extensionPointHandlers: ExtensionPointHandler[]) {}

    /**
     * Bootstrap extension points by delegating to the appropriate handlers.
     * State inputs: manifest, spec, bundle, angularModule
     * State outputs: none
     */
    perform(state: ExternalExtensionLoaderState): Observable<ExternalExtensionLoaderState> {
        try {
            const manifests: ExtensionPointManifest[] = state.manifest.extensionPoints || [];

            console.log(
                `[${state.spec.id} (${state.manifest.name})]` + ` Bootstrapping ${manifests.length} extension point(s)`
            );
            manifests.forEach((extensionPoint, index) => {
                console.log(
                    `[${state.spec.id} (${state.manifest.name})]` +
                        ` Bootstrapping extension point [${extensionPoint.urn}] (${extensionPoint.name})`
                );
                const handler = this.extensionPointHandlers.find((handler) =>
                    handler.supports(state.manifest.containerVersion, extensionPoint.type)
                );
                if (handler) {
                    // For now we use the order of the extension points in the manifest as the explicit
                    // ordering.  In future, we will want to support fetching ordering from the backend.
                    try {
                        handler.bootstrap(
                            state.bundle,
                            state.angularModules
                                ? state.angularModules[`${state.spec.id}:${extensionPoint.urn}`]
                                : null,
                            extensionPoint,
                            index,
                            state
                        );
                    } catch (e) {
                        console.error(
                            `[Bootstrap Extenson Points Step] Unable to bootstrap extension point ${extensionPoint.urn}.`,
                            e
                        );
                    }
                } else {
                    console.error(
                        `No handler supporting extension point type ` +
                            `${extensionPoint.type} for extension container version ${state.manifest.containerVersion}`
                    );
                }
            });
            return of(state);
        } catch (err) {
            console.error(`[${state?.spec?.id} (${state?.manifest?.name})] Could not bootstrap extension points`, err);
            return new ExternalExtensionLoaderErrorObservable();
        }
    }
}
