import { BrowserModule } from '@angular/platform-browser';
import { NgModule, APP_INITIALIZER, Injector } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ClarityModule, ClrIconModule } from '@clr/angular';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { environment } from '../environments/environment';
import { CONTAINER_BRANDING } from '../environments/branding';
import { Store, StoreModule } from '@ngrx/store';
import { AppConfigService } from './app-config.service';
import { HttpClientModule } from '@angular/common/http';
import { CONTAINER_CREDENTIALS } from '../environments/access';
import { CreateVappComponent } from "./compute/create-vapp/create-vapp.component";
import { CreateVmComponent } from "./compute/create-vm/create-vm.component";
import { VappActionsComponent } from "./compute/vapp-actions/vapp-actions.component";
import { VmActionsComponent } from "./compute/vm-actions/vm-actions.component";
import { VmsComponent } from "./compute/vms.component";
import { VappsComponent } from "./compute/vapps.component";
import { CreateOrgComponent } from "./create-org/create-org.component";
import { DatacenterDetailsComponent } from "./datacenter-details/datacenter-details.component";
import { DatacenterOverviewComponent } from "./datacenter-overview/datacenter-overview.component";
import { PrimaryNavigationComponent } from "./primary-navigation/primary-navigation.component";
import { CommonModule } from "@angular/common";
import { ApplicationsComponent } from "./applications/applications.component";
import { ExtensibilityModule } from "./framework/common/extensibility/extensibility.module";
import { ResourceModule } from "./framework/extensions/vdcs/resources/resource.module";
import { BootstrapDetails } from "./framework/common/models/bootstrap-details";
import { ExtensionSpec } from "./framework/common/extensibility/extensibility.model";
import { ExternalExtensionLoader } from "./framework/common/extensibility/loader/external/external-extension.loader";
import { I18nModule } from "@vcd/i18n";
import { CORE_UI_CUSTOM_EVENTS, ForceCoreUIRouterNavigationEventDetails, PluginsMenuesEventDetails } from "./framework/common/extensibility/extension-container/core-ui-events";
import { Router } from "@angular/router";
import { appEffectImports } from "./framework/common/state/app.effects";
import { appStateImports } from "./framework/common/state/app.state";
import { ExtensionNavRegistrationAction } from "./framework/common/state/extensions/extensions.actions";
import { API_ROOT_URL } from "./framework/common/tokens";
import { ClarityIcons } from "@clr/icons";
import { AllShapes } from "@clr/icons/shapes/all-shapes";
import { PluginsManagerService } from "./framework/common/extensibility/plugins-manager/plugins-manager.service";
import { HomeComponent } from "./home/home.component";
import { DatacenterCardsComponent } from "./datacenter-overview/datacenter-cards.component";

/**
 * This bootstrap method loads all modules.  It is intended to be
 * used as an APP_INITIALIZER to prevent bootstrap until the modules
 * are loaded.
 *
 * XXX Angular has SERIOUS lifecycle issues.  An APP_INITIALIZER cannot
 * resolve circular dependencies, and the Router contains them!
 * Therefore we have to inject the Injector and use a setTimeout
 * to get hold of the ExtensionLoaderService.  This works - for the current
 * version of Angular.
 *
 * Applicable for both bootstrapPlugins and bootstrapInternalPlugins.
 */
export function bootstrapPlugins(injector: Injector): () => Promise<boolean> {
	return () =>
		new Promise<boolean>((resolve, reject) => {
			const bootstrapDetails: BootstrapDetails = window["_bootstrapDetails"];
			const extensionSpecs: ExtensionSpec[] = Object.keys(bootstrapDetails.thirdPartyExtensions).map((id) => ({
				id,
				resourcePath: bootstrapDetails.thirdPartyExtensions[id],
			}));

			const externalExtensionLoader = injector.get(ExternalExtensionLoader);
			return externalExtensionLoader
				.loadExternalExtension(extensionSpecs)
				.toPromise()
				.then(() => resolve(true));
		});
}

@NgModule({
	declarations: [
		AppComponent,
		HomeComponent,
		ApplicationsComponent,
		CreateVappComponent,
		CreateVmComponent,
		VappActionsComponent,
		VmActionsComponent,
		VmsComponent,
		VappsComponent,
		CreateOrgComponent,
		DatacenterDetailsComponent,
		DatacenterOverviewComponent,
		DatacenterCardsComponent,
		PrimaryNavigationComponent,
	],
	imports: [
		BrowserModule,
		CommonModule,
		AppRoutingModule,
		ClarityModule,
		ClrIconModule,
		HttpClientModule,
		BrowserAnimationsModule,
		StoreModule.forRoot({}),

		/**
		 * Extensibility Essentials
		 */
		I18nModule.forRoot(),
		ExtensibilityModule,
		ResourceModule,
		appStateImports,
		appEffectImports,
	],
	providers: [
		{ provide: CONTAINER_BRANDING, useValue: environment['branding'] },
		{ provide: CONTAINER_CREDENTIALS, useValue: environment['credentials'] },
		{ provide: API_ROOT_URL, useValue: '' },
		{ provide: APP_INITIALIZER, useFactory: bootstrapApplication, multi: true, deps: [AppConfigService] },
		{
			provide: APP_INITIALIZER,
			multi: true,
			useFactory: bootstrapPlugins,
			deps: [Injector],
		},
		PluginsManagerService,
	],
	bootstrap: [AppComponent]
})
export class AppModule {
	constructor(private router: Router, private appStore: Store<any>,) {
		ClarityIcons.add(AllShapes);

		this.bindExtensibilityEvents(appStore);

		setInterval(() => {
			console.log("[Router]", this.router);
		}, 3000);
	}

	private bindExtensibilityEvents(appStore: Store<any>): void {
		window.addEventListener(
			CORE_UI_CUSTOM_EVENTS.FORCE_CORE_UI_ROUTER_NAVIGATION,
			(event: CustomEvent<ForceCoreUIRouterNavigationEventDetails>) => {
				this.router.navigateByUrl(event.detail.url);
			}
		);

		window.addEventListener(CORE_UI_CUSTOM_EVENTS.PLUGINS_MENUES, (e: CustomEvent<PluginsMenuesEventDetails>) => {
			e.detail.pluginMenus.forEach((menu) => {
				this.appStore.dispatch(new ExtensionNavRegistrationAction(menu));
			});
		});
	}
}

export function bootstrapApplication(appConfig: AppConfigService): () => Promise<boolean> {
	return () => new Promise<boolean>((resolve) => {
		console.group('[application bootstrap]');
		appConfig.login().then((tokens) => {
			const [jwt, authToken] = tokens;
			localStorage.setItem("jwt", jwt);
			localStorage.setItem("authtoken", authToken);
		});
		resolve(true);
		console.groupEnd();
	});
}
