import { excludedInJSON, immutable } from '../decorators';
import { LEVEL_ENUM, Vertex, Service, WebService, MicroService, SERVICE_TYPE, utils, History, updateGenericTypeList } from '..';
import appService from '../../service/app';

interface AppLoadAllOptions {
    asyncLoadServicesDetail?: boolean,
    asyncLoadViewsDetail?: boolean,
}

/**
 * 应用类
 * @example OA
 * @category App
 */
export class App extends Vertex {
    /**
     * 概念类型
     */
    @immutable()
    public readonly level: LEVEL_ENUM = LEVEL_ENUM.app;
    /**
     * App Id
     */
    @immutable()
    public readonly id: string = undefined;
    /**
     * Alias of id
     */
    @immutable()
    public readonly appId: string = undefined;
    /**
     * 应用标识
     */
    @immutable()
    public readonly name: string = undefined;
    /**
     * 应用标题
     */
    @immutable()
    public readonly title: string = undefined;
    /**
     * 应用图标
     */
    @immutable()
    public readonly icon: string = undefined;
    /**
     * 应用描述
     */
    @immutable()
    public readonly description: string = undefined;
    /**
     * 域名
     */
    @immutable()
    public readonly dnsAddr: string = undefined;
    /**
     * 官方类型
     */
    @immutable()
    public readonly officialType: 'offical' | 'nonofficial';
    /**
     * IDE 版本
     */
    @immutable()
    public readonly ideVersion: string = undefined;
    /**
     * 轻舟项目 Id
     */
    @immutable()
    public readonly projectId: string = undefined;
    /**
     * 租户 Id
     */
    @immutable()
    public readonly tenantId: string = undefined;
    /**
     * 租户 Id
     */
    @immutable()
    public readonly userGroupId: string = undefined;

    /**
     * h5 pc type
     */
    @immutable()
    public readonly scope: 'pc' | 'h5' = undefined;

    /**
     * 发布状态
     */
    @immutable()
    public readonly deploying: string = undefined;
    /**
     * 发布 Id，用于获取CICD详情
     */
    @immutable()
    public readonly deploymentId: string = undefined;
    /**
     * 应用下的服务
     */
    @immutable()
    public readonly services: Array<Service> = [];
    /**
     * 首个 Web 服务
     * 后续 App 创建不了多个模块了，默认处理该服务
     */
    @immutable()
    public readonly firstWebService: WebService = undefined;
    /**
     * 首个 Web 服务
     * 后续 App 创建不了多个模块了，默认处理该服务
     */
    @immutable()
    public readonly firstMicroService: MicroService = undefined;
    /**
     * 历史记录
     * 用于处理撤销重做
     */
    @immutable()
    @excludedInJSON()
    public readonly history: History = undefined;
    /**
     * @param source 需要合并的部分参数
     */
    constructor(source?: Partial<App>) {
        super();
        source && this.assign(source);

        this.history = new History({ app: this });
    }
    /**
     * 加载详情并同步 envList 信息
     * @requires this.id
     */
    async loadEnvList(appDetail: any) {
        if (!appDetail) {
            appDetail = await appService.loadApp({
                query: {
                    id: this.id,
                },
            });
        }
        const { envList } = appDetail.services
            .find((service: any) => service.type === 'microService') || {};
        this.assign({ envList });
    }
    /**
     * 加载详情并同步 App 信息
     * @requires this.id
     */
    async load() {
        const result = await appService.loadApp({
            query: {
                id: this.id,
            },
        });
        await this.loadEnvList(result);
        delete result.services;
        this.assign(result);
        return this;
    }
    /**
     * 加载 App 下的所有服务
     */
    async loadServices() {
        const result = await appService.loadServices({
            query: {
                appId: this.id,
            },
        });

        const services = result.map((service: Service) => {
            if (service.type === SERVICE_TYPE.web) {
                service = WebService.from(service, this);
                if (!this.firstWebService)
                    this.assign({ firstWebService: service as WebService });
            } else {
                service = MicroService.from(service, this);
                if (!this.firstMicroService)
                    this.assign({ firstMicroService: service as MicroService });
            }
            service.assign({ app: this });
            return service;
        });

        await this.firstWebService.loadPackageInfo();
        this.assign({ services });
        return services;
    }
    /**
     * 加载页面和逻辑详情
     */
    async _loadViewsDetail() {
        const tasks: Array<Promise<any>> = [];
        this.firstWebService.pages.forEach((page) => {
            utils.traverse((current) => {
                if (current.node.level === 'view') {
                    const view = current.node;
                    view.$def.logics = []; // load syncDef 不会添加重复逻辑
                    tasks.push(view.load());
                }
            }, { node: page.rootView });
        });
        // 自动 load，不用 await
        await Promise.all(tasks);
        this.emit('loadViewsDetail');
    }
    /**
     * 需要先调用 loadServices 之后再调用此函数
     * 加载 App 下的服务具体内容
     */
    private async _loadServicesDetail(options: AppLoadAllOptions = {
        asyncLoadServicesDetail: false,
        asyncLoadViewsDetail: false,
    }) {
        // 前端许多功能依赖后端数据，所以必须先 load 后端服务
        // loadMicroService
        await Promise.all([
            this.firstMicroService.loadEntities(),
            this.firstMicroService.loadStructures(),
            this.firstMicroService.loadEnums(),
        ]);
        // interfaces params 的 children 依赖 entities 和 structures，所以必须拆成两个 Promise
        await Promise.all([
            this.firstMicroService.loadInterfaces(),
            this.firstMicroService.loadProcesses(),
        ]);
        // loadWebService
        await this.firstWebService.loadPages();
        if (options.asyncLoadViewsDetail)
            this._loadViewsDetail();
        else
            await this._loadViewsDetail();

        this.emit('loadedAll');
    }
    /**
     * 加载 App 下所有子节点
     * @example
     * const app = new App({ id: appId });
     * await app.loadAll();
     */
    async loadAll(options: AppLoadAllOptions = {
        asyncLoadServicesDetail: false,
        asyncLoadViewsDetail: false,
    }) {
        await Promise.all([
            this.load(),
            this.loadServices(),
            updateGenericTypeList(),
        ]);

        if (options.asyncLoadServicesDetail)
            this._loadServicesDetail(options);
        else
            await this._loadServicesDetail(options);
    }
    /**
     * 从后端 JSON 生成规范的 App 对象
     * @param source JSON
     */
    from(source: any) {
        return new App(source);
    }
}

export default App;
