import { immutable, circular, action } from '../decorators';
import {
    config, utils, LEVEL_ENUM, Vertex, Entity,
    Schema, ActionOptions,
} from '..';
import { entityService } from '../../service/data';

/**
 * 实体索引类
 */
export class EntityIndex extends Vertex implements Schema {
    /**
     * 概念类型
     */
    @immutable()
    public level: LEVEL_ENUM = LEVEL_ENUM.entityIndex;
    /**
     * Id
     */
    @immutable()
    public readonly id: string = undefined;

    /**
     * 名称
     */
    @immutable()
    public readonly name: string = undefined;

    /**
     * 是否唯一
     */
    @immutable()
    public readonly unique: string = undefined;

    /**
     * 字段引用
     */
    @immutable()
    public readonly propertyIds: Array<string> = [];

    /**
     * 描述
     */
    @immutable()
    public readonly description: string = undefined;

    /**
     * 父级 Id
     */
    @immutable()
    public readonly entityId: string = undefined;
    /**
     *  父级引用
     */
    @circular()
    @immutable()
    public root: Entity = undefined;

    /**
     * @param source 需要合并的部分参数
     */
    constructor(source?: Partial<EntityIndex>) {
        super();
        source && this.assign(source);
    }

    /**
     * 添加实体索引
     */
    @action('添加实体索引')
    async create(none?: void, actionOptions?: ActionOptions) {
        config.defaultApp?.emit('saving', true);

        const body = this.toJSON();
        body._posIndex = this.root.indexList.indexOf(this);
        utils.logger.debug('添加实体索引', body);
        const result: Entity = await entityService.addIndex({
            headers: {
                appId: config.defaultApp?.id,
                operationAction: actionOptions?.actionName || 'EntityIndex.create',
                operationDesc: actionOptions?.actionDesc || `添加实体"${this.root.name}"索引"${this.name}"`,
            },
            body,
        });
        this.deepPick(result, ['id', '_posIndex']);
        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.deleteIndex({
                    headers: {
                        appId: config.defaultApp?.id,
                        operationAction: actionOptions?.actionName || 'EntityIndex.delete',
                        operationDesc: actionOptions?.actionDesc || `删除实体"${this.root.name}"索引"${this.name}"`,
                    },
                    query: {
                        id: this.id,
                    },
                });
            }

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

            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();
        utils.logger.debug('修改实体索引', body);
        try {
            const result = await entityService.updateIndex({
                headers: {
                    appId: config.defaultApp?.id,
                    operationAction: actionOptions?.actionName || 'EntityIndex.update',
                    operationDesc: actionOptions?.actionDesc || `修改实体"${this.root.name}"索引"${this.name}"`,
                },
                body,
            });
            await then?.();
            await config.defaultApp?.history.load();
        } catch (err) {}

        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}"`,
        });
    }
    /**
     * 设置实体索引标题
     * @param label 标题
     */
    @action('设置实体索引字段')
    async setPropertyIds(propertyIds: Array<string>) {
        this.assign({ propertyIds });
        await this.update(undefined, {
            actionDesc: `设置实体"${this.root.name}"的索引"${this.name}"的字段`,
        });
    }
    /**
     * 设置实体索引描述
     * @param description 描述
     */
    @action('设置实体索引描述')
    async setUnique(unique: boolean) {
        this.assign({ unique });
        await this.update(undefined, {
            actionDesc: `设置实体"${this.root.name}"的索引"${this.name}"的唯一性为"${unique}"`,
        });
    }
    /**
     * 设置实体索引描述
     * @param description 描述
     */
    @action('设置实体索引描述')
    async setDescription(description: string) {
        this.assign({ description });
        await this.update(undefined, {
            actionDesc: `设置实体"${this.root.name}"的索引"${this.name}"的描述为"${description}"`,
        });
    }

    /**
     * 移动位置
     * @param index 目标位置
     */
    async moveTo(index: number, oldIndex?: number) {
        const indexList = this.root.indexList;
        if (oldIndex === undefined)
            oldIndex = indexList.indexOf(this);
        indexList.splice(oldIndex, 1);
        indexList.splice(index, 0, this);

        await entityService.moveIndex({
            headers: {
                appId: config.defaultApp?.id,
                operationAction: null,
                operationDesc: null,
            },
            query: {
                id: this.id,
                targetIndex: index,
            },
        });
    }

    /**
     * 上移实体索引
     */
    @action('上移实体索引')
    moveUp() {
        const indexList = this.root.indexList;
        const oldIndex = indexList.indexOf(this);
        if (oldIndex === 0)
            return;
        return this.moveTo(oldIndex - 1, oldIndex);
    }
    /**
     * 索引下移
     */
    @action('下移实体索引')
    moveDown() {
        const indexList = this.root.indexList;
        const oldIndex = indexList.indexOf(this);
        if (oldIndex === indexList.length - 1)
            return;
        return this.moveTo(oldIndex + 1, oldIndex);
    }
}
