import { immutable, circular, action, excludedInJSON } from '../decorators';
import { Logic, config, LEVEL_ENUM, Vertex, App, Page, DataNode, GlobalLogicNode, Schema, Entity, EntityProperty, Structure, Enum, EnumItem, Interface, dataTypesMap, generator, vertexsMap, Process, EntityIndex } from '..';
import appService from '../../service/app';
import pageService from '../../service/page';
import webFileService from '../../service/webFile';
import processService from '../../service/process';
import { entityService, enumService, interfaceService, structureService } from '../../service/data';
import { convert2RefType } from '../data/dataTypeUtils';
import { updateDataTypeList } from '../data/dataTypes';
import { throttle } from 'lodash';

export enum SERVICE_TYPE {
    web = 'web',
    micro = 'micro',
}

export interface AssetsInfo {
    js: Array<string>,
    css: Array<string>,
    names?: Array<string>,
}

interface InterfaceNameMap<T> {
    [id: string]: T;
}

let vertexIdToName: { [pro: string]: string };

/**
 * 服务（模块）类
 */
export class Service extends Vertex {
    /**
     * 概念类型
     */
    @immutable()
    public readonly level: LEVEL_ENUM = LEVEL_ENUM.service;
    /**
     * 服务类型
     */
    @immutable()
    public readonly type: SERVICE_TYPE = undefined;
    /**
     * Service Id
     */
    @immutable()
    public readonly id: string = undefined;
    /**
     * 前端服务标识
     */
    @immutable()
    public readonly name: string = undefined;
    /**
     * 前端服务标题
     */
    @immutable()
    public readonly title: string = undefined;
    /**
     * 服务图标
     */
    @immutable()
    public readonly icon: string = undefined;
    /**
     * App 的 Id
     */
    @immutable()
    public readonly appId: string = undefined;
    /**
     * 数据节点
     */
    @immutable()
    public readonly data: DataNode = undefined;
    /**
     * 接口列表
     */
    @immutable()
    public readonly interfaces: Array<Interface> = [];
    /**
     * 逻辑节点 http://doc.hz.netease.com/pages/viewpage.action?pageId=310549021
     */
    @immutable()
    public readonly globalLogic: GlobalLogicNode = undefined;
    /**
     * 所在的 App
     * 父级引用，便于在树状结构中查找
     */
    @circular()
    @immutable()
    public readonly app: App = undefined;
    /**
     * 节点是否为展开状态
     * 前端 UI 状态
     */
    @excludedInJSON()
    public expanded: boolean = true;
    /**
     * @param source 需要合并的部分参数
     */
    constructor(source?: Partial<Service>) {
        super();
        this.data = new DataNode({ service: this });
        this.globalLogic = new GlobalLogicNode({ service: this });
        source && this.assign(source);
    }

    async loadStructures(): Promise<Array<Structure>> {
        const structuresOrigin: Array<Structure> = await structureService.loadList({
            query: {
                id: this.id,
            },
        });
        const structures: Array<Structure> = [];
        structuresOrigin.forEach((item) => {
            const structureNode = Structure.from(item, this);
            if (structureNode.serviceType === 'external' && structureNode.category) {
                this.globalLogic.addCategoryStructure(structureNode);
            } else {
                structures.push(structureNode);
            }
        });
        updateDataTypeList();

        this.data.assign({ structures });
        this.globalLogic.assign({ structures });
        return structures;
    }

