import { immutable, excludedInJSON, circular, action } from '../decorators';
import { config, history, utils, Vertex, LEVEL_ENUM, Interface, Param, Return, Variable, vertexsMap, typeCheck, ProcessComponent, ProcessInterface, Process, ActionOptions } from '..';
import { logicService, paramService } from '../../service/logic';
import View from '../page/View';
import { ExpressionNode, LogicItem, LogicNode } from './LogicItem';
import { convert2SchemaType } from '../data/dataTypeUtils';
import { isPlainObject, throttle } from 'lodash';

/**
 * 逻辑类
 */
export class Logic extends Vertex {
    /**
     * 概念类型
     */
    @immutable()
    public readonly level: LEVEL_ENUM = LEVEL_ENUM.logic;
    /**
     * Id
     */
    @immutable()
    public readonly id: string = undefined;
    /**
     * 名称
     */
    @immutable()
    public readonly name: string = undefined;
    /**
     * 描述
     */
    @immutable()
    public readonly description: string = undefined;
    /**
     * serviceId 或者 viewId
     */
    @immutable()
    public readonly moduleId: string = undefined;
    /**
     * 值为 view 或者 microService 或者 processComponent
     */
    @immutable()
    public readonly moduleType: 'view' | 'microService' | 'process' = undefined;
    /**
     * serviceId 或者 viewId
     */
    @immutable()
    public readonly serviceId: string = undefined;
    /**
     * 值为 view 或者 microService
     */
    @immutable()
    public readonly serviceType: 'view' | 'entity' | 'microService' = undefined;
    /**
     * 所属实体 Id
     */
    @immutable()
    public readonly entityId: string = undefined;
    /**
     *  输入参数列表
     */
    @immutable()
    public readonly params: Array<Param> = [];
    /**
     *  输出参数列表
     */
    @immutable()
    public readonly returns: Array<Return> = [];
    /**
     *  局部变量列表
     */
    @immutable()
    public readonly variables: Array<Variable> = [];
    /**
     * 逻辑体
     */
    @immutable()
    public readonly body: Array<LogicItem> = [];
    /**
     * 画布
     */
    @immutable()
    public readonly playgroundId: string = undefined;
    /**
     * 画布
     */
    @immutable()
    public readonly playground: Array<LogicItem> = [];
    /**
     * 接口
     */
    @circular()
    @immutable()
    public readonly interface: Interface = undefined;
    /**
     * 视图
     */
    @circular()
    @immutable()
    public readonly view: View = undefined;
    /**
     * 流程组件
     */
    @circular()
    @immutable()
    public readonly processComponent: ProcessComponent = undefined;
    /**
     * 流程组件
     */
    @circular()
    @immutable()
    public readonly process: Process = undefined;
    /**
     * 正在请求的 Promise
     * 前端 UI 状态
     */
    @excludedInJSON()
    public contentPromise: Promise<any> = undefined;
    /**
     * 树组件的子节点字段
     */
    @immutable()
    public readonly moreChildrenFields: Array<string> = ['params', 'returns', 'variables'];
    /**
     * @param source 需要合并的部分参数
     */
    constructor(source?: Partial<Logic>) {
        super();
        source && this.assign(source);

        this.on('change', throttle(
            this._onChange.bind(this),
            config.throttleWait,
            { leading: true, trailing: true },
        ));
    }
    /**
     * 添加逻辑
     */
    @action('添加逻辑')
    async create(none?: void, actionOptions?: ActionOptions) {
        config.defaultApp?.emit('saving');

        const body = this.toJSON();
        // body.params.forEach((param: any) => convert2SchemaType(param.schema));
        // body.returns.forEach((ret: any) => convert2SchemaType(ret.schema));
        // body.variables.forEach((variable: any) => convert2SchemaType(variable.schema));
        utils.logger.debug('添加逻辑', body);
        const result: Logic = await logicService.create({
            headers: {
                appId: config.defaultApp?.id,
                operationAction: actionOptions?.actionName || 'Logic.create',
                operationDesc: actionOptions?.actionDesc || `添加逻辑"${this.name}"`,
            },
            body,
        });
        this.deepPick(result, ['id']);
        this.pick(result, ['playgroundId']);
        this.assign({ body: result.body.map((logicNode) => LogicNode.from(logicNode, this, null)) });
        this.params.forEach((param) => {
            param.assign({ code: `ID_${param.id}` });
        });
        this.returns.forEach((returns) => {
            returns.assign({ code: `ID_${returns.id}` });
        });
        this.variables.forEach((variable) => {
            variable.assign({ code: `ID_${variable.id}` });
        });

        if (this.view) {
            this.view.page.service.emit('pageTreeChange');
            this.emit('change');
        }

        await config.defaultApp?.history.load();
        config.defaultApp?.emit('saved');
        return this;
    }
    /**
     * 删除逻辑
     */
    @action('删除逻辑')
    async delete(none?: void, actionOptions?: ActionOptions) {
        config.defaultApp?.emit('saving');

        if (this.id) {
            const body = this.toPlainJSON();
            if (this.view) {
                body.moduleId = this.view.id;
                body.moduleType = 'view';
            } else {
                body.moduleId = this.interface.service.id;
                body.moduleType = 'microService';
            }
            try {
                await logicService.delete({
                    headers: {
                        appId: config.defaultApp?.id,
                        operationAction: actionOptions?.actionName || 'Logic.delete',
                        operationDesc: actionOptions?.actionDesc || `删除逻辑"${this.name}"`,
                    },
                    body,
                });
            } catch (err) {
                await config.defaultApp?.history.load();
                throw err;
            }

            if (this.view) {
                const { logics } = this.view.$def;
                const pos = logics.indexOf(this);
                if (pos >= 0)
                    logics.splice(pos, 1);
            }

            [...this.body, ...this.playground].forEach((node) => {
                typeCheck.delete(node.id);
            });

            // const index =
            this.destroy();
            if (this.view) {
                this.view.page.service.emit('pageTreeChange');
                this.emit('change');
            }

            await config.defaultApp?.history.load();
            config.defaultApp?.emit('saved');
        } else {
            if (this.view) {
                const { logics } = this.view.$def;
                const pos = logics.indexOf(this);
                if (pos >= 0)
                    logics.splice(pos, 1);
            }
        }
    }
    /**
     * 修改逻辑
     */
    async update(none?: void, actionOptions?: ActionOptions, then?: () => Promise<any>) {
        config.defaultApp?.emit('saving');

        const body = this.toPlainJSON();
        utils.logger.debug('修改逻辑', body);
        const result = await logicService.update({
            headers: {
                appId: config.defaultApp?.id,
                operationAction: actionOptions?.actionName || 'Logic.update',
                operationDesc: actionOptions?.actionDesc || `修改逻辑"${this.name}"`,
            },
            body,
        });

        await then?.();
        await config.defaultApp?.history.load();
        config.defaultApp?.emit('saved');
        return this;
    }
    /**
     * 设置逻辑名称
     */
    @action('设置逻辑名称')
    async setName(name: string) {
        const oldName = this.name;
        this.assign({ name });
        await this.update(undefined, {
            actionDesc: `设置逻辑"${oldName}"的名称为"${name}"`,
        });

        if (this.view) {
            this.view.page.service.emit('pageTreeChange');
            this.emit('change');
        }
        return this;
    }
    /**
     * 设置逻辑描述
     */
    @action('设置逻辑描述')
    async setDescription(description: string) {
        this.assign({ description });
        await this.update(undefined, {
            actionDesc: `设置逻辑"${this.name}"的描述为"${description}"`,
        });

        if (this.view) {
            this.view.page.service.emit('pageTreeChange');
            this.emit('change');
        }
        return this;
    }

