import { immutable, circular, excludedInJSON, action } from '../decorators';
import {
    config, utils, LEVEL_ENUM, Vertex, DataNode, EnumItem, Service,
    convert2RefType, updateDataTypeList, ActionOptions,
} from '..';
import { enumService } from '../../service/data';
import { dataTypesMap } from './dataTypes';

/**
 * 枚举类型
 */
export class Enum extends Vertex {
    /**
     * 概念类型
     */
    @immutable()
    public readonly level: LEVEL_ENUM = LEVEL_ENUM.enum; // 后面换成 enum
    /**
     * 枚举 Id
     */
    @immutable()
    public readonly id: string = undefined;
    /**
     * dataTypes 中的唯一标识
     */
    @immutable()
    public readonly schemaRef: string = undefined;
    /**
     * 枚举名称
     */
    @immutable()
    public readonly name: string = undefined;
    /**
     * 枚举标题
     */
    @immutable()
    public readonly label: string = undefined;
    /**
     * 枚举描述
     */
    @immutable()
    public readonly description: string = undefined;
    /**
     * 枚举类型
     * @deprecated 兼容老版
     */
    @immutable()
    public readonly type: 'enum' = 'enum';
    /**
     * 枚举项
     */
    @immutable()
    public readonly enumItemList: Array<EnumItem> = [];
    /**
     * 所属服务 Id
     */
    @immutable()
    public readonly serviceId: string = undefined;
    /**
     * 所属服务类型
     */
    @immutable()
    public readonly serviceType: string = undefined;
    /**
     * 所属服务
     */
    @immutable()
    public readonly service: Service = undefined;
    /**
     * 父节点
     */
    @circular()
    @immutable()
    public readonly dataNode: DataNode = undefined;

    @excludedInJSON()
    public existingNames: Array<string> = [];
    /**
     * @param source 需要合并的部分参数
     */
    constructor(source?: Partial<Enum>) {
        super();
        source && this.assign(source);
    }
    /**
     * 添加枚举
     */
    @action('添加枚举')
    async create(none?: void, actionOptions?: ActionOptions) {
        const body = this.toJSON();
        utils.logger.debug('添加枚举', body);

        const result: Enum = await enumService.create({
            headers: {
                appId: config.defaultApp?.id,
                operationAction: actionOptions?.actionName || 'Enum.create',
                operationDesc: actionOptions?.actionDesc || `添加枚举"${this.name}"`,
            },
            body,
        });
        this.deepPick(result, ['id']);

        this.assign({
            schemaRef: `#/${this.dataNode.service.id}/${result.id}`,
        });
        dataTypesMap[this.schemaRef] = this;
        updateDataTypeList();
        this.dataNode.service.emit('dataTypesChange');
        this.dataNode.service.emit('enumsChange');
        this.dataNode.service.emit('vertexIdToNameChange', this.id, this.name);

        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) {
            await enumService.delete({
                headers: {
                    appId: config.defaultApp?.id,
                    operationAction: actionOptions?.actionName || 'Enum.delete',
                    operationDesc: actionOptions?.actionDesc || `删除枚举"${this.name}"`,
                },
                query: {
                    id: this.id,
                },
            });

            const index = this.dataNode.enums.indexOf(this);
            ~index && this.dataNode.enums.splice(index, 1);
            delete dataTypesMap[this.schemaRef];
        }

        updateDataTypeList();
        this.destroy();
        this.dataNode.service.emit('dataTypesChange');
        this.dataNode.service.emit('enumsChange');

        await config.defaultApp?.history.load();
        config.defaultApp?.emit('saved');
    }
    /**
     * 修改枚举
     */
    async update(
        none?: void, actionOptions?: ActionOptions, then?: () => Promise<any>,
    ) {
        config.defaultApp?.emit('saving');

        const body = this.toJSON('', ['enumItemList']);
        utils.logger.debug('修改枚举', body);
        const result: Enum = await enumService.update({
            headers: {
                appId: config.defaultApp?.id,
                operationAction: actionOptions?.actionName || 'Enum.update',
                operationDesc: actionOptions?.actionDesc || `修改枚举"${this.name}"`,
            },
            body,
        });
        this.plainAssign(result);

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

        updateDataTypeList();
        this.dataNode.service.emit('dataTypesChange');
        this.dataNode.service.emit('enumsChange');
        this.dataNode.service.emit('vertexIdToNameChange', this.id, this.name);
    }
    /**
     * 设置枚举标题
     * @param name 标题
     */
    @action('设置枚举标题')
    async setLabel(label: string) {
        const oldLabel = this.label;
        this.assign({ label });
        await this.update(undefined, {
            actionDesc: `设置枚举"${oldLabel}"的标题为"${label}"`,
        });
    }
    /**
     * 设置枚举描述
     * @param description 描述
     */
    @action('设置枚举描述')
    async setDescription(description: string) {
        this.assign({ description });
        await this.update(undefined, {
            actionDesc: `设置枚举"${this.name}"的描述为"${description}"`,
        });
    }
    /**
     * 从后端 JSON 生成规范的 Enum 对象
     */
    public static from(source: any, service: Service) {
        convert2RefType(source);
        const enumObject = new Enum(source);
        enumObject.assign({
            dataNode: service.data,
            schemaRef: `#/${service.id}/${enumObject.id}`,
        });

        const enumItemList = enumObject.enumItemList as Array<EnumItem>;
        enumItemList.forEach((property, index) => {
            property = enumItemList[index] = new EnumItem(property);
            property.assign({ root: enumObject });
        });

        dataTypesMap[enumObject.schemaRef] = enumObject;
        return enumObject;
    }
}

export default Enum;
