import { HttpErrorResponse } from '@angular/common/http';
import { Injector } from '@angular/core';
import { NcApplication, NcMenu, NcRoute, NgCortexService } from '@ng-cortex/core';
import { getErrorMessage } from '@ng-cortex/utils';
import { NzNotificationService } from 'ng-zorro-antd/notification';
import { map, tap } from 'rxjs/operators';
import { environment } from '../../../../../environments/environment';
import { routes } from '../../../dev-routing.module';

import * as NgAnimationsLibrary from '@angular/animations';
import * as NgCdkDragDropLibrary from '@angular/cdk/drag-drop';
import * as NgCdkScrollingLibrary from '@angular/cdk/scrolling';
import * as NgCommonLibrary from '@angular/common';
import * as NgCommonHttpLibrary from '@angular/common/http';
import * as NgCompilerLibrary from '@angular/compiler';
import * as NgCoreLibrary from '@angular/core';
import * as NgFormsLibrary from '@angular/forms';
import * as NgPlatformBrowserLibrary from '@angular/platform-browser';
import * as NgRouterLibrary from '@angular/router';
import * as DateFnsLibrary from 'date-fns';
import * as RxJSLibrary from 'rxjs';
import * as RxJSOperatorsLibrary from 'rxjs/operators';
import * as NzAffixLibrary from 'ng-zorro-antd/affix';
import * as NzAlertLibrary from 'ng-zorro-antd/alert';
import * as NzAnchorLibrary from 'ng-zorro-antd/anchor';
import * as NzAutocompleteLibrary from 'ng-zorro-antd/auto-complete';
import * as NzAvatarLibrary from 'ng-zorro-antd/avatar';
import * as NzBackTopLibrary from 'ng-zorro-antd/back-top';
import * as NzBadgeLibrary from 'ng-zorro-antd/badge';
import * as NzBreadCrumbLibrary from 'ng-zorro-antd/breadcrumb';
import * as NzButtonLibrary from 'ng-zorro-antd/button';
import * as NzCalendarLibrary from 'ng-zorro-antd/calendar';
import * as NzCardLibrary from 'ng-zorro-antd/card';
import * as NzCarouselLibrary from 'ng-zorro-antd/carousel';
import * as NzCascaderLibrary from 'ng-zorro-antd/cascader';
import * as NzCheckboxLibrary from 'ng-zorro-antd/checkbox';
import * as NzCollapseLibrary from 'ng-zorro-antd/collapse';
import * as NzCommentLibrary from 'ng-zorro-antd/comment';
import * as NzNoAnimationLibrary from 'ng-zorro-antd/core/no-animation';
import * as NzTransButtonLibrary from 'ng-zorro-antd/core/trans-button';
import * as NzTransitionPatchLibrary from 'ng-zorro-antd/core/transition-patch';
import * as NzWaveLibrary from 'ng-zorro-antd/core/wave';
import * as NzDatePickerLibrary from 'ng-zorro-antd/date-picker';
import * as NzDescriptionsLibrary from 'ng-zorro-antd/descriptions';
import * as NzDividerLibrary from 'ng-zorro-antd/divider';
import * as NzDrawerLibrary from 'ng-zorro-antd/drawer';
import * as NzDropDownLibrary from 'ng-zorro-antd/dropdown';
import * as NzEmptyLibrary from 'ng-zorro-antd/empty';
import * as NzFormLibrary from 'ng-zorro-antd/form';
import * as NzGridLibrary from 'ng-zorro-antd/grid';
import * as NzI18nLibrary from 'ng-zorro-antd/i18n';
import * as NzIconLibrary from 'ng-zorro-antd/icon';
import * as NzImageLibrary from 'ng-zorro-antd/image';
import * as NzInputLibrary from 'ng-zorro-antd/input';
import * as NzInputNumberLibrary from 'ng-zorro-antd/input-number';
import * as NzLayoutLibrary from 'ng-zorro-antd/layout';
import * as NzListLibrary from 'ng-zorro-antd/list';
import * as NzMentionLibrary from 'ng-zorro-antd/mention';
import * as NzMenuLibrary from 'ng-zorro-antd/menu';
import * as NzMessageLibrary from 'ng-zorro-antd/message';
import * as NzModalLibrary from 'ng-zorro-antd/modal';
import * as NzNotificationLibrary from 'ng-zorro-antd/notification';
import * as NzPageHeaderLibrary from 'ng-zorro-antd/page-header';
import * as NzPaginationLibrary from 'ng-zorro-antd/pagination';
import * as NzPopconfirmLibrary from 'ng-zorro-antd/popconfirm';
import * as NzPopoverLibrary from 'ng-zorro-antd/popover';
import * as NzProgressLibrary from 'ng-zorro-antd/progress';
import * as NzRadioLibrary from 'ng-zorro-antd/radio';
import * as NzRateLibrary from 'ng-zorro-antd/rate';
import * as NzResultLibrary from 'ng-zorro-antd/result';
import * as NzSelectLibrary from 'ng-zorro-antd/select';
import * as NzSkeletonLibrary from 'ng-zorro-antd/skeleton';
import * as NzSliderLibrary from 'ng-zorro-antd/slider';
import * as NzSpinLibrary from 'ng-zorro-antd/spin';
import * as NzStatisticLibrary from 'ng-zorro-antd/statistic';
import * as NzStepsLibrary from 'ng-zorro-antd/steps';
import * as NzSwitchLibrary from 'ng-zorro-antd/switch';
import * as NzTableLibrary from 'ng-zorro-antd/table';
import * as NzTabsLibrary from 'ng-zorro-antd/tabs';
import * as NzTagLibrary from 'ng-zorro-antd/tag';
import * as NzTimePickerLibrary from 'ng-zorro-antd/time-picker';
import * as NzTimelineLibrary from 'ng-zorro-antd/timeline';
import * as NzToolTipLibrary from 'ng-zorro-antd/tooltip';
import * as NzTransferLibrary from 'ng-zorro-antd/transfer';
import * as NzTreeLibrary from 'ng-zorro-antd/tree';
import * as NzTreeViewLibrary from 'ng-zorro-antd/tree-view';
import * as NzTreeSelectLibrary from 'ng-zorro-antd/tree-select';
import * as NzTypographyLibrary from 'ng-zorro-antd/typography';
import * as NzUploadLibrary from 'ng-zorro-antd/upload';
import * as NzResizableLibrary from 'ng-zorro-antd/resizable';
import * as NzPipesLibrary from 'ng-zorro-antd/pipes';
import * as NgCortexCoreLibrary from '@ng-cortex/core';
import * as NgCortexUtilsLibrary from '@ng-cortex/utils';

