import { immutable, circular, action, excludedInJSON } from '../decorators';
import { config, history, LEVEL_ENUM, Vertex, MicroService, ProcessComponent, ProcessParam, ProcessReturn, ProcessProperty, ProcessInterface, vertexsMap, LogicItem, typeCheck, ActionOptions } from '..';
import processService from '../../service/process';

/**
 * 流程类
 */
export class Process extends Vertex {
    /**
     * 概念类型
     */
    @immutable()
    public readonly level: LEVEL_ENUM = LEVEL_ENUM.process;
    /**
     * 流程 Id
     */
    @immutable()
    public readonly id: string = undefined;
    /**
     * 流程名称
     */
    @immutable()
    public readonly name: string = undefined;
    /**
     * 流程标题
     */
    @immutable()
    public readonly title: string = undefined;
    /**
     * 流程描述
     */
    @immutable()
    public readonly description: string = undefined;
    /**
     * 流程组件集合
     */
    @immutable()
    public readonly childShapes: Array<ProcessComponent> = [];
    /**
     * 流程输入参数
     */
    @immutable()
    public readonly params: Array<any> = [];
    /**
     * 流程输出参数
     */
    @immutable()
    public readonly returns: Array<any> = [];
    /**
     * 流程内置参数
     */
    @immutable()
    public readonly properties: Array<any> = [];
    /**
     * 启动流程接口
     */
    @immutable()
    public readonly launchProcessInterface: ProcessInterface = undefined;
    /**
     * 后端服务类型
     */
    @immutable()
    public readonly serviceType: string = 'MICROSERVICE';
    /**
     * 所属后端服务 Id
     */
    @immutable()
    public readonly serviceId: string = undefined;
    /**
     * 所属后端服务
     */
    @circular()
    @immutable()
    public readonly service: MicroService = undefined;
    /**
     * 树组件的子节点字段
     */
    @excludedInJSON()
    @immutable()
    public readonly childrenField: string = 'childShapes';
    /**
     * 树组件的子节点字段
     */
    @excludedInJSON()
    @immutable()
    public readonly moreChildrenFields: Array<string> = ['params', 'returns', 'properties', 'interfaces'];
    /**
     * 已存在名称集合
     */
    @excludedInJSON()
    @immutable()
    public readonly existingNames: Array<string> = [];
    /**
     * 接口列表，单纯用于 tree 展示
     */
    @excludedInJSON()
    @immutable()
    public readonly interfaces: Array<ProcessInterface> = [];
    /**
     * @param source 需要合并的部分参数
     */
    constructor(source?: Partial<Process>) {
        super();
        this.assign(source);
    }
    /**
     * 添加流程
     */
    @action('添加流程')
    async create(none?: void, actionOptions?: ActionOptions) {
        config.defaultApp?.emit('saving');
        this.assign({ editing: false, loading: true });
        const body = this.toJSON();
        const result = await processService.add({
            headers: {
                appId: config.defaultApp?.id,
                operationAction: actionOptions?.actionName || 'Process.create',
                operationDesc: actionOptions?.actionDesc || `添加流程"${this.name}${this.title ? `(${this.title})` : ''}"`,
            },
            body,
        });
        this.assign(Process.from(result, this.service));
        this.service.emit('interfacesChange');

        await config.defaultApp?.history.load();
        config.defaultApp?.emit('saved');
        return this;
    }
    /**
     * 删除流程
     */
    @action('删除流程')
    async delete(none?: void, actionOptions?: ActionOptions) {
        config.defaultApp?.emit('saving');
        await processService.delete({
            headers: {
                appId: config.defaultApp?.id,
                operationAction: actionOptions?.actionName || 'Process.delete',
                operationDesc: actionOptions?.actionDesc || `删除流程"${this.name}${this.title ? `(${this.title})` : ''}"`,
            },
            path: {
                id: this.id,
            },
        });
        typeCheck.deleteByProcess(this.id);
        const index = this.service.processes.indexOf(this);
        ~index && this.service.processes.splice(index, 1);
        this.destroy();
        this.service.emit('interfacesChange');

        await config.defaultApp?.history.load();
        config.defaultApp?.emit('saved');
    }
    /**
     * 修改流程
     */
    async update(deep?: boolean, actionOptions?: ActionOptions, then?: () => Promise<any>) {
        config.defaultApp?.emit('saving');
        const body = this.toPlainJSON();
        const result = await processService.update({
            headers: {
                appId: config.defaultApp?.id,
                operationAction: actionOptions?.actionName || 'Process.update',
                operationDesc: actionOptions?.actionDesc || `修改流程"${this.name}${this.title ? `(${this.title})` : ''}"`,
            },
            body,
        });
        // 深度更新，需要同步更新 流程接口 和 UserTask 中的 完成任务接口
        if (deep && result) {
            const { childShapes = [], launchProcessInterface } = result;
            const { name, path, method } = launchProcessInterface;
            this.launchProcessInterface.assign({ name, path, method });
            this.launchProcessInterface.logic.assign({ name });
            this.childShapes.forEach((childShape) => {
                const { type, completeTaskInterface } = childShape;
                if (type === 'UserTask') {
                    const newUserTask = childShapes.find((cShape: ProcessComponent) => cShape.id === childShape.id);
                    const { name, path, method } = newUserTask.completeTaskInterface;
                    completeTaskInterface.assign({ name, path, method });
                    completeTaskInterface.logic.assign({ name });
                }
            });
            this.service.emit('interfacesChange');
        }

        await then?.();
        await config.defaultApp?.history.load();
        config.defaultApp?.emit('saved');
        return this;
    }
    /**
     * 批量修改流程组件
     */
    async batchComponents(body: any, actionOptions?: ActionOptions) {
        config.defaultApp?.emit('saving');
        const result = await processService.batchComponents({
            headers: {
                appId: config.defaultApp?.id,
                operationAction: actionOptions?.actionName || 'Process.batchComponents',
                operationDesc: `批量修改流程组件"${this.name}${this.title ? `(${this.title})` : ''}"`,
            },
            body,
        });
        let childShapes: Array<ProcessComponent> = [];
        this.childShapes.forEach((processComponent) => {
            const isDeleted = result.delete.find((node: any) => node.id === processComponent.id);
            if (isDeleted) {
                return;
            }

            const isUpdatedComp = result.update.find((node: any) => node.id === processComponent.id);
            if (isUpdatedComp) {
                childShapes.push(ProcessComponent.from(isUpdatedComp, this));
                return;
            }

            childShapes.push(processComponent);
        });
        childShapes = childShapes.concat((result.add).map((childShape: any) => ProcessComponent.from(childShape, this)));
        this.assign({ childShapes });
        this.service.emit('interfacesChange');
        this.checkType();
        await config.defaultApp?.history.load();
        config.defaultApp?.emit('saved');
    }
    /**
     * 流程级别的类型检查
     */
    async checkType() {
        const result = await processService.checkTypeProcess({
            path: { id: this.id },
        });
        typeCheck.deleteByProcess(this.id);
        typeCheck.pushAll(result);
        result.forEach((typeCheckResult: any) => {
            if (typeCheckResult.processComponentId) {
                const processComponent = vertexsMap.get(typeCheckResult.processComponentId);
                ProcessComponent.assignTypeCheckResult(processComponent as ProcessComponent, typeCheckResult);
            }
            if (typeCheckResult.logicId) {
                const logicItem = vertexsMap.get(typeCheckResult.id);
                LogicItem.assignTypeCheckResult(logicItem as LogicItem, typeCheckResult);
            }
        });
    }
    /**
     * 设置流程名称
     * @param name 名称
     */
    @action('设置流程名称')
    async setName(name: string) {
        const oldName = this.name;
        this.assign({ name });
        await this.update(true, {
            actionDesc: `设置流程"${oldName}"的名称为"${name}"`,
        });
    }
    /**
     * 设置流程标题
     * @param title 标题
     */
    @action('设置流程标题')
    async setTitle(title: string) {
        this.assign({ title });
        await this.update(false, {
            actionDesc: `设置流程"${this.name}"的标题为"${title}"`,
        });
    }
    /**
     * 设置流程描述
     * @param description 描述
     */
    @action('设置流程描述')
    async setDescription(description: string) {
        this.assign({ description });
        await this.update(false, {
            actionDesc: `设置流程"${this.name}"的描述为"${description}"`,
        });
    }
    /**
     * 从后端 JSON 生成规范的 Param 对象
     */
    public static from(source: any, service: MicroService) {
        const process = new Process(source);
        process.assign({ service });
        if (source) {
            const { params = [], returns = [], properties = [], childShapes = [], launchProcessInterface } = source;
            process.assign({
                params: params.map((param: any) => ProcessParam.from(param, process)),
                returns: returns.map((rn: any) => ProcessReturn.from(rn, process)),
                properties: properties.map((property: any) => ProcessProperty.from(property, process)),
                childShapes: childShapes.map((childShape: ProcessComponent) => ProcessComponent.from(childShape, process)),
            });
            if (launchProcessInterface) {
                const ifce = ProcessInterface.from(launchProcessInterface, process);
                process.assign({
                    launchProcessInterface: ifce,
                    interfaces: [ifce],
                });
            }
        }
        return process;
    }
}

export default Process;
