import { Application, BackendSession, HandlerService, } from "pinus";
import { STATUS, HERO_SYSTEM_TYPE, ITEM_CHANGE_REASON, TASK_TYPE } from "../../../consts";
import { ItemInter, RewardInter } from "../../../pubUtils/interface";

import { resResult, parseGoodStr } from "../../../pubUtils/util";
import { addItems, getJewelRandSe, handleCost } from "../../../services/role/rewardService";
import { HeroModel, EPlace } from "../../../db/Hero";
import { gameData, getEquipByJobClassAndEPlace, getNextEquipQuality, getEquipStarIdByEquipId, getNextEquipStar } from "../../../pubUtils/data";
import { BAG, EQUIP } from "../../../pubUtils/dicParam";
import { getRandSeResult, updateEplace, updateEplaces, checkJewelCanPutOnEquip, updateStone, checkStoneCanPutOnEquip, isLocked } from "../../../services/equipService";

import { isNumber, pick } from 'underscore';
import { JewelModel, RandSe } from "../../../db/Jewel";
import { checkTaskInComposeEquip, checkTaskInEquipLvUp, checkTaskInComposeStone, checkTaskInEquipReset, checkTaskInEquipQuench, checkTaskInEquipQualityUp, checkTaskInEquipStarUp, checkTaskInPutJewel, checkTaskInPutStone } from '../../../services/task/taskService';
import { pushEquipQualityMax, pushEquipStarMax } from "../../../services/sysChatService";
import { addConsumeToHero } from "../../../services/roleService";
import { CheckMeterial } from "../../../services/role/checkMaterial";
import { combineItems } from "../../../services/role/util";
import { calculateCeWithHero } from "../../../services/playerCeService";

export default function (app: Application) {
    new HandlerService(app, {});
    return new EquipHandler(app);
}

export class EquipHandler {

    constructor(private app: Application) {
    }

    public async composeEquip(msg: { hid: number, ePlaceId: number }, session: BackendSession) {
        let roleId: string = session.get('roleId');
        let sid: string = session.get('sid');
        let serverId: number = session.get('serverId');

        let { hid, ePlaceId } = msg;
        if(!isNumber(ePlaceId) || ePlaceId > 4 || ePlaceId < 1) return resResult(STATUS.WRONG_PARMS);

        let hero = await HeroModel.findByHidAndRole(hid, roleId);
        if(!hero) return resResult(STATUS.HERO_NOT_FIND);
        
        let oldEplace = hero.ePlace||[];
        let curEquip = oldEplace.find(equip => equip.id == ePlaceId );
        if(curEquip) return resResult(STATUS.EQUIP_HAS_COMPOSE);

        let dicHero = gameData.hero.get(hero.skinId);
        let dicEquip = getEquipByJobClassAndEPlace(dicHero?.jobClass, ePlaceId);
        if(!dicEquip) return resResult(STATUS.DIC_DATA_NOT_FOUND);

        let consumeResult = await handleCost(roleId, sid, dicEquip.composeMaterial, ITEM_CHANGE_REASON.EQUIP_COMPOSE);
        if(!consumeResult) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);

        let newEquip = new EPlace(ePlaceId, dicEquip.id);
        let newEplace = [...oldEplace, newEquip];

        let update = {
            ePlace: newEplace,
            consumes: addConsumeToHero(hero.consumes, dicEquip.composeMaterial),
        }

        await calculateCeWithHero(HERO_SYSTEM_TYPE.COMPOSE_EQUIP, roleId, serverId, sid, hid, update, { ePlaceId, skinId: hero.skinId });
        await checkTaskInComposeEquip(serverId, roleId, sid, oldEplace, newEplace);

