import { immutable, excludedInJSON, action } from '../decorators';
import { config, history, utils, LEVEL_ENUM, Vertex, Logic, PackageJSON, App, WebService, Page, Block, Schema, BaseVariable, ActionOptions, Interface } from '..';
import { paramService } from '../../service/logic';
import { convert2RefType, convert2SchemaType } from '../data/dataTypeUtils';
import { getBasicTypeDefaultValue } from '../data/basicTypes';
import { schemaService } from '../../service/common';

/**
 * 逻辑输入参数
 */
export class Param extends BaseVariable {
    /**
     * 概念类型
     */
    @immutable()
    public readonly level: LEVEL_ENUM = LEVEL_ENUM.param;
    /**
     * 请求类型
     */
    @immutable()
    public readonly in: string = undefined;
    /**
     * 实体字段 Id
     */
    @immutable()
    public readonly entityFieldId: string = undefined;
    /**
     * 默认值
     * 按 JSON string 处理
     * - string: 666 -> '666'
     * - string: true -> 'true'
     * - number: 666 -> 666
     * - boolean: true -> true
     */
    @immutable()
    public readonly defaultValue: string = undefined;

    /**
     * @param source 需要合并的部分参数
     */
    constructor(source?: Partial<Param>) {
        super();
        source && this.assign(source);
    }
    /**
     * 添加逻辑输入参数
     */
    @action('添加逻辑输入参数')
    async create(none?: void, actionOptions?: ActionOptions) {
        config.defaultApp?.emit('saving');

        const body = this.toJSON();
        // convert2SchemaType(body.schema);
        body._posIndex = this.logic.params.indexOf(this);
        utils.logger.debug('添加逻辑输入参数', body);
        const result: Param = await paramService.create({
            headers: {
                appId: config.defaultApp?.id,
                operationAction: actionOptions?.actionName || 'Param.create',
                operationDesc: actionOptions?.actionDesc || `添加逻辑"${this.logic.name}"输入参数"${this.name}"`,
            },
            body,
        });
        // convert2RefType(result.schema);
        this.deepPick(result, ['id']);
        this.assign({ code: this.genCode() });
        if (this.logic.interface) {
            const interfaceItem = Vertex.getVertexByRef(this.logic.interface.id) as Interface;
            // this.logic.interface.service.emit('interfacesChange');
            interfaceItem.setParamAndReturn();
        } else if (this.logic.view) {
            this.logic.view.page.service.emit('pageTreeChange');
            this.logic.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 (this.id) {
            try {
                await paramService.delete({
                    headers: {
                        appId: config.defaultApp?.id,
                        operationAction: actionOptions?.actionName || 'Param.create',
                        operationDesc: actionOptions?.actionDesc || `删除逻辑"${this.logic.name}"输入参数"${this.name}"`,
                    },
                    query: {
                        loValId: this.id,
                        logicId: this.logic.id,
                    },
                });
            } catch(err) {
                await config.defaultApp?.history.load();
                throw err;
            }
        }

        const index = this.logic.params.indexOf(this);
        ~index && this.logic.params.splice(index, 1);
        this.destroy();
        if (this.logic.interface) {
            // this.logic.interface.service.emit('interfacesChange');
            this.logic.interface.setParamAndReturn();
        } else if (this.logic.view) {
            this.logic.view.page.service.emit('pageTreeChange');
            this.logic.emit('change');
        }

        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();
        // convert2SchemaType(body.schema);
        utils.logger.debug('修改逻辑输入参数', body);
        const result = await paramService.update({
            headers: {
                appId: config.defaultApp?.id,
                operationAction: actionOptions?.actionName || 'Param.update',
                operationDesc: actionOptions?.actionDesc || `修改逻辑输入参数"${this.name}"`,
            },
            body,
        });
        this.schema.id = result.schema.id;
        // convert2RefType(result.schema);
        // 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: `设置逻辑"${this.logic.name}"的输入参数"${oldName}"的名称为"${name}"`,
        });