    async syncStructures() {
        // this.data.structures.forEach((structure) => {
        //     delete dataTypesMap[structure.schemaRef];
        // });
        // const oldStructuresMap: Map<string, Structure> = new Map();
        // this.data.structures.forEach((structure, index) => oldStructuresMap.set(structure.id, structure));

        const newStructures: Array<Structure> = await structureService.loadList({
            query: {
                id: this.id,
            },
        });
        this.globalLogic.syncStructures(newStructures);

        // newStructures.forEach((item, index) => {
        //     const newStructure = Structure.from(item, this);
        //     if (oldStructuresMap.has(newStructure.id)) {
        //         const oldIndex = this.data.structures.indexOf(oldStructuresMap.get(newStructure.id));
        //         this.data.structures.splice(oldIndex, 1, newStructure);
        //         oldStructuresMap.delete(newStructure.id);
        //     } else {
        //         this.data.structures.push(newStructure);
        //     }
        // });
        // // Map 直接 forEach 会有问题
        // const oldRemains = Array.from(oldStructuresMap, ([id, index]) => ({ id, index }));
        // oldRemains.forEach((item) => {
        //     const structure = oldStructuresMap.get(item.id);
        //     delete dataTypesMap[structure.schemaRef];
        //     const oldIndex = this.data.structures.indexOf(structure);
        //     this.data.structures.splice(oldIndex, 1);
        //     structure.destroy();
        // });
        // updateDataTypeList();
        // this.globalLogic.assign({ structures: this.data.structures });
        return this.data.structures;
    }
    /**
     * 加载服务所有的接口列表
     */
    async loadInterfaces() {
        const RESOLVER_NAMES = ['getAll', 'get', 'create', 'update', 'delete', 'count', 'import', 'export', 'batchDelete', 'batchCreate', 'batchUpdate'];

        let interfaces: Array<Interface> = await interfaceService.loadList({
            query: {
                serviceId: this.id,
            },
        });

        interfaces = interfaces.map((item) => {
            item = Interface.from(item, this);
            if (item.logic && item.logic.entityId) {
                const entity = Entity.getVertexByRef(item.logic.entityId) as Entity;
                if (entity)
                    entity.assign({ resolvers: entity.resolvers || [] });
                const resolverName = RESOLVER_NAMES.find((name) => item.name.startsWith(name));
                if (entity)
                    entity.resolvers.push({
                        level: 'resolver',
                        name: resolverName,
                        interface: item,
                        entity,
                        isLeaf: true,
                    });
            }
            return item;
        });
        this.assign({ interfaces });
        return interfaces;
    }
}

/**
 * 前端服务（模块）类
 */
export class WebService extends Service {
    /**
     * 服务类型
     */
    @immutable()
    public readonly type: SERVICE_TYPE = SERVICE_TYPE.web;
    /**
     * 服务状态
     */
    @immutable()
    public readonly status: 'UNPUBLISHED' | 'PUBLISHED' | 'REPOSITORYCREATED' | 'FSSTARTED' | 'APPINIT' | 'CODESYNCED' | 'CICDCREATED' = undefined;
    /**
     * 是否为官方服务
     */
    @immutable()
    public readonly officialType: 'official' | 'nonofficial' = undefined;
    @immutable()
    public readonly dnsAddr: string = undefined;
    /**
     * packageJSON 的缩减版
     */
    @immutable()
    public readonly packageInfo: {
        template: { name: string, version: string },
        ui: { name: string, version: string },
        componentDependencies: { [name: string]: string },
        themeVariables: { [name: string]: string },
    } = undefined;
    /**
     * 其他配置
     */
    @immutable()
    public readonly config: string = undefined;
    @immutable()
    public readonly api: {
        features: { [name: string]: boolean };
        builtInFunctions: { [name: string]: any };
    } = undefined;
    /**
     * 前端页面
     */
    @immutable()
    public readonly pages: Array<Page> = [];
    /**
     * @param source 需要合并的部分参数
     */
    constructor(source?: Partial<WebService>) {
        super();
        source && this.assign(source);

        this.on('pageTreeChange', throttle(
            this._onPageTreeChange.bind(this),
            config.throttleWait,
            { leading: true, trailing: true },
        ));
        this.on('vertexIdToNameChange', () => {
            this.app.firstMicroService.emit('vertexIdToNameChange');
        });
    }

    async load() {
        const result = await appService.loadWebService({
            query: {
                appId: this.id,
            },
        });
        // result.id = result.appId;

        this.assign(result);
        return this;
    }

    async loadPackageInfo() {
        try {
            const res = await webFileService.loadFile({
                query: {
                    serviceId: this.id,
                    path: `package.partial.json`,
                },
            });
            if (res.content) {
                this.assign({ packageInfo: JSON.parse(res.content) });
            }
        } catch (e) { } finally {
            if (!this.packageInfo) {
                this.assign({
                    packageInfo: {
                        template: {
                            name: 'cloud-admin-template',
                            version: '0.2.23',
                        },
                        ui: {
                            name: 'cloud-ui.vusion',
                            version: '0.11.30',
                        },
                        componentDependencies: {
                            'lcap-login': '0.3.5',
                        },
                        themeVariables: {},
                    },
                });
            }
        }
    }

