import { immutable, circular, excludedInJSON, action } from '../decorators';
import {
    config, utils, LEVEL_ENUM, Vertex, Entity, Schema, Interface,
    convert2RefType, ActionOptions,
} from '..';
import { entityService } from '../../service/data';
import { getBasicTypeDefaultValue } from './basicTypes';
import { updateAllVariablesChildrenSchema } from '../logic/BaseVariable';
import { schemaService } from '../../service/common';

export interface Display {
    readonly inDetail: boolean;
    readonly inFilter: boolean;
    readonly inForm: boolean;
    readonly inTable: boolean;
}

/**
 * 实体属性类
 */
export class EntityProperty extends Vertex implements Schema {
    /**
     * 概念类型
     */
    @immutable()
    public level: LEVEL_ENUM = LEVEL_ENUM.property;
    /**
     * Id
     */
    @immutable()
    public readonly id: string = undefined;
    /**
     * 名称
     */
    @immutable()
    public readonly name: string = undefined;
    /**
     * 标题
     */
    @immutable()
    public readonly label: string = undefined;
    /**
     * 描述
     */
    @immutable()
    public readonly description: string = undefined;
    /**
     *  type
     */
    @immutable()
    public readonly type: string = undefined;
    /**
     * 数据格式
     */
    @immutable()
    public readonly format: string = undefined;
    /**
     * 是否必须
     */
    @immutable()
    public readonly required: boolean = undefined;
    /**
     * 数据类型
     */
    @immutable()
    public readonly $ref: string = undefined;
    /**
     * 数据结构Id
     */
    @immutable()
    public readonly $refId: string = undefined;
    /**
     * 引用关系（多对一、一对多等）
     */
    @immutable()
    public readonly relationship: string = undefined;
    /**
     * 规则
     */
    @immutable()
    public readonly rules: Array<string> = [];
    /**
     * UI 展示
     */
    @immutable()
    public readonly display: Display = {
        inDetail: true,
        inFilter: true,
        inForm: true,
        inTable: true,
    };
    /**
     * 默认值
     */
    @immutable()
    public readonly defaultValue: string | boolean | number = undefined;
    /**
     * 是否为主键
     */
    @immutable()
    public readonly primaryKey: boolean = undefined;
    /**
     * 父级 Id
     */
    @immutable()
    public readonly entityId: string = undefined;
    /**
     *  父级引用
     */
    @circular()
    @immutable()
    public root: Entity = undefined;
    /**
     * 节点是否为叶子节点
     * 前端 UI 状态
     */
    @excludedInJSON()
    public isLeaf: boolean = true;
    /**
     * 周边存在的名称
     */
    @excludedInJSON()
    public existingNames: Array<string> = [];
    /**
     * 数据类型Key，例子："/#basicType/String"，在前端使用
     */
    @immutable()
    public readonly typeKey: string = undefined;
    /**
     * 外键引用实体
     */
    @immutable()
    public readonly $relationEntity: string = undefined;
    /**
     * 外键引用实体字段
     */
    @immutable()
    public readonly $relationProperty: string = undefined;
    /**
     * 外键删除规则
     */
    @immutable()
    public readonly relationDelRule: string = undefined;
    /**
     * 上一版本规则
     */
    @immutable()
    @excludedInJSON()
    public readonly lastVersion: EntityProperty = undefined;
    /**
     * @param source 需要合并的部分参数
     */
    constructor(source?: Partial<EntityProperty>) {
        super();
        source && this.assign(source);
    }
    /**
     * 添加实体属性
     */
    @action('添加实体属性')
    async create(none?: void, actionOptions?: ActionOptions) {
        config.defaultApp?.emit('saving', true);

        const body = this.toJSON();
        ///
        //  convert2SchemaType(body);
        body._posIndex = this.root.propertyList.indexOf(this);
        ///
        utils.logger.debug('添加实体属性', body);
        const result: Entity = await entityService.addProperty({
            headers: {
                appId: config.defaultApp?.id,
                operationAction: actionOptions?.actionName || 'EntityProperty.create',
                operationDesc: actionOptions?.actionDesc || `添加实体"${this.root.name}"属性"${this.name}"`,
            },
            body,
        });
        this.deepPick(result, ['id']);

        // 先改成异步的
        this.root.syncInterfaces().then(() => {
            updateAllVariablesChildrenSchema();

            this.root.dataNode.service.emit('dataTypesChange');
            this.root.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', true);

        try {
            if (this.id) {
                await entityService.deleteProperty({
                    headers: {
                        appId: config.defaultApp?.id,
                        operationAction: actionOptions?.actionName || 'EntityProperty.delete',
                        operationDesc: actionOptions?.actionDesc || `删除实体"${this.root.name}"属性"${this.name}"`,
                    },
                    query: {
                        id: this.id,
                    },
                });
            }

            const index = this.root.propertyList.indexOf(this);
            ~index && this.root.propertyList.splice(index, 1);

            updateAllVariablesChildrenSchema();
            if (this.id) {
                this.root.syncInterfaces();
            }
            this.destroy();
            this.root.dataNode.service.emit('dataTypesChange');

            await config.defaultApp?.history.load();
            config.defaultApp?.emit('saved');
        } catch (err) {
            config.defaultApp?.emit('saved');
            throw err;
        }
    }
    /**
     * 修改实体属性
     */
    async update(none?: void, actionOptions?: ActionOptions, then?: () => Promise<any>) {
        config.defaultApp?.emit('saving', true);

        const body = this.toJSON();

        delete body.display.id;
        utils.logger.debug('修改实体属性', body);
        const result = await entityService.updateProperty({
            headers: {
                appId: config.defaultApp?.id,
                operationAction: actionOptions?.actionName || 'EntityPropperty.update',
                operationDesc: actionOptions?.actionDesc || `修改实体"${this.root.name}"属性"${this.name}"`,
            },
            body,
        });

        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: `设置实体"${this.root.name}"的属性"${oldName}"的名称为"${name}"`,
        }, async () => {
            await this.root.syncInterfaces();
        });

        this.root.dataNode.service.emit('dataTypesChange');
        this.root.dataNode.service.emit('vertexIdToNameChange', this.id, this.name);
    }
    /**
     * 设置实体属性标题
     * @param label 标题
     */
    @action('设置实体属性标题')
    async setLabel(label: string) {
        this.assign({ label });
        await this.update(undefined, {
            actionDesc: `设置实体"${this.root.name}"的属性"${this.name}"的标题为"${label}"`,
        });
    }
    /**
     * 设置实体属性描述
     * @param description 描述
     */
    @action('设置实体属性描述')
    async setDescription(description: string) {
        this.assign({ description });
        await this.update(undefined, {
            actionDesc: `设置实体"${this.root.name}"的属性"${this.name}"的描述为"${description}"`,
        });
    }
    /**
     * 查找schema 顶点被引用的逻辑顶点列表
     */
    async getSchemaUsage() {
        const result = await schemaService.getSchemaUsage({
            query: {
                schemaId: this.id,
                valSource: 'entity_property',
            },
        });
        return result;
    }
    /**
     * 设置实体属性的数据类型
     */
    @action('设置实体属性的数据类型')
    async setDataType(schema: Schema) {
        // 用于update失败后还原数据
        const originalData = {
            $ref: this.$ref,
            type: this.type,
            format: this.format,
            defaultValue: this.defaultValue,
            relationship: this.relationship,
            rules: this.rules,
            $relationEntity: this.$relationEntity,
            $relationProperty: this.$relationProperty,
        };
        this.assign({
            $ref: undefined, //引用到Entity或者structure
            type: undefined,
            format: undefined,
        });
        this.assign(schema);

        this.assign({
            defaultValue: getBasicTypeDefaultValue(),
            relationship: '',
            rules: [],
            $relationEntity: '',
            $relationProperty: '',
        });
        try {
            await this.update(undefined, {
                actionDesc: `设置实体"${this.root.name}"的属性"${this.name}"的数据类型为"${schema.typeKey}"`,
            });
            this.assign({ isDataTypeSetting: false });
            await this.root.syncInterfaces();
            updateAllVariablesChildrenSchema();
            this.root.dataNode.service.emit('dataTypesChange');
        } catch (err) {
            this.assign(convert2RefType(originalData));
            config.defaultApp?.emit('saved');
            throw err;
        }
    }
    /**
     * 设置实体属性关系
     */
    @action('设置实体属性关系')
    async setRelationship(relationship: string) {
        this.assign({ relationship });
        await this.update(undefined, {
            actionDesc: `设置实体"${this.root.name}"的属性"${this.name}"的关系为"${relationship}"`,
        });

        this.root.dataNode.service.emit('dataTypesChange');
    }
    /**
     * 设置实体属性外键
     * reference: {$relationEntity, $relationProperty}
     */
    @action('设置实体属性外键')
    async setReference(reference: any) {
        this.assign(reference);
        await this.update(undefined, {
            actionDesc: `设置实体"${this.root.name}"的属性"${this.name}"的关联字段为"${reference.$relationProperty}"`,
        });

        this.root.dataNode.service.emit('dataTypesChange');
    }
    /**
     * 清除实体属性外键
     */
    @action('清除实体属性外键')
    async clearReference() {
        this.assign({
            $relationEntity: undefined,
            $relationProperty: undefined,
            relationDelRule: undefined,
        });
        await this.update(undefined, {
            actionDesc: `清除实体"${this.root.name}"的属性"${this.name}"的外键`,
        });

        this.root.dataNode.service.emit('dataTypesChange');
    }
    @action('设置实体属性外键删除规则')
    async setRelationDelRule(relationDelRule: string) {
        this.assign({ relationDelRule });
        await this.update(undefined, {
            actionDesc: `设置实体属性外键删除规则`,
        });
    }
    @action('设置实体属性规则')
    async setRules(rules: any) {
        this.assign({ rules });
        await this.update(undefined, {
            actionDesc: `设置实体属性规则`,
        });
    }
    /**
     * 设置实体属性的默认值
     */
    @action('设置实体属性的默认值')
    async setDefaultValue(defaultValue: string) {
        this.assign({ defaultValue });
        await this.update(undefined, {
            actionDesc: `设置实体"${this.root.name}"的属性"${this.name}"的默认值为"${defaultValue}"`,
        });

        this.root.dataNode.service.emit('dataTypesChange');
    }
    /**
     * 设置实体属性是否必须
     * @param required 必须
     */
    @action('设置实体属性是否必须')
    async setRequired(required: boolean) {
        this.assign({ required });
        await this.update(undefined, {
            actionDesc: `设置实体"${this.root.name}"的属性"${this.name}"为"${required ? '必须' : '不必须'}"`,
        });

        this.root.dataNode.service.emit('dataTypesChange');
    }
    @action('设置实体属性显示')
    async setDisplay(display: Display) {
        this.assign({ display });
        await this.update(undefined, {
            actionDesc: '设置实体属性显示',
        });
    }
    /**
     * 移动位置
     * @param index 目标位置
     */
    async moveTo(index: number, oldIndex?: number) {
        const propertyList = this.root.propertyList;
        if (oldIndex === undefined)
            oldIndex = propertyList.indexOf(this);
        propertyList.splice(oldIndex, 1);
        propertyList.splice(index, 0, this);

        await entityService.moveProperty({
            headers: {
                appId: config.defaultApp?.id,
                operationAction: null,
                operationDesc: null,
            },
            query: {
                id: this.id,
                targetIndex: index,
            },
        });
    }
    /**
     * 上移实体属性
     */
    @action('上移实体属性')
    moveUp() {
        const propertyList = this.root.propertyList;
        const oldIndex = propertyList.indexOf(this);
        if (oldIndex === 0)
            return;
        return this.moveTo(oldIndex - 1, oldIndex);
    }
    /**
     * 属性下移
     */
    @action('下移实体属性')
    moveDown() {
        const propertyList = this.root.propertyList;
        const oldIndex = propertyList.indexOf(this);
        if (oldIndex === propertyList.length - 1)
            return;
        return this.moveTo(oldIndex + 1, oldIndex);
    }
    /**
    * 只更改Entity Property的名称
    */
    async updateLogicParamsForName(entity: Entity, property: EntityProperty) {
        const itfacePrefix = ['getAll', 'count', 'export'];
        const interfaces: Array<Interface> = (entity.resolvers || [])
            .filter((resolver) => itfacePrefix.includes(resolver.name))
            .map((resolver) => resolver.interface);

        const tasks = interfaces.map((itface) => {
            if (itface.logic) {
                return itface.logic.params.filter((param) => param.entityFieldId === this.id)
                    .map((param) => {
                        const name = param.name.split('.');
                        const newName = `${property.name}.${name[1]}`;
                        return param.setName(newName);
                    });
            } else {
                return [];
            }
        });

        await Promise.all(tasks);
    }
}

export default EntityProperty;
