import BaseModel from './BaseModel';
import { index, getModelForClass, prop, DocumentType, modelOptions } from '@typegoose/typegoose';
import { BAG } from '../pubUtils/dicParam';
import { SearchItemParam } from '../domain/backEndField/search';
// const Transaction = require("mongoose-transactions");
@index({ roleId: 1, id: 1 })
@index({ seqId: 1 })
@modelOptions({ schemaOptions: { id: false } })
export default class Item extends BaseModel {
    @prop({ required: true, default: '' })
    roleId: string;					// 角色 id
    @prop({ required: true, default: '' })
    roleName: string;				// 角色名称

    @prop({ required: true, default: '' })
    id: number;				  // 道具 id
    @prop({ required: true, default: '' })
    type: number;						// 道具类型
    @prop({ required: true, default: '' })
    itemName: string;			  // 道具名称
    @prop({ required: true, default: 0 })
    hid: number;			      // 将魂：武将id，其他：0
    @prop({ required: true })
    count: number;			    // 道具数量


    public static async findbyRole(roleId: string, select = '') {
        const items: ItemType[] = await ItemModel.find({ roleId, count: {$gte: 0} }).select(select).lean();
        return items;
    }

    public static async findbyRoleAndIds(roleId: string, ids: Array<number>, lean = true) {
        const items: ItemType[] = await ItemModel.find({ roleId, id: { $in: ids }, count: {$gte: 0} }).select('id count type').lean(lean);
        return items;
    }

    public static async findByRoleAndType(roleId: string, type: number, lean = true) {
        const items: ItemType[] = await ItemModel.find({ roleId, type }).select('id count type').sort({ id: 1 }).lean(lean);
        return items;
    }

    public static async findbyRoleAndGidAndCount(roleId: string, id: number, count: number, lean = true) {
        const items: ItemType = await ItemModel.findOne({ roleId, id, count: { $gte: count } }).select('id count type').lean(lean);
        return items;
    }

    public static async findbyRoleAndGid(roleId: string, id: number, lean = true) {
        const items: ItemType = await ItemModel.findOne({ roleId, id }).select('id count type').lean(lean);
        return items;
    }

    public static async increaseItem(roleId: string, id: number, count: number, itemInfo: { roleId: string, roleName: string, id: number, itemName: string, type: number, hid?: number }) {
        const doc = new ItemModel();
        const setOnInsert = Object.assign(doc.toJSON(), itemInfo);
        let items: ItemType = await ItemModel.findOneAndUpdate({ roleId, id }, { $setOnInsert: setOnInsert, $inc: { count } }, { new: true, upsert: true }).lean();
        if(items.count > BAG.BAG_GOODS_UPLIMITED) {
            items = await ItemModel.findOneAndUpdate({ roleId, id, count: { $gte: BAG.BAG_GOODS_UPLIMITED } }, { $set: { count: BAG.BAG_GOODS_UPLIMITED } }, { new: true }).lean();
        }
        return items;
    }

    public static async decreaseItems(roleId: string, items: Array<{ id: number, count: number, ratio?: number }>, lean = true) {
        let updateItems = new Array<{ id: number, count: number, ratio?: number }>(), hasError: boolean = false, result = new Array();
        for (let { id, count, ratio } of items) {
            let rec: ItemType;
            if (!ratio) {
                rec = await ItemModel.findOneAndUpdate({ roleId, id, count: { $gte: count } }, { $inc: { count: -1 * count } }, { new: true }).lean(lean);
            } else {
                rec = await ItemModel.findOneAndUpdate({ roleId, id }, { $inc: { count: ratio * count } }, { new: true, upsert: true }).lean(lean);
            }
            if (!!rec) {
                let index = result.findIndex(cur => cur.id == rec.id);
                if (index != -1) {
                    result[index].count = rec.count;
                    result[index].inc += -count;
                } else {
                    result.push({ id: rec.id, count: rec.count, inc: -count });
                }
                updateItems.push({ id, count, ratio });
            } else {
                hasError = true; break;
            }
        }
        if (hasError) { // 数量不足
            for (let { id, count, ratio } of updateItems) {
                if (!ratio) ratio = -1;
                await ItemModel.findOneAndUpdate({ roleId, id }, { $inc: { count: -ratio * count } }, { new: true }).lean(lean);
            }
            return { hasError: true }
        } else {
            return { hasError: false, result }
        }
    }

    public static async deleteAccount(roleId: string) {
        let result = await ItemModel.deleteMany({ roleId });
        return result;
    }

    public static async deletebyRoleAndIds(roleId: string, ids: Array<number>) {
        const result = await ItemModel.deleteMany({ roleId, id: { $in: ids } });
        return result;
    }

    public static async updateCountByRoleAndIds(roleId: string, ids: Array<number>, count: number) {
        const result: ItemType = await ItemModel.findOneAndUpdate({ roleId, id: { $in: ids } }, {$set: {count}}, {new: true, upsert: true}).lean();
        return result;
    }

    private static getSearchObj(form: SearchItemParam) {
        let searchObj = {};
        if (form.roleId) searchObj['roleId'] = form.roleId;
        if (form.roleName) searchObj['roleName'] = { $regex: new RegExp(form.roleName.toString(), 'i') };
        if (form.id) searchObj['id'] = form.id;
        return searchObj
    }

    public static async findByCondition(page: number, pageSize: number, sortField: string = 'updatedAt', sortOrder: string = 'descend', form: SearchItemParam = {}) {

        let searchObj = this.getSearchObj(form);
        let sort = {};
        if (sortField && sortOrder) {
            if (sortOrder == 'ascend') {
                sort[sortField] = 1;
            } else if (sortOrder == 'descend') {
                sort[sortField] = -1;
            }
        }
        const result: ItemType[] = await ItemModel.find(searchObj).limit(pageSize).skip((page - 1) * pageSize).sort(sort).select('-_id -__v -createdAt -updatedAt').lean({ getters: true, virtuals: true });
        return result;

    }

    public static async countByCondition(form: SearchItemParam = {}) {

        let searchObj = this.getSearchObj(form);
        const result = await ItemModel.count(searchObj);
        return result;
    }
}


export const ItemModel = getModelForClass(Item);

export interface ItemType extends Pick<DocumentType<Item>, keyof Item> {
    id: number;
};
