import { action, circular, excludedInJSON, immutable } from '../decorators';
import { config, history, utils, LEVEL_ENUM, Vertex, PackageJSON, App, Page, DataNode, View, Element, Directive, ExpressionNode, Logic, genFinalCode, ActionOptions, ACTION_MODE } from '..';
import { elementService, attributeService, eventService, directiveService } from '../../service/page';
import { LogicItem, evaluate } from '../logic/LogicItem';
import { traverse } from '../utils';

function generateBody(body: Attr) {
    if (body.expression) {
        traverse((current) => {
            delete current.node.editable;
            delete current.node.index;
            delete current.node.memberIdentifierSchema;
        }, { node: body.expression as any }, { mode: 'anyObject' });
    }
}

/**
 * 前端组件属性
 * 纯组件属性，不包含事件、指令等
 * @examples
 * - 纯字符串：color="primary"
 * - 静态表达式：:min="23"
 * - 静态表达式：:data="[{ text: '选项1', value: '选项1' }, { text: '选项2', value: '选项2' }]"
 * - 动态表达式：:expr="$utils.LowerCase(a + b)"
 */
export class Attr extends Vertex {
    /**
     * 概念类型
     */
    @immutable()
    public readonly level: LEVEL_ENUM = LEVEL_ENUM.attr;
    /**
     * 属性类型
     */
    @immutable()
    public readonly type: 'string' | 'static' | 'dynamic' = 'string';
    /**
     * 属性 Id
     */
    @immutable()
    public readonly id: string;
    /**
     * 属性名
     */
    @immutable()
    public readonly name: string;
    /**
     * 属性原始值
     * 用户输入框中填入的值，同样也是 HTML 中 "" 中间的字符串
     * 和 ASTElement 中的有区别
     */
    @immutable()
    public readonly value: string;
    /**
     * model 是否开启
     */
    @immutable()
    public readonly model?: boolean = false;
    /**
     * sync 是否开启
     */
    @immutable()
    public readonly sync?: boolean = false;
    /**
     * 关联的动态表达式
     */
    @immutable()
    public readonly expression?: ExpressionNode;
    /**
     * 所属元素 Id
     */
    @immutable()
    public readonly elementId: string;
    /**
     * 所属元素
     */
    @circular()
    @immutable()
    public readonly element: Element;
    /**
     * @param source 需要合并的部分参数
     */
    constructor(source?: Partial<Attr>) {
        super();
        source && this.assign(source);
    }
    /**
     * 转换成 Vue 的模板格式
     */
    toVue(placeholder?: any, finalCode?: boolean) {
        let value = this.value;
        if (this.expression && this.type === 'dynamic') {
            value = evaluate(this.expression, finalCode);
            value && (value = genFinalCode(value, finalCode));
        }
        if (value !== undefined && value !== null && typeof value === 'string') {
            value = value.replace(/"/g, '\'');
        }

        if (this.type === 'string') {
            return value !== undefined && value !== null ? `${this.name}="${value}"` : '';
        } else {
            return value !== undefined && value !== null && value !== '' ? `:${this.name}${this.sync ? '.sync' : ''}="${value}"` : '';
        }
    }
    /**
     * 从后端 JSON 生成规范的 Attr 对象
     */
    public static from(source: any, element: Element) {
        const attr = new Attr(source);
        attr.assign({ element });
        return attr;
    }
    /**
     * 添加组件属性
     */
    @action('添加组件属性')
    async create(none?: void, actionOptions?: ActionOptions) {
        if (actionOptions?.actionMode === ACTION_MODE.local) {
            this.element?.view?.emit('local-change');
            return this;
        }

        config.defaultApp?.emit('saving');

        if (actionOptions?.actionMode !== ACTION_MODE.undoRedo) {
            const body = this.toJSON();
            generateBody(body);
            utils.logger.debug('添加组件属性', body);

            const result: Attr = await attributeService.create({
                headers: {
                    appId: config.defaultApp?.id,
                    operationAction: actionOptions?.actionName || 'Attr.create',
                    operationDesc: actionOptions?.actionDesc || `添加组件"${this.element.tag}"属性"${this.name}"`,
                },
                body,
            });
            this.assign({ id: result });
        }

        this.element?.view?.emit('change');
        await config.defaultApp?.history.load();
        config.defaultApp?.emit('saved');
        return this;
    }
    /**
     * 删除组件属性
     */
    @action('删除组件属性')
    async delete(none?: void, actionOptions?: ActionOptions) {
        if (actionOptions?.actionMode === ACTION_MODE.local) {
            const index = this.element.attrList.indexOf(this);
            ~index && this.element.attrList.splice(index, 1);
            this.element?.view?.emit('local-change');
            return this;
        }

        config.defaultApp?.emit('saving');

        if (actionOptions?.actionMode !== ACTION_MODE.undoRedo) {
            if (this.id) {
                try {
                    await attributeService.delete({
                        headers: {
                            appId: config.defaultApp?.id,
                            operationAction: actionOptions?.actionName || 'Attr.delete',
                            operationDesc: actionOptions?.actionDesc || `删除组件"${this.element.tag}"属性"${this.name}"`,
                        },
                        query: {
                            id: this.id,
                        },
                    });
                } catch (err) {
                    await config.defaultApp?.history.load();
                    throw err;
                }
            }
        }
        const index = this.element.attrList.indexOf(this);
        ~index && this.element.attrList.splice(index, 1);
        this.destroy();

        this.element?.view?.emit('change');
        await config.defaultApp?.history.load();
        config.defaultApp?.emit('saved');
    }
    /**
     * 修改组件属性
     */
    @action('修改组件属性')
    async update(data?: Attr, actionOptions?: ActionOptions) {
        config.defaultApp?.emit('saving');

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

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

export default Attr;
