// import stringify = require('json-stringify-safe');
import { action, circular, excludedInJSON, immutable } from '../decorators';
import { config, history, utils, LEVEL_ENUM, Vertex, PackageJSON, App, Page, DataNode, View, Element, ExpressionNode, Logic, genFinalCode, ActionOptions, ACTION_MODE } from '..';
import { elementService, attributeService, eventService, directiveService } from '../../service/page';
import * as babel from '@babel/core';
import { traverse } from '../utils';
import generate from '@babel/generator';
import { vertexsMap } from '../cacheData';
import Variable from '../logic/Variable';

/**
 * 前端组件事件
 */
export class Event extends Vertex {
    /**
     * 概念类型
     */
    @immutable()
    public readonly level: LEVEL_ENUM = LEVEL_ENUM.event;
    /**
     * 事件 Id
     */
    @immutable()
    public readonly id: string;
    /**
     * 事件名
     */
    @immutable()
    public readonly name: string;
    /**
     * 事件名称
     */
    @immutable()
    public readonly value: string;
    /**
     * 逻辑 Id
     */
    @immutable()
    public readonly logicId?: string = undefined;
    /**
     * 逻辑 Id
     */
    @immutable()
    public readonly logic?: string;
    /**
     * 所属元素 Id
     */
    @immutable()
    public readonly elementId: string;
    /**
     * 所属元素
     */
    @circular()
    @immutable()
    public readonly element: Element;
    /**
     * @param source 需要合并的部分参数
     */
    constructor(source?: Partial<Event>) {
        super();
        source && this.assign(source);
    }
    /**
     * 转换成 Vue 的模板格式
     */
    toVue(element?: Element, finalCode?: boolean) {
        let value = genFinalCode(this.value, finalCode); // 可能存在没有logicId但value是ID_**的情况
        if (this.logicId) {
            if (vertexsMap.get(this.logicId))
                value = vertexsMap.get(this.logicId).name;
        }
        if (value) {
            try {
                const expression = ((babel.parse(value, { filename: 'file.js' }) as babel.types.File)
                    .program.body[0] as babel.types.ExpressionStatement).expression;
                if (expression.type === 'Identifier' || expression.type === 'MemberExpression') {
                    const params = ['$event'];
                    let tempElement = element;
                    while (tempElement) {
                        if (tempElement.slotScope) {
                            params.push(tempElement.slotScope);
                        }
                        tempElement = tempElement.parent;
                    }
                    value = `${value}(${params.join(',')})`;
                } else if (expression.type === 'CallExpression') {
                    //
                } else {
                    // console.warn('[asl]', `event 中存在一些复杂表达式'${value}'，请在后续迭代中处理掉。`);
                }
            } catch (e) {
                console.warn('[asl]', `event 中'${value}'好像不是表达式。`);
            }
        }
        return value !== '' && value !== undefined && value !== null ? `@${this.name}="${value}"`: '';
    }
    /**
     * 从后端 JSON 生成规范的 Event 对象
     */
    public static from(source: any, element: Element) {
        const event = new Event(source);
        event.assign({ element });
        return event;
    }

    /**
     * 添加组件事件
     */
    @action('添加组件事件')
    async create(none?: void, actionOptions?: ActionOptions) {
        config.defaultApp?.emit('saving');

        if (actionOptions?.actionMode !== ACTION_MODE.undoRedo) {
            const body = this.toJSON();
            utils.logger.debug('添加组件事件', body);
            const result: Event = await eventService.create({
                headers: {
                    appId: config.defaultApp?.id,
                    operationAction: actionOptions?.actionName || 'Event.create',
                    operationDesc: actionOptions?.actionDesc || `添加组件"${this.element.tag}"事件"${this.name}"`,
                },
                body,
            });
            this.assign({ id: result.id });
        }

        // this.element.view && this.element.view.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 (actionOptions?.actionMode !== ACTION_MODE.undoRedo) {
            if (this.id) {
                try {
                    await eventService.delete({
                        headers: {
                            appId: config.defaultApp?.id,
                            operationAction: actionOptions?.actionName || 'Event.delete',
                            operationDesc: actionOptions?.actionDesc || `删除组件"${this.element.tag}"事件"${this.name}"`,
                        },
                        query: {
                            id: this.id,
                        },
                    });
                } catch(err) {
                    await config.defaultApp?.history.load();
                    throw err;
                }
            }
        }
        this.destroy();
        this.element.view && this.element.view.emit('change');
        await config.defaultApp?.history.load();
        config.defaultApp?.emit('saved');
    }
    /**
     * 修改组件事件
     */
    @action('修改组件事件')
    async update(data: Event, actionOptions?: ActionOptions) {
        config.defaultApp?.emit('saving');

        data && this.assign(data);
        if (actionOptions?.actionMode !== ACTION_MODE.undoRedo) {
            const body = this.toJSON();
            utils.logger.debug('修改组件事件', body);
            await eventService.update({
                headers: {
                    appId: config.defaultApp?.id,
                    operationAction: actionOptions?.actionName || 'Event.update',
                    operationDesc: actionOptions?.actionDesc || `修改组件"${this.element.tag}"事件"${this.name}"`,
                },
                body,
            });
        }

        this.element.view && this.element.view.emit('change');
        await config.defaultApp?.history.load();
        config.defaultApp?.emit('saved');
        return this;
    }
}

export default Event;