    async savePackageInfo() {
        const serviceId = this.id;
        const path = `package.partial.json`;

        const promise = webFileService.saveFile({
            body: {
                serviceId,
                type: 'config',
                path,
                content: JSON.stringify(this.packageInfo),
            },
        });

        generator.saveLastModified(serviceId, path);
        return promise;
    }
    /**
     * 根据 packageInfo 的信息生成用于 load 的 assetsInfo
     * 在环境中需要 basic 和 custom 分开 load，否则容易报错
     * @param prefix
     */
    genAllAssetsInfo(prefix: string) {
        const packageInfo = this.packageInfo;
        const scope = this.app.scope;
        const result: {
            basic: AssetsInfo,
            custom?: AssetsInfo,
        } = {
            basic: {
                js: [
                    `${prefix}/packages/vue@2/dist/vue.min.js`,
                    // `${prefix}/packages/vue-router@3/dist/vue-router.min.js`,

                    scope === 'h5' ? `${prefix}/packages/@lcap/mobile-ui@${packageInfo.ui.version}/dist-theme/index.js` : `${prefix}/packages/cloud-ui.vusion@${packageInfo.ui.version}/dist-theme/index.js`,

                    scope === 'h5' ? `${prefix}/packages/@lcap/mobile-template@${packageInfo.template.version}/cloudAdminDesigner.umd.min.js` : `${prefix}/packages/lcap-pc-template@${packageInfo.template.version}/cloudAdminDesigner.umd.min.js`,
                ],
                css: [
                    scope === 'h5' ? `${prefix}/packages/@lcap/mobile-ui@${packageInfo.ui.version}/dist-theme/index.css` : `${prefix}/packages/cloud-ui.vusion@${packageInfo.ui.version}/dist-theme/index.css`,

                    scope === 'h5' ? `${prefix}/packages/@lcap/mobile-template@${packageInfo.template.version}/cloudAdminDesigner.css` : `${prefix}/packages/lcap-pc-template@${packageInfo.template.version}/cloudAdminDesigner.css`,
                ],
            },
            custom: {
                js: [],
                css: [],
                names: [],
            },
        };

        Object.keys(packageInfo.componentDependencies).forEach((name) => {
            const version = packageInfo.componentDependencies[name].replace(/^[^0-9]+/, '');

            result.custom.js.push(`${prefix}/packages/${name}@${version}/dist-theme/index.js`);
            result.custom.css.push(`${prefix}/packages/${name}@${version}/dist-theme/index.css`);
            result.custom.names.push(name);
        });

        return result;
    }

    genThemeCSS() {
        const themeVariables: { [name: string]: string } = this.packageInfo.themeVariables || {};

        const keys = Object.keys(themeVariables);
        if (!keys.length)
            return;

        return `:root {
${Object.keys(themeVariables).map((key) => `    ${key}: ${themeVariables[key]};`).join('\n')}
}
`;
    }

    async loadPages() {
        const result: Array<Page> = await pageService.loadPages({
            query: {
                serviceId: this.id,
            },
            config: {
                mock: config.mock,
            },
        });
        const pages = result.map((page) => Page.from(page, this));
        this.assign({ pages });
        return pages;
    }

    @action('添加页面')
    async addPage(page: Page) {
        page.assign({ serviceId: this.id });
        await page.create(undefined, undefined, async () => {
            page.assign({ service: this });
            this.pages.unshift(page);
        });
        return page;
    }

    @action('添加页面')
    async importPage(page: Page) {
        page.assign({ serviceId: this.id });
        await page.importPage(undefined, undefined, async () => {
            page.assign({ service: this });
            this.pages.unshift(page);
        });
        return page;
    }

    private _onPageTreeChange() {
        generator.savePageTreeCache(this);
    }

    /**
     * 从后端 JSON 生成规范的 WebService 对象
     * @param source JSON
     * @param app 父级 App
     */
    public static from(source: any, app: App) {
        source.app = app;

        // if (!source.packageInfo) {
        //     source.packageInfo = {
        //         template: {
        //             name: 'cloud-admin-template',
        //             version: '0.2.22',
        //         },
        //         ui: {
        //             name: 'cloud-ui.vusion',
        //             version: '0.11.23',
        //         },
        //         componentDependencies: {
        //             'lcap-login': '^0.3.3',
        //         },
        //     };
        // } else if (typeof source.packageInfo === 'string')
        //     source.packageInfo = JSON.parse(source.packageInfo);
        return new WebService(source);
    }
}

/**
 * 后端服务（模块）类
 */