/** NgCortex引导启动制造器 */
export function NcBootFactory(injector: Injector): () => Promise<void> {
    const nzNotificationService = injector.get(NzNotificationService);
    const ngCortexService = injector.get(NgCortexService);
    return () => ngCortexService
        .registerDependency('@angular/animations', NgAnimationsLibrary)
        .registerDependency('@angular/cdk/drag-drop', NgCdkDragDropLibrary)
        .registerDependency('@angular/cdk/scrolling', NgCdkScrollingLibrary)
        .registerDependency('@angular/common', NgCommonLibrary)
        .registerDependency('@angular/common/http', NgCommonHttpLibrary)
        .registerDependency('@angular/compiler', NgCompilerLibrary)
        .registerDependency('@angular/core', NgCoreLibrary)
        .registerDependency('@angular/forms', NgFormsLibrary)
        .registerDependency('@angular/platform-browser', NgPlatformBrowserLibrary)
        .registerDependency('@angular/router', NgRouterLibrary)
        .registerDependency('date-fns', DateFnsLibrary)
        .registerDependency('rxjs', RxJSLibrary)
        .registerDependency('rxjs/operators', RxJSOperatorsLibrary)
        .registerDependency('ng-zorro-antd/affix', NzAffixLibrary)
        .registerDependency('ng-zorro-antd/alert', NzAlertLibrary)
        .registerDependency('ng-zorro-antd/anchor', NzAnchorLibrary)
        .registerDependency('ng-zorro-antd/auto-complete', NzAutocompleteLibrary)
        .registerDependency('ng-zorro-antd/avatar', NzAvatarLibrary)
        .registerDependency('ng-zorro-antd/back-top', NzBackTopLibrary)
        .registerDependency('ng-zorro-antd/badge', NzBadgeLibrary)
        .registerDependency('ng-zorro-antd/breadcrumb', NzBreadCrumbLibrary)
        .registerDependency('ng-zorro-antd/button', NzButtonLibrary)
        .registerDependency('ng-zorro-antd/calendar', NzCalendarLibrary)
        .registerDependency('ng-zorro-antd/card', NzCardLibrary)
        .registerDependency('ng-zorro-antd/carousel', NzCarouselLibrary)
        .registerDependency('ng-zorro-antd/cascader', NzCascaderLibrary)
        .registerDependency('ng-zorro-antd/checkbox', NzCheckboxLibrary)
        .registerDependency('ng-zorro-antd/collapse', NzCollapseLibrary)
        .registerDependency('ng-zorro-antd/comment', NzCommentLibrary)
        .registerDependency('ng-zorro-antd/core/no-animation', NzNoAnimationLibrary)
        .registerDependency('ng-zorro-antd/core/trans-button', NzTransButtonLibrary)
        .registerDependency('ng-zorro-antd/core/transition-patch', NzTransitionPatchLibrary)
        .registerDependency('ng-zorro-antd/core/wave', NzWaveLibrary)
        .registerDependency('ng-zorro-antd/date-picker', NzDatePickerLibrary)
        .registerDependency('ng-zorro-antd/descriptions', NzDescriptionsLibrary)
        .registerDependency('ng-zorro-antd/divider', NzDividerLibrary)
        .registerDependency('ng-zorro-antd/drawer', NzDrawerLibrary)
        .registerDependency('ng-zorro-antd/dropdown', NzDropDownLibrary)
        .registerDependency('ng-zorro-antd/empty', NzEmptyLibrary)
        .registerDependency('ng-zorro-antd/form', NzFormLibrary)
        .registerDependency('ng-zorro-antd/grid', NzGridLibrary)
        .registerDependency('ng-zorro-antd/i18n', NzI18nLibrary)
        .registerDependency('ng-zorro-antd/icon', NzIconLibrary)
        .registerDependency('ng-zorro-antd/image', NzImageLibrary)
        .registerDependency('ng-zorro-antd/input', NzInputLibrary)
        .registerDependency('ng-zorro-antd/input-number', NzInputNumberLibrary)
        .registerDependency('ng-zorro-antd/layout', NzLayoutLibrary)
        .registerDependency('ng-zorro-antd/list', NzListLibrary)
        .registerDependency('ng-zorro-antd/mention', NzMentionLibrary)
        .registerDependency('ng-zorro-antd/menu', NzMenuLibrary)
        .registerDependency('ng-zorro-antd/message', NzMessageLibrary)
        .registerDependency('ng-zorro-antd/modal', NzModalLibrary)
        .registerDependency('ng-zorro-antd/notification', NzNotificationLibrary)
        .registerDependency('ng-zorro-antd/page-header', NzPageHeaderLibrary)
        .registerDependency('ng-zorro-antd/pagination', NzPaginationLibrary)
        .registerDependency('ng-zorro-antd/popconfirm', NzPopconfirmLibrary)
        .registerDependency('ng-zorro-antd/popover', NzPopoverLibrary)
        .registerDependency('ng-zorro-antd/progress', NzProgressLibrary)
        .registerDependency('ng-zorro-antd/radio', NzRadioLibrary)
        .registerDependency('ng-zorro-antd/rate', NzRateLibrary)
        .registerDependency('ng-zorro-antd/result', NzResultLibrary)
        .registerDependency('ng-zorro-antd/select', NzSelectLibrary)
        .registerDependency('ng-zorro-antd/skeleton', NzSkeletonLibrary)
        .registerDependency('ng-zorro-antd/slider', NzSliderLibrary)
        .registerDependency('ng-zorro-antd/spin', NzSpinLibrary)
        .registerDependency('ng-zorro-antd/statistic', NzStatisticLibrary)
        .registerDependency('ng-zorro-antd/steps', NzStepsLibrary)
        .registerDependency('ng-zorro-antd/switch', NzSwitchLibrary)
        .registerDependency('ng-zorro-antd/table', NzTableLibrary)
        .registerDependency('ng-zorro-antd/tabs', NzTabsLibrary)
        .registerDependency('ng-zorro-antd/tag', NzTagLibrary)
        .registerDependency('ng-zorro-antd/time-picker', NzTimePickerLibrary)
        .registerDependency('ng-zorro-antd/timeline', NzTimelineLibrary)
        .registerDependency('ng-zorro-antd/tooltip', NzToolTipLibrary)
        .registerDependency('ng-zorro-antd/transfer', NzTransferLibrary)
        .registerDependency('ng-zorro-antd/tree', NzTreeLibrary)
        .registerDependency('ng-zorro-antd/tree-view', NzTreeViewLibrary)
        .registerDependency('ng-zorro-antd/tree-select', NzTreeSelectLibrary)
        .registerDependency('ng-zorro-antd/typography', NzTypographyLibrary)
        .registerDependency('ng-zorro-antd/upload', NzUploadLibrary)
        .registerDependency('ng-zorro-antd/resizable', NzResizableLibrary)
        .registerDependency('ng-zorro-antd/pipes', NzPipesLibrary)
        .registerDependency('@ng-cortex/core', NgCortexCoreLibrary)
        .registerDependency('@ng-cortex/utils', NgCortexUtilsLibrary)
        .initialize({
            resourceUrl: environment.resourceUrl,
            coreService: environment.coreService,
            titleSuffix: environment.titleSuffix,
            storageKeyForToken: environment.storageKeyForToken,
            routeForLogin: '/login',
            routeForApplicationIndex: '/dev',
            routeForApplication403: '/dev/403',
            routeForApplication404: '/dev/404',
            routeForApplication500: '/dev/500',
            routeReuse: environment.routeReuse,
            dev: true
        })
        .pipe(map(() => {
            (window as any).Spinner.done();
            injectDevApplication(ngCortexService);
            injectDevMenuList(ngCortexService);
            updateRouteConfigMap(ngCortexService);
        }))
        .pipe(tap(() => { }, (httpErrorResponse: HttpErrorResponse) => {
            (window as any).Spinner.done();
            nzNotificationService.error('初始化失败', getErrorMessage(httpErrorResponse));
            const errorContainer = document.createElement('div');
            errorContainer.innerHTML = '<div style="position: absolute; top: 0px; left: 0px; min-width: 100%; min-height: 100%; display: flex; flex-direction: column; justify-content: center; align-items: center;">' +
                '<img src="/assets/images/init-fail.svg" style="height: 300px; margin-bottom: 24px;">' +
                '<span class="ant-result-title">初始化失败</span>' +
                '</div>';
            document.body.appendChild(errorContainer.firstElementChild!);
        }))
        .toPromise();
}