        let curHero = {
            hid,
            ePlace: [newEquip]
        }
        return resResult(STATUS.SUCCESS, { curHero });
    }
    
    // 装备栏强化
    public async strengthen(msg: { hid: number, ePlaceId: number, isOneClick: boolean }, session: BackendSession) {
        let roleId: string = session.get('roleId');
        // let roleName: string = session.get('roleName');
        const serverId = session.get('serverId');
        let sid: string = session.get('sid');
        

        let { hid, ePlaceId, isOneClick } = msg;
        let hero = await HeroModel.findByHidAndRole(hid, roleId);
        if (!hero) return resResult(STATUS.HERO_NOT_FIND);
        let oldEplace = hero.ePlace||[];
        let curEquip = oldEplace.find(cur => cur.id == ePlaceId);
        if(!curEquip) return resResult(STATUS.EQUIP_NOT_FIND);
        if(curEquip.lv >= hero.lv) return resResult(STATUS.ROLE_EQUIP_REACH_MAX);
        let fromLv = curEquip.lv;
        let toLv = isOneClick? hero.lv: fromLv + 1;
        let newLv = fromLv;

        let check = new CheckMeterial(roleId);
        for (let lv = fromLv + 1; lv <= toLv; lv++) {
            let dicCost = gameData.equipStrengthenCost.get(lv);
            let isEnough = await check.decrease(dicCost.consume);
            if(!isEnough) break; // 消耗不足
            newLv = lv;
        }
        if(newLv == fromLv) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);
        let consumes = check.getConsume();
        let result = await handleCost(roleId, sid, consumes, ITEM_CHANGE_REASON.EQUIP_STRENTHEN);
        if (!result) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);

        let { newEplace, updatedEplace } = updateEplace(oldEplace, ePlaceId, { lv: newLv });

        let update = {
            ePlace: newEplace,
            consumes: addConsumeToHero(hero.consumes, consumes)
        }
        await calculateCeWithHero(HERO_SYSTEM_TYPE.EQUIP_STRENGTH, roleId, serverId, sid, hid, update, { ePlaceIds: [ePlaceId] });
        await checkTaskInEquipLvUp(serverId, roleId, sid, oldEplace, newEplace, [ePlaceId]);

        const curHero = {
            hid,
            ePlace: updatedEplace
        }
        return resResult(STATUS.SUCCESS, { curHero });

    }

    // 装备栏一键强化至相应等级
    public async strengthenAll(msg: { hid: number, lv: number }, session: BackendSession) {
        let roleId: string = session.get('roleId');
        let serverId: number = session.get('serverId');
        let sid: string = session.get('sid');

        let { hid, lv } = msg; // lv: 升到哪一级
        let hero = await HeroModel.findByHidAndRole(hid, roleId);
        if (!hero) return resResult(STATUS.HERO_NOT_FIND);

        let { ePlace, lv: playerLv } = hero; // 装备栏
        let fromLv = Math.min(...ePlace.map(cur => cur.lv)); // 最小的等级
        let toLv = lv < playerLv? lv: playerLv;

        let eplaceIds = new Map<number, { lv: number }>(); // 更新了的装备栏id
        let check = new CheckMeterial(roleId);
        for(let lv = fromLv + 1; lv <= toLv; lv++) {
            let isBreak = false;
            for(let equip of ePlace) {
                if(equip.lv >= lv) continue;
                let dicCost = gameData.equipStrengthenCost.get(lv);
                let isEnough = await check.decrease(dicCost.consume);
                if(!isEnough) { isBreak = true; break; } // 消耗不足

                eplaceIds.set(equip.id, { lv });
            }
            if(isBreak) break;
        }

        if(eplaceIds.size <= 0) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);
        let consumes = check.getConsume();
        let result = await handleCost(roleId, sid, consumes, ITEM_CHANGE_REASON.EQUIP_STRENTHEN);
        if (!result) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);

        let { newEplace, updatedEplace } = updateEplaces(ePlace, eplaceIds);

        let update = {
            ePlace: newEplace,
            consumes: addConsumeToHero(hero.consumes, consumes),
        }
        await calculateCeWithHero(HERO_SYSTEM_TYPE.EQUIP_STRENGTH, roleId, serverId, sid, hid, update, { ePlaceIds: [...eplaceIds.keys()] });
        await checkTaskInEquipLvUp(serverId, roleId, sid, ePlace, newEplace, [...eplaceIds.keys()]);

        const curHero = {
            hid,
            ePlace: updatedEplace
        }
        return resResult(STATUS.SUCCESS, { curHero });

    }


    // 装备升品
    public async qualityUp(msg: { hid: number, ePlaceId: number, isOneClick: boolean }, session: BackendSession) {
        let roleId: string = session.get('roleId');
        let roleName: string = session.get('roleName');
        const serverId = session.get('serverId');
        let sid: string = session.get('sid');
        
        let { hid, ePlaceId, isOneClick } = msg;
        let hero = await HeroModel.findByHidAndRole(hid, roleId);
        if (!hero) return resResult(STATUS.HERO_NOT_FIND);

        let oldEplace = hero.ePlace||[];
        let curEquip = oldEplace.find(cur => cur.id == ePlaceId);
        if(!curEquip) return resResult(STATUS.EQUIP_NOT_FIND);
        
        let nextEquipQuality = getNextEquipQuality(curEquip.equipId, curEquip.quality, curEquip.qualityStage);
        if(!nextEquipQuality) return resResult(STATUS.EQUIP_QUALITY_MAX);

        let equipUpdate = {
            quality: curEquip.quality,
            qualityStage: curEquip.qualityStage,
        }
        let check = new CheckMeterial(roleId);
        let count = 0;
        if(isOneClick) { // 一键升到该品最高点
            if(nextEquipQuality.quality != curEquip.quality) {
                return resResult(STATUS.EQUIP_QUALITYSTAGE_IS_MAX);
            }
            while(nextEquipQuality && nextEquipQuality.quality == curEquip.quality) {
                let isEnough = await check.decrease(nextEquipQuality.consume);
                if(!isEnough) break; // 消耗不足
                equipUpdate.qualityStage++;
                nextEquipQuality = getNextEquipQuality(curEquip.equipId, equipUpdate.quality, equipUpdate.qualityStage);
                count++;
            }
        } else { // 往下一阶，包括满点之后的升品
            let isEnough = await check.decrease(nextEquipQuality.consume);
            if(isEnough) {
                equipUpdate.quality = nextEquipQuality.quality;
                equipUpdate.qualityStage = nextEquipQuality.point;
                count++;
            }
        }
        if(equipUpdate.quality == curEquip.quality && equipUpdate.qualityStage == curEquip.qualityStage) {
            return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);
        }
        let consumes = check.getConsume();
        let result = await handleCost(roleId, sid, consumes, ITEM_CHANGE_REASON.EQUIP_QUALITYUP);
        if (!result) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);

        let isUpQuality = equipUpdate.quality != curEquip.quality;

        let { newEplace, updatedEplace, newEquip } = updateEplace(oldEplace, ePlaceId, equipUpdate);
        
        let update = {
            ePlace: newEplace,
            consumes: addConsumeToHero(hero.consumes, consumes),
        }
        await calculateCeWithHero(HERO_SYSTEM_TYPE.EQUIP_QUALITY, roleId, serverId, sid, hid, update, { ePlaceId });

        await checkTaskInEquipQualityUp(serverId, roleId, sid, oldEplace, newEplace, ePlaceId, hid, isUpQuality, count);
        pushEquipQualityMax(roleId, roleName, serverId, hid, newEquip, isUpQuality);
        const curHero = {
            hid,
            isUpQuality,
            ePlace: updatedEplace
        }
        return resResult(STATUS.SUCCESS, { curHero });

    }

    // 装备升星
    public async starUp(msg: { hid: number, ePlaceId: number, isOneClick: boolean }, session: BackendSession) {
        let roleId: string = session.get('roleId');
        let roleName: string = session.get('roleName');
        const serverId = session.get('serverId');
        let sid: string = session.get('sid');
        
        let { hid, ePlaceId, isOneClick } = msg;
        let hero = await HeroModel.findByHidAndRole(hid, roleId);
        if (!hero) return resResult(STATUS.HERO_NOT_FIND);

        let oldEplace = hero.ePlace||[];
        let curEquip = oldEplace.find(cur => cur.id == ePlaceId);
        if(!curEquip) return resResult(STATUS.EQUIP_NOT_FIND);
        let incEquipStarSum = 0;
        let equipUpdate = {
            star: curEquip.star,
            starStage: curEquip.starStage,
        }

        let check = new CheckMeterial(roleId);
        let dicEquipStar = getEquipStarIdByEquipId(curEquip.equipId, curEquip.star);
        if(!dicEquipStar) return resResult(STATUS.DIC_DATA_NOT_FOUND);
        let isUpStar = curEquip.starStage == dicEquipStar.count;
        let count = 0;

        if(isUpStar) { // 升星
            if(isOneClick) {
                return resResult(STATUS.EQUIP_STARSTAGE_IS_MAX);
            } else {
                let nextEquipStar = getNextEquipStar(curEquip.equipId, curEquip.star);
                if(!nextEquipStar) return resResult(STATUS.EQUIP_STAR_MAX);
                let dicEquipQualityExtra = gameData.equipQualityExtra.get(curEquip.quality);
                if(!dicEquipQualityExtra || dicEquipQualityExtra.star < nextEquipStar.star) {
                    return resResult(STATUS.EQUIP_QUALITY_NOT_ENOUGH);
                }
    
                let isEnough = await check.decrease(dicEquipStar.subConsume);
                if(isEnough) {
                    equipUpdate.star = nextEquipStar.star;
                    equipUpdate.starStage = 0;
                    incEquipStarSum += equipUpdate.star - curEquip.star;
                    count++;
                }
            }
        } else { // 升小点，包括一键升到满小点和升一个小点
            let toStage = isOneClick? dicEquipStar.count: curEquip.starStage + 1;
            for(let stage = curEquip.starStage + 1; stage <= toStage; stage++) {
                let isEnough = await check.decrease(dicEquipStar.mainConsume);
                if(!isEnough) break; // 消耗不足
                equipUpdate.starStage = stage;
                count++;
            }
        }

        if(equipUpdate.star == curEquip.star && equipUpdate.starStage == curEquip.starStage) {
            return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);
        }
        let consumes = check.getConsume();
        let result = await handleCost(roleId, sid, consumes, ITEM_CHANGE_REASON.EQUIP_STARUP);
        if (!result) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);

        let { newEplace, updatedEplace, newEquip } = updateEplace(oldEplace, ePlaceId, equipUpdate);
        let update = {
            ePlace: newEplace,
            consumes: addConsumeToHero(hero.consumes, consumes)
        }

        // console.log('### incEquipStarSum', incEquipStarSum)
        let { curRole } = await calculateCeWithHero(HERO_SYSTEM_TYPE.EQUIP_STAR, roleId, serverId, sid, hid, update, { ePlaceId, skinId: hero.skinId, roleIncUpdate: { equipStarSum: incEquipStarSum } });

        checkTaskInEquipStarUp(serverId, roleId, sid, oldEplace, newEplace, ePlaceId, hid, isUpStar, count, hero.skinId);
        pushEquipStarMax(roleId, roleName, serverId, hid, newEquip, isUpStar);

        const curHero = {
            hid,
            isUpStar,
            ePlace: updatedEplace
        }
        return resResult(STATUS.SUCCESS, { curHero });

    }

    //镶嵌天晶石
    public async putOnJewel(msg: { hid: number, ePlaceId: number, jewel: number }, session: BackendSession) {
        let { hid, ePlaceId, jewel: seqId } = msg;
        let roleId: string = session.get('roleId');
        let sid: string = session.get('sid');
        let serverId: number = session.get('serverId');
        
        let hero = await HeroModel.findByHidAndRole(hid, roleId);
        if (!hero) return resResult(STATUS.HERO_NOT_FIND);

        let oldEplace = hero.ePlace||[];
        let curEquip = oldEplace.find(cur => cur.id == ePlaceId);
        if(!curEquip) return resResult(STATUS.EQUIP_NOT_FIND);
        let jewel = await JewelModel.findbySeqId(seqId);
        if(!jewel) return resResult(STATUS.JEWEL_IS_NOT_FIND);
        if(curEquip.jewel == seqId) return resResult(STATUS.JEWEL_HAS_SUIT);
        if(!checkJewelCanPutOnEquip(curEquip, jewel)) {
            return resResult(STATUS.EQUIP_NOT_MATCH_JEWEL)
        }

        let originHeroResult: { hid: number, ePlace: Partial<EPlace>[] };
        let originJewel = curEquip.jewel? await JewelModel.findbySeqId(curEquip.jewel): null; // 原本自己的天晶石
        let canSentMineToOrigin = false; // 自己的能不能塞到对方身上
        if(jewel.hid != 0) { // 如果天晶石原本镶嵌在其他武将身上，把自己的给他
            let originHero = await HeroModel.findByHidAndRole(jewel.hid, roleId);
            let originEplace = originHero?.ePlace||[];
            let originEquip = originEplace.find(cur => cur.jewel == seqId);
            if(originEquip) {
                let canChange = originJewel && checkJewelCanPutOnEquip(originEquip, originJewel);
                if(canChange) canSentMineToOrigin = true;
                let { newEplace, updatedEplace } = updateEplace(originEplace, ePlaceId, { jewel: canChange? originJewel.seqId: 0 });
                await calculateCeWithHero(HERO_SYSTEM_TYPE.EQUIP_JEWEL, roleId, serverId, sid, jewel.hid, { ePlace: newEplace }, { ePlaceId, jewel: canChange? originJewel: null, skinId: originHero.skinId });
                await checkTaskInPutJewel(serverId, roleId, sid, jewel.hid, originEplace, newEplace, ePlaceId, jewel, canChange? originJewel:null);

                originHeroResult = { hid: originHero.hid, ePlace: updatedEplace };  
            }
        }
        if(originJewel) { // 更新自己的天晶石
            originJewel = await JewelModel.putOnOrOff(originJewel.seqId, canSentMineToOrigin? jewel.hid: 0, canSentMineToOrigin? ePlaceId: 0);
        }

        // 目标镶嵌上
        let curJewel = await JewelModel.putOnOrOff(seqId, hid, ePlaceId);
        let { newEplace, updatedEplace } = updateEplace(oldEplace, ePlaceId, { jewel: seqId });
        await calculateCeWithHero(HERO_SYSTEM_TYPE.EQUIP_JEWEL, roleId, serverId, sid, hid, { ePlace: newEplace }, { ePlaceId, jewel: curJewel, skinId: hero.skinId });
        await checkTaskInPutJewel(serverId, roleId, sid, hid, oldEplace, newEplace, ePlaceId, originJewel, curJewel);

        let curHero = {
            hid,
            ePlace: updatedEplace
        }

        return resResult(STATUS.SUCCESS, { curHero, originHero: originHeroResult, curJewel, originJewel });
    }

    // 卸下天晶石
    public async putOffJewel(msg: { hid: number, ePlaceId: number }, session: BackendSession) {
        let { hid, ePlaceId } = msg;
        let roleId: string = session.get('roleId');
        let sid: string = session.get('sid');
        let serverId: number = session.get('serverId');
        
        let hero = await HeroModel.findByHidAndRole(hid, roleId);
        if (!hero) return resResult(STATUS.HERO_NOT_FIND);

        let oldEplace = hero.ePlace||[];
        let curEquip = oldEplace.find(cur => cur.id == ePlaceId);
        if(!curEquip) return resResult(STATUS.EQUIP_NOT_FIND);
        if(curEquip.jewel == 0) return resResult(STATUS.JEWEL_NOT_SUIT);

        let curJewel = await JewelModel.putOnOrOff(curEquip.jewel, 0, 0);
        let { newEplace, updatedEplace } = updateEplace(oldEplace, ePlaceId, { jewel: 0 });
        await calculateCeWithHero(HERO_SYSTEM_TYPE.EQUIP_JEWEL, roleId, serverId, sid, hid, { ePlace: newEplace }, { ePlaceId, jewel: null, skinId: hero.skinId });
        await checkTaskInPutJewel(serverId, roleId, sid, hid, oldEplace, newEplace, ePlaceId, null, curJewel);

        let curHero = {
            hid,
            ePlace: updatedEplace
        }

        return resResult(STATUS.SUCCESS, { curHero, curJewel });
    }

    // 装备或卸载地玉石
    public async putOnOrOffStone(msg: { hid: number, ePlaceId: number, stonesId: number, gid: number }, session: BackendSession) {
        let { hid, ePlaceId, stonesId, gid } = msg;
        let roleId: string = session.get('roleId');
        let roleName: string = session.get('roleName');
        let sid: string = session.get('sid');
        let serverId: number = session.get('serverId');

        let hero = await HeroModel.findByHidAndRole(hid, roleId);
        if (!hero) return resResult(STATUS.HERO_NOT_FIND);

        let oldEplace = hero.ePlace||[];
        let curEquip = oldEplace.find(cur => cur.id == ePlaceId);
        if(!curEquip) return resResult(STATUS.EQUIP_NOT_FIND);
        
        let curStone = curEquip.stones?.find(cur => cur.id == stonesId)||{ id: stonesId, stone: 0 };
        if(curStone.stone == gid) {
            return resResult(gid == 0? STATUS.STONE_NOT_SUIT: STATUS.STONE_HAS_SUIT);
        }
        if(!checkStoneCanPutOnEquip(curEquip, stonesId, gid)) { // 是否可以镶嵌
            return resResult(STATUS.STONE_CANNOT_SUIT);
        }

        let updateConsumes = hero.consumes;
        if(gid > 0) {
            updateConsumes = addConsumeToHero(updateConsumes, [{ id: gid, count: 1 }]);
            let consumeResult = await handleCost(roleId, sid, [{ id: gid, count: 1 }], ITEM_CHANGE_REASON.EQUIP_FILL_HOLE);
            if (!consumeResult) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);
        }

        if(curStone.stone > 0) { // 返回石头
            updateConsumes = addConsumeToHero(updateConsumes, [{ id: curStone.stone, count: -1 }]);
            await addItems(roleId, roleName, sid, [{ id: curStone.stone, count: 1 }], ITEM_CHANGE_REASON.EQUIP_FILL_HOLE);
        }

        let newStone = updateStone(curEquip.stones, stonesId, gid);
        let { newEplace, updatedEplace } = updateEplace(oldEplace, ePlaceId, { stones: newStone });
        let jewel = await JewelModel.findbySeqId(curEquip.jewel);
        let update = {
            ePlace: newEplace,
            consumes: updateConsumes,
        }
        await calculateCeWithHero(HERO_SYSTEM_TYPE.EQUIP_STONE, roleId, serverId, sid, hid, update, { ePlaceId, jewel, skinId: hero.skinId, stonesId });
        await checkTaskInPutStone(serverId, roleId, sid, hid, oldEplace, newEplace, ePlaceId, jewel);

        let curHero = {
            hid,
            ePlace: updatedEplace
        }

        return resResult(STATUS.SUCCESS, { curHero });
    }

    // 随机属性锁定
    public async lockRandSe(msg: { seqId: number, randSeId: number, type: number }, session: BackendSession) {
        let { seqId, randSeId, type } = msg;
        let roleId: string = session.get('roleId');
        let roleName: string = session.get('roleName');
        let sid: string = session.get('sid');

        let jewel = await JewelModel.findbySeqId(seqId);
        if(!jewel) return resResult(STATUS.JEWEL_NOT_FOUND);

        let { randSe } = jewel;
        if (!randSe || randSe.length <= 0) {
            return resResult(STATUS.JEWEL_HAVE_NO_RANDSE);
        }
        let curSe = randSe.find(cur => cur.id == randSeId);
        if (!curSe || (type == 1 && curSe.locked)) {
            return resResult(STATUS.JEWEL_DUPLICATE_LOCK);
        }

        if (type == 1) { // 仅在上锁时消耗，根据已有的锁的数量判断消耗
            let lockNum = randSe.filter(cur => cur.locked).length;
            let consumes: Array<{ id: number, count: number }> = [];
            if (lockNum == 0) {
                consumes = parseGoodStr(EQUIP.EQUIP_ONE_LOCKED);
            } else if (lockNum == 1) {
                consumes = parseGoodStr(EQUIP.EQUIP_TWO_LOCKED);
            } else if (lockNum == 2) {
                consumes = parseGoodStr(EQUIP.EQUIP_THREE_LOCKED);
            } else {
                return resResult(STATUS.ROLE_ALL_SE_LOCK);
            }
            let result = await handleCost(roleId, sid, consumes, ITEM_CHANGE_REASON.EQUIP_LOCK_RANDSE);
            if (!result) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);
        }

        let result = await JewelModel.lock(seqId, randSeId, type == 1);
        if (!result) {
            return resResult(STATUS.JEWEL_HAVE_NO_RANDSE);
        }
        return resResult(STATUS.SUCCESS, { curJewel: pick(result, ['seqId', 'id', 'hid', 'ePlaceId', 'randSe']) });

    }

    // 装备洗炼预览
    public async previewRandSe(msg: { seqId: number }, session: BackendSession) {

        let roleId: string = session.get('roleId');
        // let roleName: string = session.get('roleName');
        let sid: string = session.get('sid');

        let { seqId } = msg;
        let jewel = await JewelModel.findbySeqId(seqId);
        if(!jewel) return resResult(STATUS.JEWEL_NOT_FOUND);

        let { id, randSe, previewRandSe } = jewel;
        if(previewRandSe.length <= 0) {

            let previewRandSe = getRandSeResult(id, randSe);
            if(!previewRandSe) return resResult(STATUS.DIC_DATA_NOT_FOUND);

            let lockNum = randSe.reduce((pre, cur) => {
                return cur.locked? pre + 1: pre;
            }, 0);

            if (lockNum >= randSe.length) {
                return resResult(STATUS.ROLE_EQUIP_CANNOT_RESTRENGTHEN);
            }
            // 消耗
            let consumes: Array<{ id: number, count: number }> = [];
            if (lockNum == 0) {
                consumes = parseGoodStr(EQUIP.EQUIP_ONE_REFORGED);
            } else if (lockNum == 1) {
                consumes = parseGoodStr(EQUIP.EQUIP_TWO_REFORGED);
            } else if (lockNum == 2) {
                consumes = parseGoodStr(EQUIP.EQUIP_THREE_REFORGED);
            } else {
                consumes = parseGoodStr(EQUIP.EQUIP_FOUR_REFORGED);
            }

            let result = await handleCost(roleId, sid, consumes, ITEM_CHANGE_REASON.EQUIP_RESTRENGTHEN);
            if (!result) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);

            jewel = await JewelModel.updateInfo(seqId, { previewRandSe });
        }


        return resResult(STATUS.SUCCESS, { curJewel: pick(jewel, ['seqId', 'id', 'hid', 'ePlaceId', 'randSe', 'previewRandSe']) });

    }

    // 装备洗炼
    public async resetRandSe(msg: { seqId: number }, session: BackendSession) {
        let roleId: string = session.get('roleId');
        let serverId: number = session.get('serverId');
        let sid: string = session.get('sid');

        let { seqId } = msg;
        let jewel = await JewelModel.findbySeqId(seqId);
        if(!jewel) return resResult(STATUS.JEWEL_NOT_FOUND);

        let { randSe, hid, ePlaceId, previewRandSe } = jewel;
        if(!previewRandSe || previewRandSe.length <= 0) { // 没预览过
            return resResult(STATUS.EQUIP_RESTRENGTHEN_NOT_PREVIEW);
        }

        let newJewel = await JewelModel.updateInfo(seqId, { previewRandSe: [], randSe: previewRandSe });
        // 更新战力
        if(hid > 0) {
            const hero = await HeroModel.findByHidAndRole(hid, roleId);
            await calculateCeWithHero(HERO_SYSTEM_TYPE.JEWEL_RESET_RANDSE, roleId, serverId, sid, hid, { ePlace: hero.ePlace }, { ePlaceId, jewel: newJewel, skinId: hero.skinId });
        }

        await checkTaskInEquipReset(serverId, roleId, sid);        
        return resResult(STATUS.SUCCESS, { curJewel: pick(newJewel, ['seqId', 'id', 'hid', 'ePlaceId', 'randSe', 'previewRandSe']) });

    }

    // 放弃这条洗练
    public async giveupPreview(msg: { seqId: number }, session: BackendSession) {

        let { seqId } = msg;
        let jewel = await JewelModel.findbySeqId(seqId);
        if(!jewel) return resResult(STATUS.JEWEL_NOT_FOUND);

        let { randSe, previewRandSe } = jewel;
        if (!randSe || randSe.length <= 0) {
            return resResult(STATUS.JEWEL_HAVE_NO_RANDSE);
        }

        if(!previewRandSe) { // 没预览过
            return resResult(STATUS.JEWEL_NOT_PREVIEW);
        }

        let jewelResult = await JewelModel.updateInfo(seqId, { previewRandSe: [] })

        return resResult(STATUS.SUCCESS, { curJewel: pick(jewelResult, ['seqId', 'id', 'hid', 'ePlaceId', 'randSe', 'previewRandSe']) });

    }

    // 选择淬炼的那一条
    public async chooseQuench(msg: { seqId: number, randSeId: number }, session: BackendSession) {

        let { seqId, randSeId } = msg;
        let jewel = await JewelModel.findbySeqId(seqId);
        if(!jewel) return resResult(STATUS.JEWEL_NOT_FOUND);

        let { randSe } = jewel;
        if (!randSe || randSe.length <= 0) {
            return resResult(STATUS.JEWEL_HAVE_NO_RANDSE);
        }

        let quenchedSe = randSe.find(cur => cur.quenched);
        if(!!quenchedSe) return resResult(STATUS.JEWEL_HAS_CHOOSEN_QUENCH);

        let curRandSe = randSe.find(cur => cur.id == randSeId);
        if(!curRandSe) return resResult(STATUS.JEWEL_HAVE_NO_CUR_RANDSE);

        let jewelResult = await JewelModel.chooseQuench(seqId, randSeId);

        return resResult(STATUS.SUCCESS, { curJewel: pick(jewelResult, ['seqId', 'id', 'hid', 'ePlaceId', 'randSe', 'previewRandSe']) });
    }

    // 淬火
    public async quench(msg: { seqId: number }, session: BackendSession) {
        let roleId: string = session.get('roleId');
        let serverId: number = session.get('serverId');
        let sid: string = session.get('sid');
    
        let { seqId } = msg;
        let jewel = await JewelModel.findbySeqId(seqId);
        if(!jewel) return resResult(STATUS.JEWEL_NOT_FOUND);

        let { randSe, hid, ePlaceId } = jewel;
        if (!randSe || randSe.length <= 0) {
            return resResult(STATUS.JEWEL_HAVE_NO_RANDSE);
        }
        let quenchedSe = randSe.find(cur => cur.quenched);
        if(!quenchedSe) return resResult(STATUS.JEWEL_NOT_CHOOSEN_QUENCH);

        let dicJewel = gameData.jewel.get(jewel.id);
        if(!dicJewel) return resResult(STATUS.DIC_DATA_NOT_FOUND);

        let consumeResult = await handleCost(roleId, sid, dicJewel.quenchConsume, ITEM_CHANGE_REASON.EQUIP_QUENCH);
        if(!consumeResult) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);

        let newRandSe = getJewelRandSe(quenchedSe.id, quenchedSe.seid);
        let isSuccess = newRandSe.rand >= quenchedSe.rand;
        let newJewel = await JewelModel.quench(seqId, isSuccess, newRandSe.rand);
        // 更新战力
        if(isSuccess && hid > 0) {
            const hero = await HeroModel.findByHidAndRole(hid, roleId);
            await calculateCeWithHero(HERO_SYSTEM_TYPE.JEWEL_QUENCH, roleId, serverId, sid, hid, { ePlace: hero.ePlace }, { ePlaceId, jewel: newJewel, skinId: hero.skinId });
        }

        await checkTaskInEquipQuench(serverId, roleId, sid, isSuccess);        
        return resResult(STATUS.SUCCESS, { isSuccess, curJewel: pick(newJewel, ['seqId', 'id', 'hid', 'ePlaceId', 'randSe', 'previewRandSe']) });

    }

    // 地玉石和图纸分解
    public async decomposeItem(msg: { origin: { id: number, count: number }[] }, session: BackendSession) {
        let { origin } = msg;
        let roleId: string = session.get('roleId');
        let roleName: string = session.get('roleName');
        let sid: string = session.get('sid');
        let goods: ItemInter[] = [];

        for (let { id, count } of origin) {
            let dicGoods = gameData.goods.get(id);
            if (!dicGoods) return resResult(STATUS.DIC_DATA_NOT_FOUND);
            if (!dicGoods.decomposeItem || dicGoods.decomposeItem.length <= 0) return resResult(STATUS.CONSUME_TYPE_ERR);
            if (count < 0) return resResult(STATUS.WRONG_PARMS);

            for(let result of dicGoods.decomposeItem) {
                goods.push({ id: result.id, count: result.count * count });
            }
        }

        let costResult = await handleCost(roleId, sid, origin, ITEM_CHANGE_REASON.EQUIP_PIECE_DECOMPOSE);
        if(!costResult) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);
        let result = await addItems(roleId, roleName, sid, goods, ITEM_CHANGE_REASON.EQUIP_PIECE_DECOMPOSE);
        return resResult(STATUS.SUCCESS, { goods: combineItems(result) });
    }

    //分解装备
    public async decomposeJewel(msg: { origin: number[] }, session: BackendSession) {
        let { origin } = msg;
        let roleId: string = session.get('roleId');
        let roleName: string = session.get('roleName');
        let sid: string = session.get('sid');
        if(origin.length > BAG.BAG_RESOLVE_UPLIMITED) {
            return resResult(STATUS.EQUIP_DECOMPOSE_IS_UPLIMIT);
        }
        let jewels = await JewelModel.findbySeqIds(origin);
        if (jewels.length < origin.length) return resResult(STATUS.JEWEL_IS_NOT_FIND);

        let goods: RewardInter[] = [];
        let cost: ItemInter[] = [];

        for(let jewel of jewels) {
            if(jewel.hid > 0) {
                return resResult(STATUS.JEWEL_IS_EQUIPED);
            }
            let dicGoods = gameData.goods.get(jewel.id);
            cost.push({ seqId: jewel.seqId, id: jewel.id, count: 1 });
            goods.push(...dicGoods.decomposeItem);
        }

        let costResult = await handleCost(roleId, sid, cost, ITEM_CHANGE_REASON.EQUIP_DECOMPOSE); // 删掉装备
        if(!costResult) return resResult(STATUS.BATTLE_CONSUMES_NOT_ENOUGH);

        let result = await addItems(roleId, roleName, sid, goods, ITEM_CHANGE_REASON.EQUIP_DECOMPOSE);
        return resResult(STATUS.SUCCESS, { goods: combineItems(result) });
    }

    //宝石合成
    public async composeStone(msg: { id: number, count: number }, session: BackendSession) {
        let { id, count } = msg;
        let roleId: string = session.get('roleId');
        let roleName: string = session.get('roleName');
        let sid: string = session.get('sid');
        let serverId: number = session.get('serverId');
        
        let check = new CheckMeterial(roleId);
        let isEnough = await check.composeStone(id, count);
        if(!isEnough) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);

        let consumes = check.getConsume();
        let costResult = await handleCost(roleId, sid, consumes, ITEM_CHANGE_REASON.COMPOSE_STONE);
        if (!costResult) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);
        
        let goods = await addItems(roleId, roleName, sid, [{ id, count }], ITEM_CHANGE_REASON.COMPOSE_STONE);

        await checkTaskInComposeStone(serverId, roleId, sid, count);
        return resResult(STATUS.SUCCESS, { goods });
    }

    public async inheritJewel(msg: { originJewel: number, targetJewel: number }, session: BackendSession) {
        let { originJewel: originJewelId, targetJewel: targetJewelId } = msg;
        let roleId: string = session.get('roleId');
        let sid: string = session.get('sid');
        let serverId: number = session.get('serverId');

        let originJewel = await JewelModel.findbySeqId(originJewelId);
        if(!originJewel || originJewel.roleId != roleId) return resResult(STATUS.JEWEL_IS_NOT_FIND);
        if(originJewel.hid > 0) return resResult(STATUS.JEWEL_HAS_EQUPED);

        let targetJewel = await JewelModel.findbySeqId(targetJewelId);
        if(!targetJewel || targetJewel.roleId != roleId) return resResult(STATUS.JEWEL_IS_NOT_FIND);

        if(isLocked(targetJewel.randSe)) return resResult(STATUS.JEWEL_LOCKED_CANNOT_INHERIT);
        
        let dicOldJewel = gameData.jewel.get(originJewel.id);
        if(!dicOldJewel) return resResult(STATUS.DIC_DATA_NOT_FOUND);
        let dicJewel = gameData.jewel.get(targetJewel.id);
        if(!dicJewel) return resResult(STATUS.DIC_DATA_NOT_FOUND);
        if(dicJewel.eplaceId != dicOldJewel.eplaceId || dicJewel.lv < dicOldJewel.lv) {
            return resResult(STATUS.JEWEL_CANNOT_INHERIT);
        }

        // 消耗
        let consumeResult = await handleCost(roleId, sid, [
            { id: originJewel.id, seqId: originJewel.seqId},
            ...dicJewel.inheritConsume
        ], ITEM_CHANGE_REASON.JEWEL_INHERIT);
        if(!consumeResult) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);

        let newRandSe = getRandSeResult(targetJewel.id, targetJewel.randSe, originJewel.randSe, originJewel.id);
        let newJewel = await JewelModel.updateInfo(targetJewel.seqId, { previewRandSe: [], randSe: newRandSe });
        // 更新战力
        if(targetJewel.hid > 0) {
            const hero = await HeroModel.findByHidAndRole(targetJewel.hid, roleId);
            await calculateCeWithHero(HERO_SYSTEM_TYPE.JEWEL_RESET_RANDSE, roleId, serverId, sid, targetJewel.hid, { ePlace: hero.ePlace }, { ePlaceId: targetJewel.ePlaceId, jewel: newJewel, skinId: hero.skinId });

        }

        await checkTaskInEquipReset(serverId, roleId, sid);        
        return resResult(STATUS.SUCCESS, { curJewel: pick(newJewel, ['seqId', 'id', 'hid', 'ePlaceId', 'randSe', 'previewRandSe']) });
    }

}