export class MicroService extends Service {
    /**
     * 服务类型
     */
    @immutable()
    public readonly type: SERVICE_TYPE = SERVICE_TYPE.micro;
    /**
     * 包名
     */
    @immutable()
    public readonly packageName: string;
    /**
     * 服务端口
     */
    @immutable()
    public readonly serverPort: number;
    /**
     * 接口列表
     */
    @immutable()
    public readonly interfaces: Array<Interface> = [];
    /**
     * 流程列表
     */
    @immutable()
    public readonly processes: Array<Process> = [];
    // logics: { [name]: Logic };
    /**
     * @param source 需要合并的部分参数
     */
    constructor(source?: Partial<MicroService>) {
        super();
        source && this.assign(source);

        this.on('dataTypesChange', throttle(
            this._onDataTypesChange.bind(this),
            config.throttleWait,
            { leading: true, trailing: true },
        ));
        this.on('enumsChange', throttle(
            this._onEnumsChange.bind(this),
            config.throttleWait,
            { leading: true, trailing: true },
        ));
        this.on('interfacesChange', throttle(
            this._onInterfacesChange.bind(this),
            config.throttleWait,
            { leading: true, trailing: true },
        ));

        this._onVertexIdToNameChange = throttle(
            this._onVertexIdToNameChange,
            config.throttleWait,
            { leading: true, trailing: true },
        );
        this.on('vertexIdToNameChange', this._onVertexIdToNameChange.bind(this));

        this._saveVertexIdToName = throttle(
            async () => {
                await generator.saveVertexIdToName(this.app);
                vertexIdToName = generator.getVertexIdToName();
            },
            config.throttleWait,
            { leading: true, trailing: true },
        );
    }

    private async _saveVertexIdToName() { }

    async loadEntities() {
        const entities: Array<Entity> = await entityService.loadList({
            query: {
                microServiceId: this.id,
            },
        });
        entities.forEach((item, index) => {
            convert2RefType(item);
            entities[index] = item = new Entity(item);
            item.assign({
                dataNode: this.data,
                schemaRef: `#/${this.id}/${item.id}`,
                // schemaNameRef: `#/${this.name}/entities/${entity.name}`
            });

            const lastVersionRef = item.lastVersionDef && item.lastVersionDef.properties || {};

            const propertyList = item.propertyList as Array<EntityProperty>;
            propertyList.sort((a, b) => (a as any)._posIndex - (b as any)._posIndex);
            propertyList.forEach((property, index) => {
                property = propertyList[index] = new EntityProperty(property);
                if (lastVersionRef[property.name]) {
                    property.assign({ lastVersion: convert2RefType(lastVersionRef[property.name]) });
                }
                property.assign({ root: item });
            });
            item.assign({ resolvers: [] });

            const indexList = item.indexList as Array<EntityIndex>;
            indexList.forEach((indexItem, index) => {
                indexItem = indexList[index] = new EntityIndex(indexItem);
                indexItem.assign({ root: item });
            });

            dataTypesMap[item.schemaRef] = item as Schema;
        });
        updateDataTypeList();

        this.data.assign({ entities });

        this.mountResolverOnEntity();
        return entities;
    }

    /**
     * 添加实体
     * @param name 实体名称
     */
    async addEntity(name: string): Promise<Entity>;
    /**
     * 添加实体
     * @param entityOptions 实体参数
     */
    async addEntity(entityOptions: Partial<Entity>): Promise<Entity>;
    /**
     * 添加实体
     * @param entity 已有的实体实例
     */
    async addEntity(entity: Entity): Promise<Entity>;
    async addEntity(name: string | Partial<Entity> | Entity) {
        const entityOptions = {
            dataNode: this.data,
            serviceId: this.id,
            serviceType: this.type,
        };
        let entity: Entity;
        if (typeof name === 'string') {
            entity = new Entity(Object.assign(entityOptions, {
                name,
            }));
        } else if (name instanceof Entity) {
            entity = name;
        } else {
            entity = new Entity(Object.assign(entityOptions, name));
        }

        entity.loading = true;
        try {
            await entity.create();
            entity.loading = false;
            if (!this.data.entities.includes(entity))
                this.data.entities.push(entity);

            entity.propertyList.forEach((property) => property.loading = false);
            this.globalLogic.syncEntityLogics();
        } catch (err) {
            const index = this.data.entities.indexOf(entity);
            ~index && this.data.entities.splice(index, 1);
        }

        return entity;
    }