/**
 * 注入开发应用
 * @param ngCortexService ngCortex服务
 */
function injectDevApplication(ngCortexService: NgCortexService): void {
    const devApplication: NcApplication = {
        name: '调试应用',
        code: 'dev',
        description: '一个开发调试的虚拟应用',
        icon: 'bug',
        groupName: '调试',
        common: false,
        sequence: 1
    };
    ngCortexService.data.applicationList = [devApplication];
    ngCortexService.data.currentApplication = devApplication;
    (ngCortexService as any)._applicationList.next(ngCortexService.data.applicationList);
    (ngCortexService as any)._currentApplication.next(ngCortexService.data.currentApplication);
}

/**
 * 注入开发菜单列表
 * @param ngCortexService ngCortex服务
 */
function injectDevMenuList(ngCortexService: NgCortexService): void {
    const menuList: NcMenu[] = [];
    routes.forEach(routeConfig => {
        const routeConfigData = routeConfig.data!;
        if (routeConfigData.major && !routeConfigData.hidden) {
            menuList.push({
                name: routeConfigData.name!,
                icon: 'file',
                type: '菜单',
                level: 1,
                url: '/dev/<%= getSeparatorCase(name) %>/' + routeConfig.path
            });
        }
    });
    ngCortexService.data.menuList = menuList;
    (ngCortexService as any)._menuList.next(ngCortexService.data.menuList);
}