        if (this.logic.interface) {
            // this.logic.interface.service.emit('interfacesChange');
            this.logic.interface.setParamAndReturn();
        } else if (this.logic.view) {
            this.logic.view.page.service.emit('pageTreeChange');
            this.logic.emit('change');
        }
    }
    /**
     * 设置逻辑输入参数描述
     * @param description 描述
     */
    @action('设置逻辑输入参数描述')
    async setDescription(description: string) {
        this.assign({ description });
        await this.update(undefined, {
            actionDesc: `设置逻辑"${this.logic.name}"的输入参数"${this.name}"的描述为"${description}"`,
        });

        if (this.logic.interface) {
            this.logic.interface.service.emit('interfacesChange');
        } else if (this.logic.view) {
            this.logic.view.page.service.emit('pageTreeChange');
            this.logic.emit('change');
        }
    }
    /**
     * 查找schema 顶点被引用的逻辑顶点列表
     */
    async getSchemaUsage() {
        if (this.schema) {
            const schemaId = this.schema.type === 'genericType' ? this.id : this.schema.id;
            const result = await schemaService.getSchemaUsage({
                query: {
                    schemaId,
                    valSource: 'logic',
                },
            });
            return result;
        }
    }
    /**
     * 设置逻辑输入参数的数据类型
     */
    @action('设置逻辑输入参数的数据类型')
    async setDataType(schema: Schema) {
        // 用于update失败后还原数据
        const originalData = {
            schema: Object.assign({}, this.schema),
            defaultValue: this.defaultValue,
        };
        Object.assign(this.schema, {
            $ref: undefined, //引用到Entity或者structure
            type: undefined,
            format: undefined,
            typeInstantiation: undefined,
        });
        Object.assign(this.schema, schema);
        // if (this.defaultValue)
        this.assign({ defaultValue: getBasicTypeDefaultValue() });

        try {
            await this.update(undefined, {
                actionDesc: `设置逻辑"${this.logic.name}"的输入参数"${this.name}"的数据类型为"${this.schema.typeKey}"`,
            });
            this.genSchemaChildren();

            if (this.logic.interface) {
            // this.logic.interface.service.emit('interfacesChange');
                this.logic.interface.setParamAndReturn();
            } else if (this.logic.view) {
                this.logic.view.page.service.emit('pageTreeChange');
                this.logic.emit('change');
            }
        } catch (err) {
            this.assign(originalData);
            config.defaultApp?.emit('saved');
            throw err;
        }
    }
    /**
     * 设置逻辑输入参数为列表
     * @param isArray
     */
    @action('设置逻辑输入参数是否为列表')
    async setAsList(isArray: boolean) {
        this.schema.isArray = isArray;
        this.assign({ defaultValue: getBasicTypeDefaultValue() });

        await this.update(undefined, {
            actionDesc: `设置逻辑"${this.logic.name}"的输入参数"${this.name}"${isArray ? '为' : '不为'}列表`,
        });
        this.genSchemaChildren();

        if (this.logic.interface) {
            // this.logic.interface.service.emit('interfacesChange');
            this.logic.interface.setParamAndReturn();
        } else if (this.logic.view) {
            this.logic.view.page.service.emit('pageTreeChange');
            this.logic.emit('change');
        }
    }
    /**
     * 设置逻辑输入参数的默认值
     */
    @action('设置逻辑输入参数的默认值')
    async setDefaultValue(defaultValue: string) {
        this.assign({ defaultValue });
        await this.update(undefined, {
            actionDesc: `设置逻辑输入参数的默认值`,
        });

        if (this.logic.interface) {
            this.logic.interface.service.emit('interfacesChange');
        } else if (this.logic.view) {
            this.logic.view.page.service.emit('pageTreeChange');
            this.logic.emit('change');
        }
    }
    /**
     * 设置逻辑输入参数的请求类型
     * @param in 必须
     */
    @action('设置逻辑输入参数的请求类型')
    async setIn(requestIn: boolean) {
        this.assign({ in: requestIn });
        await this.update(undefined, {
            actionDesc: `设置逻辑输入参数"${this.logic.name}"的输入参数"${this.name}"的请求类型为"${requestIn}"`,
        });

        if (this.logic.interface) {
            // this.logic.interface.service.emit('interfacesChange');
            this.logic.interface.setParamAndReturn();
        } else if (this.logic.view) {
            this.logic.view.page.service.emit('pageTreeChange');
            this.logic.emit('change');
        }
    }
    /**
     * 设置逻辑输入参数是否必须
     * @param required 必须
     */
    @action('设置逻辑输入参数是否必须')
    async setRequired(required: boolean) {
        this.assign({ required });
        await this.update(undefined, {
            actionDesc: `设置逻辑"${this.logic.name}"的输入参数"${this.name}"为"${required ? '必须' : '不必须'}"`,
        });

        if (this.logic.interface) {
            // this.logic.interface.service.emit('interfacesChange');
            this.logic.interface.setParamAndReturn();
        } else if (this.logic.view) {
            this.logic.view.page.service.emit('pageTreeChange');
            this.logic.emit('change');
        }
    }
    /**
     * 从后端 JSON 生成规范的 Param 对象
     */
    public static from(source: any, logic: Logic) {
        convert2RefType(source.schema);
        source.logic = logic;
        source.code = 'ID_' + source.id;
        const param = new Param(source);
        param.genSchemaChildren();
        return param;
    }
}

export default Param;