    /**
     * 删除实体
     * @param name 实体名称
     */
    async removeEntity(name: string): Promise<void>;
    /**
     * 删除实体
     * @param id 实体 Id
     */
    async removeEntity(id: string): Promise<void>;
    /**
     * 删除实体
     * @param entity 已有的实体实例
     */
    async removeEntity(entity: Entity): Promise<void>;
    async removeEntity(name: string | Entity) {
        let entity: Entity;
        if (typeof name === 'string') {
            entity = this.data.entities.find((item) => item.name === name || item.id === name);
            if (!entity)
                throw new Error('找不到实体 ' + name);
        } else {
            entity = name;
        }

        entity.loading = true;
        await entity.delete();
        entity.loading = false;
        this.globalLogic.syncEntityLogics();
    }

    /**
     * 加载所有枚举
     */
    async loadEnums() {
        const enums: Array<Enum> = await enumService.loadList({
            query: {
                microServiceId: this.id,
            },
        });

        enums.forEach((item, index) => {
            convert2RefType(item as any);
            enums[index] = item = new Enum(item);
            item.assign({
                dataNode: this.data,
                schemaRef: `#/${this.id}/${item.id}`,
                // schemaNameRef: `#/${this.name}/enums/${entity.name}`
            });

            item.enumItemList.forEach((enumItem, index) => {
                enumItem = item.enumItemList[index] = new EnumItem(enumItem);
                enumItem.assign({ root: item });
            });

            dataTypesMap[item.schemaRef] = item as any;
        });

        updateDataTypeList();

        this.data.assign({ enums });
        return enums;
    }
    /**
     * 加载所有流程
     */
    async loadProcesses() {
        const result: Array<Process> = await processService.loadAll({
            query: {
                serviceId: this.id,
            },
        });
        const processes = result.map((process) => Process.from(process, this));
        this.assign({ processes });
        this.mountResolverOnProcess();
        return processes;
    }

    mountResolverOnProcess() {
        this.globalLogic.assign({ processLogics: this.processes });
    }

    /**
     * 导入sql或其他需要同步entity
     */
    async syncEntities() {
        const entities: Array<Entity> = await entityService.loadList({
            query: {
                microServiceId: this.id,
            },
        });
        const newEntities: Array<Entity> = [];
        const syncEntities: Array<Entity> = [];
        entities.forEach((item, index) => {
            // 已存在的entity直接使用
            const tempEntity = <Entity>vertexsMap.get(item.id);
            if (tempEntity) {
                newEntities[index] = tempEntity;
            } else {
                convert2RefType(item);
                newEntities[index] = item = new Entity(item);
                item.assign({
                    dataNode: this.data,
                    schemaRef: `#/${this.id}/${item.id}`,
                });

                const propertyList = item.propertyList as Array<EntityProperty>;
                propertyList.sort((a, b) => (a as any)._posIndex - (b as any)._posIndex);
                propertyList.forEach((property, index) => {
                    property = propertyList[index] = new EntityProperty(property);
                    property.assign({ root: item });
                });
                item.assign({ resolvers: [] });
                const indexList = item.indexList as Array<EntityIndex>;
                indexList.forEach((indexItem, index) => {
                    indexItem = indexList[index] = new EntityIndex(indexItem);
                    indexItem.assign({ root: item });
                });
                dataTypesMap[item.schemaRef] = item as Schema;
                syncEntities.push(item);
            }
        });
        this.data.assign({ entities: newEntities });
        const tasks = syncEntities.map((entity) => Promise.all([
            // entity.syncStructures(),
            entity.syncInterfaces(),
        ]));
        await Promise.all(tasks).then(() => {
            updateDataTypeList();
            this._onDataTypesChange();
            this._saveVertexIdToName();
        });
        return newEntities;
    }
    async syncEntityPropertyVersion() {
        const entities: Array<Entity> = await entityService.loadList({
            query: {
                microServiceId: this.id,
            },
        });
        entities.forEach((item, index) => {
            const tempEntity = <Entity>vertexsMap.get(item.id);
            if (tempEntity) {
                tempEntity.assign({ lastVersionDef: item.lastVersionDef });
            }
            const lastVersionRef = item.lastVersionDef && item.lastVersionDef.properties || {};
            const propertyList = tempEntity.propertyList as Array<EntityProperty>;
            propertyList.forEach((property, index) => {
                property = <EntityProperty>vertexsMap.get(property.id);
                if (property && lastVersionRef[property.name]) {
                    property.assign({ lastVersion: convert2RefType(lastVersionRef[property.name]) });
                }
            });
        });
    }
    /**
     * 加载服务所有的接口列表
     */
    async loadInterfaces() {
        let interfaces: Array<Interface> = await interfaceService.loadList({
            query: {
                serviceId: this.id,
            },
            headers: {
                IdeVersion: 2.3,
            },
        });

        interfaces = interfaces.map((item) => Interface.from(item, this));

        interfaces.forEach((i) => {
            if (i.serviceType === 'export') {
                const logic = Vertex.getVertexByRef(i.logicId) as Logic;
                i.logic = logic;
            }
        });
        this.assign({ interfaces });
        this.mountResolverOnEntity();
        this.mountResolverOnInterface();
        // this.generateLogics();
        return interfaces;
    }
    mountResolverOnEntity() {
        const RESOLVER_NAMES = ['getAll', 'get', 'create', 'update', 'delete', 'count', 'import', 'export', 'batchDelete', 'batchCreate', 'batchUpdate'];
        if (this.interfaces.length && this.data.entities.length) {
            const entityLogics: Array<Entity> = [];
            this.interfaces.forEach((item) => {
                if (item.logic && item.logic.entityId) {
                    const entity = Entity.getVertexByRef(item.logic.entityId) as Entity;
                    if (entity)
                        entity.assign({ resolvers: entity.resolvers || [] });
                    const resolverName = RESOLVER_NAMES.find((name) => item.name.startsWith(name));
                    if (entity) {
                        const exist = entity.resolvers.find((r) => r.name === resolverName);
                        if (exist) {
                            exist.interface = item;
                            exist.entity = entity;
                        } else {
                            entity.resolvers.push({
                                level: 'resolver',
                                name: resolverName,
                                interface: item,
                                entity,
                                isLeaf: true,
                            });
                        }

                        item.entityId = entity.id;
                    }
                }
            });
            this.data.entities.forEach((entity) => {
                entityLogics.push(entity);
            });
            this.globalLogic.assign({ entityLogics });
        }
    }