/**
 * 更新路由配置映射
 * @param ngCortexService ngCortex服务
 */
function updateRouteConfigMap(ngCortexService: NgCortexService): void {
    const routeConfigMap: Map<string, NcRoute[]> = new Map();
    const eachRouteFn = (routeConfig: NcRoute[], pathList?: string[]): void => {
        pathList = pathList || [];
        for (const i in routeConfig) {
            const currentRouteConfig = routeConfig[i];
            const currentPathList = currentRouteConfig.path ? pathList.concat(currentRouteConfig.path.split('/')).filter(path => path) : pathList;
            const currentRoute = '^\\\/' + currentPathList.map(path => path.replace(/^\:.+$/, '[^\\\/]+').replace(/^\*{2}$/, '.*')).join('\\\/') + '$';
            const routeConfigList = routeConfigMap.get(currentRoute) || [];
            routeConfigList.push(currentRouteConfig);
            routeConfigMap.set(currentRoute, routeConfigList);
            if (currentRouteConfig.children !== undefined) {
                eachRouteFn(currentRouteConfig.children, currentPathList);
            }
        }
    };
    (ngCortexService as any)._routeConfigMap.forEach((routeConfigList: NcRoute[], regexp: string) => {
        routeConfigMap.set(regexp, routeConfigList);
        if (regexp.includes('<%= getSeparatorCase(name) %>') && new RegExp(regexp).test(ngCortexService.routeForApplicationIndex + '/<%= getSeparatorCase(name) %>')) {
            eachRouteFn(routes, regexp.replace(/\^|\$/g, '').split('\\\/').filter(segment => segment));
        }
    });
    (ngCortexService as any)._routeConfigMap = routeConfigMap;
}