    _onChange() {
        this.view && this.view.emit('change');
    }
    /**
     * 按当前 id 加载逻辑数据
     */
    async load() {
        if (!this.id)
            return;

        // 如果有正在进行的 Promise，则直接返回它
        if (this.contentPromise)
            return this.contentPromise;

        return this.contentPromise = (async () => {
            try {
                const result = await logicService.loadDetail({
                    query: {
                        moduleId: this.moduleId,
                        moduleType: this.moduleType,
                        id: this.id,
                    },
                });
                const newLogic = Logic.from(result, this.interface || this.view || this.processComponent, this);
                this.assign(newLogic);
            } catch (err) {}
            return this;
        })().finally(() => this.contentPromise = undefined);
    }
    isContentLoaded() {
        return !!this.body.length;
    }
    /**
     *  批量添加参数
     */
    async addParamList(params: Array<Param>) {
        const body = params.map((param) => {
            param = param.toJSON();
            convert2SchemaType(param.schema);
            return param;
        });

        const results = await paramService.addList({
            body,
        });
        params.forEach((param, index) => param.pick(results[index], ['id']));
        this.params.push(...params);
    }
    /**
     * 添加逻辑节点
     * @param item
     */
    addItem(item: LogicItem, actionOptions?: ActionOptions) {
        if (isPlainObject(item)) {
            item = LogicItem.from(item, this, null);
        }
        // if (!this.children.includes(child)) {
        //     const index = child._posIndex === undefined ? this.children.length : child._posIndex;
        //     this.children.splice(index, 0, child);
        // }
        const parent = item.parentAttr ? vertexsMap.get(item.parentId) as LogicItem : undefined;
        return item.create({
            parent,
            parentId: item.parentId,
            parentAttr: item.parentAttr,
            _posIndex: item._posIndex,
            cache: false,
        }, actionOptions);
    }
    /**
     * 删除逻辑节点
     * @param item
     */
    removeItem(item: LogicItem, actionOptions?: ActionOptions) {
        if (isPlainObject(item)) {
            item = vertexsMap.get(item.id) as LogicItem;
        }
        return item.delete(undefined, actionOptions);
    }
    /**
     *
     * @param type
     * @returns
     */
    public findLogicItemByType(type: string) {
        let result: any;

        utils.traverse((current) => {
            if ((current.node as any).type === type)
                return result = current.node;
        }, { node: this }, {
            mode: 'anyObject',
            excludedKeySet: this.JSON_EXCLUDED_KEYS,
        });

        return result as LogicItem;
    }
    /**
     * 从后端 JSON 生成规范的 Logic 对象
     */
    public static from(source: any, parent: Interface | View | ProcessComponent | ProcessInterface, currentLogic?: Logic) {
        const logic = new Logic(source);
        currentLogic = currentLogic || logic;

        logic.assign({ params: logic.params.map((param) => Param.from(param, currentLogic)) });
        logic.assign({ returns: logic.returns.map((ret) => Return.from(ret, currentLogic)) });
        logic.assign({ variables: logic.variables.map((variable) => Variable.from(variable, currentLogic)) });

        logic.body.forEach((node, index) => {
            if (node.level === LEVEL_ENUM.logicNode)
                node = logic.body[index] = LogicNode.from(node, currentLogic, null);
            else if (node.level === LEVEL_ENUM.expressionNode)
                node = logic.body[index] = ExpressionNode.from(node, currentLogic, null);
        });
        logic.playground.forEach((node, index) => {
            if (node.level === LEVEL_ENUM.logicNode)
                node = logic.playground[index] = LogicNode.from(node, currentLogic, null);
            else if (node.level === LEVEL_ENUM.expressionNode)
                node = logic.playground[index] = ExpressionNode.from(node, currentLogic, null);
        });

        if (parent instanceof Interface)
            logic.assign({
                interface: parent,
                moduleType: 'microService',
                moduleId: parent.id,
            });
        else if (parent instanceof View)
            logic.assign({
                view: parent,
                moduleType: 'view',
                moduleId: parent.id,
            });
        else if (parent instanceof ProcessComponent)
            logic.assign({
                processComponent: parent,
                moduleType: 'process',
                moduleId: parent.id,
            });
        else if (parent instanceof ProcessInterface) {
            logic.assign({
                process: parent,
            });
        }

        return logic;
    }
}

export default Logic;
