import { IContext, IEntityBase } from '../../interface';
import { where, equals, isNil, isEmpty } from 'ramda';
import { isExistSessionKey, isExistSrfKey } from '../service-exist-util/service-exist-util';
import { LogUtil } from '../log-util/log-util';
import { createUUID } from 'qx-util';

/**
 * 实体缓存工具类
 *
 * @export
 * @class EntityCache
 * @template T
 */
export class EntityCache<T extends IEntityBase> {
    /**
     * 数据缓存
     *
     * @type {Map<string, T>}
     * @memberof EntityCache
     */
    readonly cacheMap: Map<string, Map<string, T>> = new Map();

    /**
     * 新增数据
     *
     * @param {*} context
     * @param {T} entity
     * @return {*}  {boolean}
     * @memberof EntityCache
     */
    add(context: IContext, entity: T): T | null {
        try {
            isExistSessionKey('add', context);
            if (isNil(entity.srfkey) || isEmpty(entity.srfkey)) {
                entity.srfkey = createUUID();
            }
            entity.srftempdate = new Date().getTime();
            const map = this.getCacheByTag(context.srfsessionkey!);
            map.set(entity.srfkey, this.cloneData(entity));
            LogUtil.warn('add', entity.srfkey, entity);
            return entity;
        } catch (err) {
            throw err;
        }
    }

    /**
     * 查找数据
     *
     * @param {*} context
     * @param {string} srfKey
     * @return {*}  {T}
     * @memberof EntityCache
     */
    get(context: IContext, srfKey: string): T | null {
        try {
            isExistSessionKey('get', context);
            const map = this.getCacheByTag(context.srfsessionkey!);
            const data = map.get(srfKey);
            LogUtil.warn('get', srfKey, data);
            return this.cloneData(data)!;
        } catch (err) {
            throw err;
        }
    }

    /**
     * 更新数据
     *
     * @param {IContext} context
     * @param {T} entity
     * @return {*}  {T}
     * @memberof EntityCache
     */
    update(context: IContext, entity: T): T | null {
        try {
            isExistSessionKey('update', context);
            isExistSrfKey('update', entity);
            entity.srftempdate = new Date().getTime();
            const map = this.getCacheByTag(context.srfsessionkey!);
            const data = map.get(entity.srfkey!);
            if (data) {
                Object.assign(data, this.cloneData(entity));
                map.set(entity.srfkey!, data);
                LogUtil.warn('update', entity.srfkey, entity);
                return entity;
            }
            throw new Error('数据不存在，无法更新!');
        } catch (err) {
            throw err;
        }
    }

    /**
     * 删除数据
     *
     * @param {IContext} context
     * @param {string} srfKey
     * @return {*}  {(T | null)}
     * @memberof EntityCache
     */
    delete(context: IContext, srfKey: string): T | null {
        try {
            isExistSessionKey('delete', context);
            const map = this.getCacheByTag(context.srfsessionkey!);
            const key = srfKey;
            if (map.has(key)) {
                const data = map.get(key)!;
                data.srftempdate = new Date().getTime();
                map.delete(key);
                LogUtil.warn('delete', key);
                return data;
            }
            return null;
        } catch (err) {
            throw err;
        }
    }

    /**
     * 检查数据是否已经存在
     *
     * @param {IContext} context
     * @param {string} srfkey
     * @return {*}  {boolean}
     * @memberof EntityCache
     */
    checkData(context: IContext, srfkey: string): boolean {
        const list = this.getList(context);
        const i = list.findIndex(item => item.srfkey === srfkey);
        return i !== -1;
    }

    /**
     * 克隆数据
     *
     * @param {*} data
     * @memberof EntityCache
     */
    cloneData(data: any) {
        const targetData: any = {};
        for (const key in data) {
            if (data[key] !== void 0 && typeof data[key] !== 'function') {
                targetData[key] = data[key]
            }
        }
        // 删除模型数据
        if (targetData.appDataEntity) {
            delete targetData.appDataEntity;
        }
        // 添加关键性数据
        if (data.srfdename) {
            targetData.srfdename = data.srfdename;
        }
        if (data.srfkey) {
            targetData.srfkey = data.srfkey;
        }
        if (data.srfmajortext) {
            targetData.srfmajortext = data.srfmajortext;
        }
        return targetData;
    }

    /**
     * 根据条件查找数据，未设置条件默认返回全部
     *
     * @param {IContext} context
     * @param {({ [key: string]: string | number | boolean })} condition
     * @return {*}  {(T[] | null)}
     * @memberof EntityCache
     */
    getList(context: IContext): T[] {
        try {
            isExistSessionKey('getList', context);
            const map = this.getCacheByTag(context.srfsessionkey!);
            if (!map) {
                return [];
            }
            return Array.from(map.values());
        } catch (error) {
            throw error;
        }
    }

    /**
     * 根据条件生成查询
     *
     * @param {*} [params={}]
     * @return {*}
     * @memberof EntityCache
     */
    generatePred(params: any = {}) {
        // 查询数据条件集
        const data: any = {};
        if (params.srfkey) {
            data.srfkey = equals(params.srfkey);
        }
        delete params.srfkey;
        for (const key in params) {
            if (params.hasOwnProperty(key)) {
                const val = params[key];
                data[key] = equals(val);
            }
        }
        return where(data);
    }

    /**
     * 清除缓存
     *
     * @param {IContext} context
     * @memberof EntityCache
     */
    clear(context: IContext): void {
        this.cacheMap.delete(context.srfsessionkey!);
    }

    /**
     * 根据标识获取map组
     *
     * @private
     * @param {string} tag
     * @return {*}  {Map<string, T>}
     * @memberof EntityCache
     */
    private getCacheByTag(tag: string): Map<string, T> {
        if (!this.cacheMap.has(tag)) {
            this.cacheMap.set(tag, new Map());
        }
        return this.cacheMap.get(tag)!;
    }
}