    mountResolverOnInterface() {
        const globalLogics: Array<Interface> = [];
        const interfaces: Array<Interface> = [];
        const exportInterface: Array<Interface> = [];
        const exportLogicId: InterfaceNameMap<Interface> = {};
        // const categoryInterfaceMap = {};
        // const categoryProccessMap = {};
        this.interfaces.forEach((item) => {
            if (item.serviceType === 'micro') {
                globalLogics.push(item);
            } else if (item.serviceType === 'external') {
                const category = item.category;
                if (!category) {
                    interfaces.push(item);
                } else {
                    this.globalLogic.addCategoryInterface(item);
                    // if (!categoryInterfaceMap[category]) {
                    //     categoryInterfaceMap[category] = [];
                    // }
                    // categoryInterfaceMap[category].push(item);
                }
            } else if (item.serviceType === 'export') {
                exportInterface.push(item);
                exportLogicId[item.logicId] = item;
            }
        });
        // const categories = [];
        // Object.keys(categoryInterfaceMap).forEach((category) => {

        //     categories.push({
        //         level: LEVEL_ENUM.category,
        //         name: category,
        //         categoryInterfaces: categoryInterfaceMap[category],
        //         categoryStructures: [],
        //         isLeaf: false,
        //         expanded: false,
        //     });
        // });
        const exportLogicIdList = Object.keys(exportLogicId);
        this.interfaces.forEach((item) => {
            if (exportLogicIdList.includes(item.logicId)) {
                item.exportedInterface = exportLogicId[item.logicId];
            }
        });

        this.globalLogic.assign({ globalLogics, interfaces });
    }

    private _onDataTypesChange() {
        config.webFileCache && generator.saveDataTypesCache(this.app);
    }

    private _onEnumsChange() {
        config.webFileCache && generator.saveEnumsCache(this.app);
    }

    private _onInterfacesChange() {
        config.webFileCache && generator.saveApisCache(this.app);
        generator.saveInterfacesCache(this);
    }

    private async _onVertexIdToNameChange(id: string, name: string) {
        if (!config.webFileCache)
            return;

        if (id && name) {
            if (!vertexIdToName) {
                vertexIdToName = await generator.loadVertexIdToName(this.app);
                vertexIdToName = vertexIdToName || {};
            }
            if (vertexIdToName[id] === name)
                return;
        }
        // id 和 name 都没传时，跳过上一步的判断逻辑，直接更新
        if ((id && name) || (!id && !name)) {
            this._saveVertexIdToName();
        }
    }
    /**
     * 从后端 JSON 生成规范的 MicroService 对象
     * @param source JSON
     * @param app 父级 App
     */
    public static from(source: any, app: App) {
        source.app = app;
        return new MicroService(source);
    }
}

export default Service;
