import { Application, BackendSession, ChannelService, HandlerService, } from 'pinus';
import { handleCost, addItems, unlockFigure, getCoinObject, getGoldObject } from '../../../services/role/rewardService';
import { resResult, deepCopy, parseGoodStr } from '../../../pubUtils/util';
import { STATUS } from '../../../consts/statusCode';
import { HeroModel, Connect, HeroSkin, HeroUpdate, EPlace, Talent } from '../../../db/Hero';
import { CURRENCY_BY_TYPE, CURRENCY_TYPE, CONSUME_TYPE, HERO_GROW_MAX, HERO_SYSTEM_TYPE, ABI_STAGE, DEBUG_MAGIC_WORD, HERO_INITIAL_QUALITY, REDIS_KEY, TASK_TYPE, ITEM_CHANGE_REASON, CHECK_HERO_CONSUME } from '../../../consts';
import { RoleModel } from '../../../db/Role';
import { ItemModel } from '../../../db/Item';
import { gameData, getHeroExpByLv, getHeroStarByQuality, getHeroWakeByQuality, getHeroLvByExp, getMaxGradeByjobClass, getJobByGradeAndClass, getConnectLvByExp, getEquipByJobClassAndEPlace, getScollByStar, getExpByLv, getConnectMaxLv, getFriendShipByIdAndLv } from '../../../pubUtils/data';
import { ItemInter, RewardInter } from '../../../pubUtils/interface';
import { getDropItems, FIGURE_UNLOCK_CONDITION, ITID } from '../../../consts/constModules/itemConst'
import { pushHeroStarMax, pushHeroWakeUp } from '../../../services/chatService';
import { PvpDefenseModel } from '../../../db/PvpDefense';
import { checkTask, checkTaskInHeroGiveFavor, checkTaskInHeroQUalityUp, checkTaskInHeroStarUp, checkTaskInHeroTrain, checkTaskInHeroWakeUp } from '../../../services/task/taskService';
import { isNumber, pick } from 'underscore';
import { updateEplaces } from '../../../services/equipService';
import { addConnect, addConsumeToHero, calStarUpConsume, checkUnlockTalentCondition, initSkinTalent, updateSkinTalent } from '../../../services/roleService';
import { JewelModel, jewelUpdate } from '../../../db/Jewel';
import { HERO, REBORN } from '../../../pubUtils/dicParam';
import { createHero, createHeroes } from '../../../services/role/createHero';
import { CheckMeterial } from '../../../services/role/checkMaterial';
import { HeroParam } from '../../../domain/roleField/hero';
import { calculateCeWithHero } from '../../../services/playerCeService';
import { SchoolModel } from '../../../db/School';
import { SkinModel } from '../../../db/Skin';
import { RoleCeModel } from '../../../db/RoleCe';
import { saveRebirthLog } from '../../../pubUtils/logUtil';
import { isGoodsHidden, isHeroHidden } from '../../../services/dataService';
import { LadderMatchModel } from '../../../db/LadderMatch';
import { PvpSaveDataModel } from '../../../db/PvpSaveData';
import { ArtifactModel } from '../../../db/Artifact';

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

export class HeroHandler {
    constructor(private app: Application) {
    }

    // 武将碎片合成
    public async compose(msg: { hid: number }, session: BackendSession) {
            
        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 { hid } = msg;
        if(isHeroHidden(hid)) return resResult(STATUS.HERO_IS_HIDDEN);
        let dicHero = gameData.hero.get(hid);
        if (!dicHero) return resResult(STATUS.DIC_DATA_NOT_FOUND);

        // 检查是否存在武将
        let hasHero = await HeroModel.findByHidAndRole(hid, roleId);
        if (hasHero) return resResult(STATUS.ROLE_HERO_EXISTS);
        // 根据dic_hero 获得 1. 碎片id 2. 碎片数量 3. 初始武将星级 4. 初始品质
        let { pieceId, pieceCount } = dicHero;
        // 碎片数量是否足够
        let costResult = await handleCost(roleId, sid, [{ id: pieceId, count: pieceCount }], ITEM_CHANGE_REASON.COMPOSE_HERO);
        if (!costResult) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);
        // createHero
        let { heroes, resultHeroes } = await createHero(roleId, roleName, sid, serverId, { hid, count: 1 });

        return resResult(STATUS.SUCCESS, { curHero: {hid}, addHeros: heroes });
    }

    // 武将升级
    public async lvUp(msg: { hid: number, type: number }, session: BackendSession) {
        let roleId: string = session.get('roleId');
        let sid: string = session.get('sid');
        
        const serverId = session.get('serverId');

        let { hid, type } = msg;
        if(isHeroHidden(hid)) return resResult(STATUS.HERO_IS_HIDDEN);

        let addLv = 0;
        if (type == 1) {
            addLv = 1;
        } else if (type == 5) {
            addLv = 5;
        } else {
            return resResult(STATUS.ROLE_HERO_LV_TYPE_ERROR);
        }
        // 计算武将可以升的级数
        let hero = await HeroModel.findByHidAndRole(hid, roleId);
        if (!hero) return resResult(STATUS.ROLE_HERO_NOT_EXISTS);
        let { lv: playerLv } = await RoleModel.findByRoleId(roleId);
        let { lv: oldLv, exp: oldExp } = hero;
        if (oldLv >= playerLv) return resResult(STATUS.ROLE_HERO_LV_OVER);
        if (oldLv + addLv > playerLv) addLv = playerLv - oldLv;

        let nextExp = getHeroExpByLv(oldLv + addLv - 1);
        // console.log('nextExp', nextExp, oldLv + addLv - 1, oldExp)
        let needExp = nextExp - oldExp;
        let newExp = oldExp;

        // 计算得材料可转换的经验
        let originalConsumes = await ItemModel.findByRoleAndType(roleId, CONSUME_TYPE.EXP);
        let material = new Array<RewardInter>();
        for (let { id, count } of originalConsumes) {
            let dicGoods = gameData.goods.get(id);
            if (!dicGoods) return resResult(STATUS.DIC_DATA_NOT_FOUND);
            if(needExp < 0) break;
            let _count = Math.ceil(needExp / dicGoods.value);
            if (_count < count) {
                material.push({ id, count: _count });
                newExp += dicGoods.value * _count;
                needExp -= dicGoods.value * _count;
                break;
            } else {
                if(count > 0) {
                    material.push({ id, count });
                    newExp += dicGoods.value * count;
                    needExp -= dicGoods.value * count;
                }
            }
        }

        if (newExp == oldExp) {
            return resResult(STATUS.ROLE_EXP_NOT_ENOUGH);
        }

        let newLv = getHeroLvByExp(newExp);

        let costResult = await handleCost(roleId, sid, material, ITEM_CHANGE_REASON.HERO_LV_UP);
        if (!costResult) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);

        let update = {
            lv: newLv,
            exp: newExp,
            consumes: addConsumeToHero(hero.consumes, material)
        }

        let { curHero } = await calculateCeWithHero(HERO_SYSTEM_TYPE.LVUP, roleId, serverId, sid, hid, update);

        // 任务
        await checkTask(serverId, roleId, sid, TASK_TYPE.HERO_LV, { oldLv, hero: curHero });
        return resResult(STATUS.SUCCESS, { curHero: pick(curHero, ['hid', 'lv', 'exp']), cost: material });

    }

    // 武将升星
    public async starUp(msg: { hid: number, star: number, starStage: number, isOneClick: boolean }, session: BackendSession) {
        let roleId: string = session.get('roleId');
        const serverId = session.get('serverId');
        let sid: string = session.get('sid');
        
        let { hid, star, starStage, isOneClick } = msg;
        if(isHeroHidden(hid)) return resResult(STATUS.HERO_IS_HIDDEN);

        // 根据dic_hero 获得 1. 碎片id 2. 碎片数量 3. 初始武将星级 4. 初始品质
        let dicHero = gameData.hero.get(hid);
        if (!dicHero) return resResult(STATUS.DIC_DATA_NOT_FOUND);
        let { pieceId, jobid } = dicHero;
        let dicJob = gameData.job.get(jobid);
        if (!dicJob) return resResult(STATUS.DIC_DATA_NOT_FOUND);

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

        let { star: oldStar, starStage: oldStarStage, quality } = hero;
        // if (oldStar != star || oldStarStage != starStage) {
        //     return resResult(STATUS.WRONG_PARMS, { oldStarStage, oldStar, star, starStage });
        // }
        if (oldStar == HERO_GROW_MAX.STAR) {
            return resResult(STATUS.ROLE_STAR_REACH_MAX);
        }
        // 根据dic_zyz_hero_star 计算需要花的碎片并检查碎片数量
        const curDicHeroStar = getHeroStarByQuality(dicJob.job_class, quality, oldStar);
        if (!curDicHeroStar) return resResult(STATUS.DIC_DATA_NOT_FOUND);

        let newStarStage = oldStarStage;
        let max = isOneClick ? ABI_STAGE.END : oldStarStage + 1;
        let check = new CheckMeterial(roleId);
        for (let i = oldStarStage; i < max; i++) {

            let isEnough = await check.decrease([{ id: pieceId, count: curDicHeroStar.advanceUpFragmentNum }]);
            if(!isEnough) break; // 消耗不足
            newStarStage++;
        }
        if (newStarStage == oldStarStage) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);
        let consumes = check.getConsume();
        let result = await handleCost(roleId, sid, consumes, ITEM_CHANGE_REASON.HERO_STAR_UP);
        if (!result)
            return resResult(STATUS.BATTLE_CONSUMES_NOT_ENOUGH);

        let isUpStar = newStarStage == ABI_STAGE.END;

        let update = {
            star: isUpStar ? oldStar + 1 : oldStar,
            starStage: isUpStar ? ABI_STAGE.START : newStarStage,
        }
        if(CHECK_HERO_CONSUME) {
            update['consumes'] = addConsumeToHero(hero.consumes, consumes)
        }


        let { curHero } = await calculateCeWithHero(HERO_SYSTEM_TYPE.STAR, roleId, serverId, sid, hid, update, { hid, hero, isUpStar });
        if (isUpStar) {
            await checkTaskInHeroStarUp(serverId, roleId, sid, curHero, oldStar); // 任务
            await SchoolModel.updateByHid(roleId, hid, { hid, star: update.star });
        }
        return resResult(STATUS.SUCCESS, { isUpStar, curHero: pick(curHero, ['hid', 'star', 'starStage', 'colorStar', 'colorStarStage']) });
    }

    // 武将升品
    public async qualityUp(msg: { hid: number, quality: number }, session: BackendSession) {
        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 { hid, quality } = msg;
        if(isHeroHidden(hid)) return resResult(STATUS.HERO_IS_HIDDEN);

        let dicHero = gameData.hero.get(hid);
        if (!dicHero) return resResult(STATUS.DIC_DATA_NOT_FOUND);
        // 根据dic_hero 获得 碎片id
        let { pieceId } = dicHero;

        let hero = await HeroModel.findByHidAndRole(hid, roleId);
        if (!hero) return resResult(STATUS.ROLE_HERO_NOT_EXISTS);
        let { quality: oldQuality, star } = hero;
        // if (quality != oldQuality) {
        //     return resResult(STATUS.WRONG_PARMS);
        // }
        if (oldQuality == HERO_GROW_MAX.QUALITY) {
            return resResult(STATUS.ROLE_QUALITY_REACH_MAX);
        }
        if (star != HERO_GROW_MAX.STAR) {
            return resResult(STATUS.ROLE_STAR_NOT_ENOUGH);
        }

        // 根据dic_zyz_hero_quality_up 获得需要的材料
        const curDicHeroQualityUp = gameData.heroQualityUp.get(quality);
        if (!curDicHeroQualityUp) return resResult(STATUS.DIC_DATA_NOT_FOUND);
        let { fragmentNum } = curDicHeroQualityUp;

        let material = [{ id: pieceId, count: fragmentNum }];
        let costResult = await handleCost(roleId, sid, material, ITEM_CHANGE_REASON.HERO_QUALITY_UP);
        if (!costResult) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);

        let update = {
            quality: hero.quality + 1,
        }
        if(CHECK_HERO_CONSUME) {
            update['consumes'] = addConsumeToHero(hero.consumes, material)
        }

        await SchoolModel.updateByHid(roleId, hid, { hid, star: update.quality });
        let { curHero } = await calculateCeWithHero(HERO_SYSTEM_TYPE.QUALITY, roleId, serverId, sid, hid, update, { hero });
        await checkTaskInHeroQUalityUp(serverId, roleId, sid, curHero);
        // pushHeroQualityUpMsg(roleId, roleName, serverId, curHero);
        return resResult(STATUS.SUCCESS, { curHero: pick(curHero, ['hid', 'quality']) });
    }

    // 武将觉醒
    public async wakeUp(msg: { hid: number, colorStar: number, colorStarStage: number, isOneClick: boolean }, session: BackendSession) {

        let roleId: string = session.get('roleId');
        let sid: string = session.get('sid');
        let roleName: string = session.get('roleName');
        const serverId = session.get('serverId');
        
        let { hid, colorStar, colorStarStage, isOneClick } = msg;
        if(isHeroHidden(hid)) return resResult(STATUS.HERO_IS_HIDDEN);

        // 根据dic_hero 获得 1. 碎片id 2. 碎片数量 3. 初始武将星级 4. 初始品质
        let dicHero = gameData.hero.get(hid);
        if (!dicHero) return resResult(STATUS.DIC_DATA_NOT_FOUND);
        let { pieceId, jobid } = dicHero;
        let dicJob = gameData.job.get(jobid);
        if (!dicJob) return resResult(STATUS.DIC_DATA_NOT_FOUND);

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

        let { colorStar: oldColorStar, colorStarStage: oldColorStarStage, star, quality } = hero;
        // if (colorStar != oldColorStar || colorStarStage != oldColorStarStage) {
        //     return resResult(STATUS.WRONG_PARMS);
        // }
        if (star != HERO_GROW_MAX.STAR) {
            return resResult(STATUS.ROLE_WAKE_STAR_NOT_ENOUGH);
        }
        if (quality < HERO_GROW_MAX.QUALITY) {
            return resResult(STATUS.ROLE_QUALITY_NOT_ENOUGH);
        }
        // 根据dic_zyz_hero_wake 计算需要花的碎片并检查碎片数量
        const curDicHeroStar = getHeroWakeByQuality(dicJob.job_class, dicHero.quality, oldColorStar)
        if (!curDicHeroStar) return resResult(STATUS.DIC_DATA_NOT_FOUND);

        let newColorStarStage = oldColorStarStage;
        let isWakeUp = oldColorStar == 0;
        let check = new CheckMeterial(roleId);
        if(isWakeUp) {
            let { fragmentNum, consume } = curDicHeroStar;
            let isEnough = await check.decrease([{ id: pieceId, count: fragmentNum }, ...consume]);
            if(!isEnough) {
                return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);
            };
        } else {
            let max = isOneClick ? ABI_STAGE.END : oldColorStarStage + 1;
            for (let i = oldColorStarStage; i < max; i++) {
                let { fragmentNum, consume } = curDicHeroStar;
                let isEnough = await check.decrease([{ id: pieceId, count: fragmentNum }, ...consume]);
                if(!isEnough) break; // 消耗不足
                newColorStarStage++;
            }
            if (newColorStarStage == oldColorStarStage) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);
        }
        let consumes = check.getConsume();
        // console.log('&&&&&&&&7', JSON.stringify(consumes))
        let result = await handleCost(roleId, sid, consumes, ITEM_CHANGE_REASON.HERO_WAKE_UP);
        if (!result)
            return resResult(STATUS.BATTLE_CONSUMES_NOT_ENOUGH);

        let isUpStar = isWakeUp || newColorStarStage == ABI_STAGE.END;

        let update = {
            quality: isWakeUp? quality + 1: quality,
            colorStar: isUpStar ? oldColorStar + 1 : oldColorStar,
            colorStarStage: isUpStar ? ABI_STAGE.START : newColorStarStage,
            consumes: addConsumeToHero(hero.consumes, consumes)
        }

        let { curHero } = await calculateCeWithHero(HERO_SYSTEM_TYPE.COLORSTAR, roleId, serverId, sid, hid, update, { hero, isUpStar });
        if (isUpStar) {
            await SchoolModel.updateByHid(roleId, hid, { hid, star: update.colorStar });
            await checkTaskInHeroWakeUp(serverId, roleId, sid, curHero, oldColorStar); // 任务
        }
        if (isWakeUp) pushHeroWakeUp(roleId, roleName, serverId, curHero); // 第一次觉醒
        pushHeroStarMax(roleId, roleName, serverId, curHero);
        return resResult(STATUS.SUCCESS, { isUpStar, curHero: pick(curHero, ['hid', 'quality', 'star', 'starStage', 'colorStar', 'colorStarStage']) });
    }

    //训练
    async heroJobTrain(msg: { hid: number, isOneClick: boolean, canReplace: boolean }, session: BackendSession) {
        let roleId: string = session.get('roleId');
        let sid: string = session.get('sid');
        
        const serverId = session.get('serverId');
        let { hid, isOneClick, canReplace = false } = msg;
        if(isHeroHidden(hid)) return resResult(STATUS.HERO_IS_HIDDEN);

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

        let dicJob = gameData.job.get(hero.job);
        if (!dicJob) return resResult(STATUS.DIC_DATA_NOT_FOUND);

        if (hero.jobStage >= dicJob.maxStage)
            return resResult(STATUS.HERO_JOB_STAGE_REACH_MAX_STAGE);
        if (hero.job >= getMaxGradeByjobClass(dicJob.job_class))
            return resResult(STATUS.HERO_JOB_REACH_MAX_STAGE);

        let newJobStage = hero.jobStage;
        let oldJobStage = hero.jobStage, oldJob = hero.job;
        let max = isOneClick ? dicJob.maxStage: hero.jobStage + 1;
        let trainCount = 0;
        let check = new CheckMeterial(roleId);
        check.setCanReplace(canReplace);
        for(let i = hero.jobStage; i < max; i++) {
            let singleConsume = dicJob.trainingConsume[i];
            if(!singleConsume) break; // 每天消耗不可训练

            let isEnough = await check.decrease([singleConsume]);
            if(!isEnough) break; // 消耗不足
            newJobStage ++;
            trainCount++;
        }
        if (newJobStage == hero.jobStage) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);
        let consumes = check.getConsume();
        console.log('#### consumes', consumes);
        let result = await handleCost(roleId, sid, consumes, ITEM_CHANGE_REASON.HERO_JOB_TRAIN);
        if (!result)
            return resResult(STATUS.BATTLE_CONSUMES_NOT_ENOUGH);

        //重算战力并下发
        let update = {
            job: hero.job,
            jobStage: newJobStage,
            consumes: addConsumeToHero(hero.consumes, consumes)
        }
        let { curHero } = await calculateCeWithHero(HERO_SYSTEM_TYPE.TRAIN, roleId, serverId, sid, hid, update, { hero });
        await checkTaskInHeroTrain(serverId, roleId, sid, curHero, trainCount); // 任务
        return resResult(STATUS.SUCCESS, { curHero: pick(curHero, ['hid', 'job', 'jobStage'])});
    }

    //进阶 
    async heroJobStageUp(msg: { hid: number }, session: BackendSession) {
        let roleId: string = session.get('roleId');
        const serverId = session.get('serverId');
        let sid: string = session.get('sid');
        
        let { hid } = msg;
        if(isHeroHidden(hid)) return resResult(STATUS.HERO_IS_HIDDEN);

        let hero = await HeroModel.findByHidAndRole(hid, roleId);
        if (!hero)
            return resResult(STATUS.HERO_NOT_FIND);
        let curJob = hero.job;
        let heroJob = gameData.job.get(curJob);
        if (heroJob.unlockLevel > hero.lv)
            return resResult(STATUS.NOT_REACH_UNLOCK_LEVEL);
        if (curJob >= getMaxGradeByjobClass(heroJob.job_class))
            return resResult(STATUS.HERO_JOB_REACH_MAX_STAGE);
        let consumes = heroJob.upGradeConsume;
        let result = await handleCost(roleId, sid, consumes, ITEM_CHANGE_REASON.HERO_JOB_STAGEUP);
        if (!result) {
            return resResult(STATUS.BATTLE_CONSUMES_NOT_ENOUGH);
        }
        let nextHeroJob = getJobByGradeAndClass(heroJob.job_class, heroJob.grade + 1);
        if (!nextHeroJob) return resResult(STATUS.DIC_DATA_NOT_FOUND);

        //重算战力并下发
        let update = {
            job: nextHeroJob.jobid,
            jobStage: 0,
            consumes: addConsumeToHero(hero.consumes, consumes)
        }

        let { curHero } = await calculateCeWithHero(HERO_SYSTEM_TYPE.STAGEUP, roleId, serverId, sid, hid, update, { hid, hero });
        await checkTask(serverId, roleId, sid, TASK_TYPE.HERO_STAGE_UP, { hero: curHero, stageUpCnt: 1 }); // 任务
        const heroResult = new HeroParam(curHero);
        return resResult(STATUS.SUCCESS, { curHero: {...pick(heroResult, ['hid', 'job', 'jobStage', 'totalTalentPoint']) }});
    }

    //赠送（包括一键赠送）
    async heroGiveFavor(msg: { hid: number, shipId: number, type: number }, session: BackendSession) {
        let roleId: string = session.get('roleId');
        let sid: string = session.get('sid');
        let serverId: number = session.get('serverId');
        
        let { hid, shipId, type } = msg;
        if(isHeroHidden(hid)) return resResult(STATUS.HERO_IS_HIDDEN);

        let dicFriendShip = getFriendShipByIdAndLv(hid, shipId, 1);
        if(!dicFriendShip) return resResult(STATUS.DIC_DATA_NOT_FOUND);

        let heroes = await HeroModel.findByHidRange(dicFriendShip.hids, roleId);
        if (heroes.length < dicFriendShip.hids.length) return resResult(STATUS.ROLE_HERO_NOT_EXISTS);
        let hero = heroes.find(cur => cur.hid == hid);
        if(!hero) return resResult(STATUS.ROLE_HERO_NOT_EXISTS);
        let { connections = []} = hero;
        let curConnect = connections.find(cur => cur.shipId == shipId);
        let { level: oldLv = 0, exp: oldExp = 0 } = curConnect||{};

        let addLv = 0;
        if (type == 1) {
            addLv = 1;
        } else if (type == 5) {
            addLv = 5;
        } else {
            return resResult(STATUS.WRONG_PARMS);
        }

        let maxLv = getConnectMaxLv(hid, shipId); // 好感度最大等级
        if (maxLv <= oldLv)
            return resResult(STATUS.HERO_FAVOUR_LEVEL_REACH_MAXT);

        // 计算武将可以升的级数
        if (oldLv + addLv > maxLv) addLv = maxLv - oldLv;

        let nextObj = getFriendShipByIdAndLv(hid, shipId, oldLv + addLv);
        if (!nextObj) return resResult(STATUS.HERO_FAVOUR_LEVEL_REACH_MAXT);
        let nextExp = nextObj.shipExp;
        let needExp = nextExp - oldExp;
        let newExp = oldExp;

        // 计算得材料可转换的经验
        let originalConsumes = await ItemModel.findByRoleAndType(roleId, CONSUME_TYPE.FAVOUR);
        let material = new Array<RewardInter>();
        for (let { id, count } of originalConsumes) {
            let dicGoods = gameData.goods.get(id);
            if (!dicGoods) return resResult(STATUS.DIC_DATA_NOT_FOUND);
            let needCount = Math.ceil(needExp / dicGoods.value);
            if(needCount <= 0) break;
            if(needCount > count) needCount = count;
            material.push({ id, count: needCount });
            newExp += dicGoods.value * needCount;
            needExp -= dicGoods.value * count;
        }

        if (newExp == oldExp) {
            return resResult(STATUS.BATTLE_CONSUMES_NOT_ENOUGH);
        }

        let newLevel = getConnectLvByExp(hid, shipId, newExp);

        let result = await handleCost(roleId, sid, material, ITEM_CHANGE_REASON.HERO_GIVE_FAVOR);
        if (!result) {
            return resResult(STATUS.BATTLE_CONSUMES_NOT_ENOUGH);
        }

        let updateConsume = addConsumeToHero(hero.consumes, material);
        let newConnections = addConnect(connections, { shipId, level: newLevel, exp: newExp });
        let update = {
            connections: newConnections,
            consumes: updateConsume
        }

        let isLv = oldLv != newLevel;
        let { curHero } = await calculateCeWithHero(HERO_SYSTEM_TYPE.CONNECT, roleId, serverId, sid, hero.hid, update, { shipId });
        if(isLv) {
            // await unlockFigure(sid, roleId, [{ type: FIGURE_UNLOCK_CONDITION.HERO_FAVOR, paramHid: curHero.hid, paramFavourLv: curHero.favourLv }]);
            await checkTaskInHeroGiveFavor(serverId, roleId, sid, shipId, newConnections, connections);
        }
        return resResult(STATUS.SUCCESS, { curHero: { ...pick(curHero, ['hid', 'connections']), shipId } });
    }

    //穿带时装
    async heroWearSkin(msg: { id: number }, session: BackendSession) {
        let roleId: string = session.get('roleId');
        let sid: string = session.get('sid');
        let serverId: number = session.get('serverId');

        let { id } = msg;
        if(isGoodsHidden(id))  return resResult(STATUS.FASHION_IS_HIDDEN);
        let dicSkin = gameData.fashion.get(id);
        // console.log('*****', id, dicSkin)
        if (!dicSkin) return resResult(STATUS.HERO_SKIN_NOT_FIND);
        if(isHeroHidden(dicSkin.actorId)) return resResult(STATUS.HERO_IS_HIDDEN);

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

        let newHeroSkins: HeroSkin[] = [];
        let result = false;
        let lastSkinId: number;
        for (let skin of hero.skins) {
            if (skin.id == id) {
                if (!!skin.enable) {
                    return resResult(STATUS.HERO_SKIN_IS_EQUIPED);
                }
                newHeroSkins.push({ ...skin, enable: true });
                result = true;
            } else {
                if (!!skin.enable) {
                    lastSkinId = skin.skinId;
                }
                newHeroSkins.push({ ...skin, enable: false });
            }
        }
        if (!result) {
            return resResult(STATUS.HERO_SKIN_NOT_FIND);
        }
        
        let dicHero = gameData.hero.get(dicSkin.heroId);
        let dicMyJob = gameData.job.get(hero.job);
        let dicNewJob = getJobByGradeAndClass(dicHero.jobClass, dicMyJob.grade);

        let oldEplace = hero.ePlace||[];
        let eplaceIds = new Map<number, { equipId: number }>(); // 更新了的装备栏id
        for(let { id } of oldEplace) {
            let dicEquip = getEquipByJobClassAndEPlace(dicHero?.jobClass, id);
            eplaceIds.set(id, { equipId: dicEquip.id });
        }

        let { newEplace } = updateEplaces(oldEplace, eplaceIds);
        let update = {
            skins: newHeroSkins, 
            skinId: dicSkin.heroId, 
            job: dicNewJob.jobid, 
            ePlace: newEplace
        }
        let artifact = hero.artifact? await ArtifactModel.findbySeqId(roleId, hero.artifact): null;
        let { curHero } = await calculateCeWithHero(HERO_SYSTEM_TYPE.SKIN, roleId, serverId, sid, hero.hid, update, { hero, artifact });
        let resultHero = new HeroParam(curHero);
        return resResult(STATUS.SUCCESS, { curHero: {...pick(resultHero, ['hid', 'skins', 'skinId', 'job', 'talent', 'usedTalentPoint']), ePlace: newEplace }});
    }

    public async previewRebirth(msg: { hid: number }, session: BackendSession) {
        let roleId: string = session.get('roleId');
        let { hid } = msg;
        
        let hero = await HeroModel.findByHidAndRole(hid, roleId, 'hid consumes star starStage quality');
        if(!hero) return resResult(STATUS.HERO_NOT_FIND);
        let { consumes} = calStarUpConsume(hero);

        return resResult(STATUS.SUCCESS, { previewGoods: consumes.filter(consume => consume.count > 0) });
    }

    public async rebirth(msg: { hid: number }, session: BackendSession) {
        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 { hid } = msg;
        if(!isNumber(hid) && !hid) return resResult(STATUS.WRONG_PARMS);

        // 武将初始，但是皮肤这里不初始
        let hero = await HeroModel.findByHidAndRole(hid, roleId);
        if(!hero) return resResult(STATUS.HERO_NOT_FIND);

        // 消耗
        if(hero.lv >= REBORN.REBORON_FREE_LEVEL) {
            let cost = [getGoldObject(REBORN.REBORON_HERO)];
            let costResult = await handleCost(roleId, sid, cost, ITEM_CHANGE_REASON.REBIRTH);
            if(!costResult) return resResult(STATUS.BATTLE_GOLD_NOT_ENOUGH);
        }

        // 皮肤处理
        let { skins, skinId, ce, scrollActive, quality, star, starStage } = hero;
        let dicHero = gameData.hero.get(skinId);
        let dicJob = getJobByGradeAndClass(dicHero.jobClass, 0);

        let { consumes: consumesResult, newConsumes } = calStarUpConsume(hero);

        let newSkins = initSkinTalent(skins);
        let newQuality = quality >= 3? ( dicHero.quality >= 3? dicHero.quality: 3 ): quality;
        let dicHeroScroll = getScollByStar(dicHero.quality, star, newQuality, dicHero.initialColorStar);
        let initInfo = HeroModel.getInitInfo(hid, { 
            artifact: hero.artifact,
            job: dicJob.jobid, skins: newSkins, skinId, ce,
            star, starStage, colorStar: dicHero.initialColorStar, colorStarStage: 0, quality: newQuality,
            scrollActive: scrollActive, 
            scrollId: scrollActive? dicHeroScroll.id: 0,
            scrollStar: scrollActive? dicHeroScroll.stars: 0,
            scrollQuality: scrollActive? dicHeroScroll.qualityUp: 0,
            scrollColorStar: scrollActive? dicHeroScroll.colorstars: 0,
            consumes: newConsumes
        });

        // 天晶石
        let curJewels: jewelUpdate[] = [];
        for(let { jewel, id } of hero.ePlace) {
            if(jewel > 0) {
                let jewelInfo = await JewelModel.putOnOrOff(jewel, 0, id);
                curJewels.push({
                    seqId: jewelInfo.seqId,
                    id: jewelInfo.id,
                    hid: jewelInfo.hid,
                });
            }
        }

        let school = await SchoolModel.findByHid(roleId, hid);
        let { curHero } = await calculateCeWithHero(HERO_SYSTEM_TYPE.REBIRTH, roleId, serverId, sid, hid, initInfo, { schoolId: school?.schoolId });
        let goods = await addItems(roleId, roleName, sid, consumesResult, ITEM_CHANGE_REASON.REBIRTH);
        saveRebirthLog(session, hero);

        const heroResult = new HeroParam(curHero);
        return resResult(STATUS.SUCCESS, { curHero: heroResult, curJewels, goods });
    }

    // 解锁天赋
    public async unlockTalent(msg: { hid: number, id: number }, session: BackendSession) {
        let roleId = session.get('roleId');
        let sid = session.get('sid');
        let serverId = session.get('serverId');
        let { hid, id } = msg;

        if(isHeroHidden(hid)) return resResult(STATUS.HERO_IS_HIDDEN);

        let dicHeroTalent = gameData.heroTalent.get(id);
        if(!dicHeroTalent) return resResult(STATUS.DIC_DATA_NOT_FOUND);

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

        let skins = hero.skins||[];
        let curSkin = skins.find(cur => cur.enable);
        if(!curSkin) return resResult(STATUS.HERO_SKIN_NOT_FIND);

        let talent = curSkin.talent.find(cur => cur.id == id);
        if(talent) return resResult(STATUS.TALENT_HAS_UNLOCKED);

        let usedTalentPoint = curSkin.usedTalentPoint;
        if(!checkUnlockTalentCondition(curSkin.skinId, id, curSkin.talent, usedTalentPoint)) {
            return resResult(STATUS.TALENT_CONDITION_NOT_FIT);
        }

        let totalTalentPoint = gameData.talentPointOfJob.get(hero.job);
        let needTalentPoint = dicHeroTalent.level.find(cur => cur.lv == 1)?.cost||0;
        if(totalTalentPoint - usedTalentPoint - needTalentPoint < 0) {
            return resResult(STATUS.TALENT_POINT_NOT_ENOUGH);
        }

        let { newSkins } = updateSkinTalent(skins, new Talent(id), usedTalentPoint + needTalentPoint);

        let { curHero } = await calculateCeWithHero(HERO_SYSTEM_TYPE.TALENT_UNLOCK, roleId, serverId, sid, hid, { skins: newSkins }, { talentId: id });
        const heroResult = new HeroParam(curHero);
        return resResult(STATUS.SUCCESS, { curHero: {...pick(heroResult, ['hid', 'talent', 'usedTalentPoint', 'totalTalentPoint']) }});

    }

    // 升级天赋
    public async upgradeTalent(msg: { hid: number, id: number }, session: BackendSession) {
        let roleId = session.get('roleId');
        let sid = session.get('sid');
        let serverId = session.get('serverId');
        let { hid, id } = msg;

        if(isHeroHidden(hid)) return resResult(STATUS.HERO_IS_HIDDEN);

        let dicHeroTalent = gameData.heroTalent.get(id);
        if(!dicHeroTalent) return resResult(STATUS.DIC_DATA_NOT_FOUND);

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

        let skins = hero.skins||[];
        let curSkin = skins.find(cur => cur.enable);
        if(!curSkin) return resResult(STATUS.HERO_SKIN_NOT_FIND);

        let talent = curSkin.talent.find(cur => cur.id == id);
        if(!talent) return resResult(STATUS.TALENT_NOT_UNLOCKED);

        let usedTalentPoint = curSkin.usedTalentPoint;
        let totalTalentPoint = gameData.talentPointOfJob.get(hero.job);
        let nextLv = dicHeroTalent.level.find(cur => cur.lv == talent.level + 1);
        if(!nextLv) return resResult(STATUS.TALENT_LEVEL_MAX);
        let needTalentPoint = nextLv.cost||0;
        if(totalTalentPoint - usedTalentPoint - needTalentPoint < 0) {
            return resResult(STATUS.TALENT_POINT_NOT_ENOUGH);
        }

        let { newSkins } = updateSkinTalent(skins, new Talent(id, talent.level + 1), usedTalentPoint + needTalentPoint);

        let { curHero } = await calculateCeWithHero(HERO_SYSTEM_TYPE.TALENT_LV, roleId, serverId, sid, hid, { skins: newSkins }, { talentId: id });
        const heroResult = new HeroParam(curHero);
        return resResult(STATUS.SUCCESS, { curHero: {...pick(heroResult, ['hid', 'talent', 'usedTalentPoint', 'totalTalentPoint']) }});
    }

    // 洗点
    public async resetTalent(msg: { hid: number }, session: BackendSession) {
        let roleId = session.get('roleId');
        let sid = session.get('sid');
        let serverId = session.get('serverId');
        let { hid } = msg;

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

        let skins = hero.skins||[];
        let curSkin = skins.find(cur => cur.enable);
        if(!curSkin) return resResult(STATUS.HERO_SKIN_NOT_FIND);

        let costItem = parseGoodStr(HERO.HERO_TALENTPOINT_RESET);
        let consumeResult = await handleCost(roleId, sid, costItem, ITEM_CHANGE_REASON.RESET_TALENT);
        if(!consumeResult) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);


        let newSkins = initSkinTalent(skins);
        let { curHero } = await calculateCeWithHero(HERO_SYSTEM_TYPE.TALENT_RESET, roleId, serverId, sid, hid, { skins: newSkins });

        const heroResult = new HeroParam(curHero);
        return resResult(STATUS.SUCCESS, { curHero: {...pick(heroResult, ['hid', 'talent', 'usedTalentPoint', 'totalTalentPoint']) }});
    }


    // 设置副将
    async setSubHero(msg: { hid: number, subHid: number }, session: BackendSession) {
        let roleId = session.get('roleId');
        let { hid, subHid: subSkinId } = msg;

        let dicHero = gameData.hero.get(hid);
        if(!dicHero || dicHero.urType != 1) return resResult(STATUS.HERO_CAN_NOT_SET_SUB);

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

        let subHid = 0;
        if(subSkinId > 0) { // 设置上
            if(hero.subHid == subSkinId) return resResult(STATUS.HERO_SUB_DUPLICATE);
            let dicHero = gameData.hero.get(subSkinId);
            if(!dicHero || dicHero.urType == 1) return resResult(STATUS.HERO_CAN_NOT_BE_SET_SUB);
            subHid = dicHero.actorId;

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

            await LadderMatchModel.removeBySub(roleId, subHid);
            await PvpSaveDataModel.removeBySub(roleId, subHid);
            await PvpDefenseModel.removeBySub(roleId, subHid);

        } else { // 卸下
            if(!hero.subHid) return resResult(STATUS.HERO_CAN_NOT_REMOVE_SUB);
        }

        // 设置副将
        const { preHid, curHero } = await HeroModel.setSubHero(roleId, hid, subSkinId, subHid);

        return resResult(STATUS.SUCCESS, { preHid, curHero: pick(curHero, ['hid', 'subHid']) });
    }

    // ! debug接口 一键全武将
    public async debugGetAllHeroes(msg: { magicWord: string }, session: BackendSession) {
        let roleId: string = session.get('roleId');
        let roleName: string = session.get('roleName');
        let sid: string = session.get('sid');
        let serverId: number = session.get('serverId');
        

        const { magicWord } = msg;
        if (magicWord !== DEBUG_MAGIC_WORD) {
            return resResult(STATUS.TOKEN_ERR);
        }

        await HeroModel.deleteAccount(roleId);
        await JewelModel.deleteAccount(roleId);
        await SkinModel.deleteMany({ roleId });
        await SchoolModel.deleteAccount(roleId);
        await RoleCeModel.deleteMany({ roleId });
        await RoleModel.updateRoleInfo(roleId, { jewelCount: 0 });
        let heroInfos = [];

        for(let [actorId] of gameData.recruit) {
            let dicHero = gameData.hero.get(actorId);
            if(!dicHero) continue;
            let { jobid, grade } = gameData.jobClassMaxGrades.get(dicHero.jobClass);
            heroInfos.push({
                hid: actorId, star: 6, colorStar: 6, quality: 4, lv: 100, exp: getExpByLv(100).sum, job: jobid, grade
            });
        }
        let resultHeroes = await createHeroes(roleId, roleName, sid, serverId, heroInfos);

        return resResult(STATUS.SUCCESS, resultHeroes);
    }


    // ! 测试接口，用来测试前清理并初始化某个武将；代码抄自：gm-server/app/service/users.ts deleteHero
    public async testCleanUp(msg: { magicWord: string, hid: number }, session: BackendSession) {
        let roleId: string = session.get('roleId');
        let roleName: string = session.get('roleName');
        let sid: string = session.get('sid');

        const { magicWord, hid } = msg;
        if (magicWord !== DEBUG_MAGIC_WORD) {
            return resResult(STATUS.TOKEN_ERR);
        }

        let dicHero = gameData.hero.get(hid);
        if (!dicHero) return resResult(STATUS.HERO_NOT_FIND);
        await addItems(roleId, roleName, sid, [{
            id: dicHero.pieceId, count: dicHero.pieceCount * HERO_GROW_MAX.STAR * ABI_STAGE.END
        }], ITEM_CHANGE_REASON.DEBUG);

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

        await HeroModel.deleteHero(roleId, hid);
        let role = await RoleModel.findByRoleId(roleId);
        await PvpDefenseModel.deleteHero(roleId, hid);
        await RoleModel.updateRoleInfo(roleId, { topLineup: role.topLineup, topLineupCe: role.topLineupCe, ce: role.ce - hero.ce });
        return resResult(STATUS.SUCCESS);
    }

    public async addItem(msg: { id: number, count: number }, session: BackendSession) {
        let roleId: string = session.get('roleId');
        let roleName: string = session.get('roleName');
        let sid: string = session.get('sid');

        let { id, count } = msg;

        //let result = await handleCost(roleId, sid, [{id, count}] );
        let items = [{ id, count }];
        let role = await RoleModel.findByRoleId(roleId);
        if (id == 999999) {
            items = getDropItems();
            if (role.lv < 60) {
                role.lv = 60;
                let roleLvInfo = gameData.kingexp.get(role.lv - 1);
                role.exp = roleLvInfo.sum;
                await RoleModel.updateRoleInfo(roleId, role);
            }
        }
        let result = await addItems(roleId, roleName, sid, items, ITEM_CHANGE_REASON.DEBUG);
        if (!result) {
            return resResult(STATUS.BATTLE_CONSUMES_NOT_ENOUGH);
        }
        return resResult(STATUS.SUCCESS, { goods: result, lv: role.lv, exp: role.exp });

    }

    public async debugSetHero(msg: { magicWord: string, hid: number, params: HeroUpdate }, session: BackendSession) {
        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 { magicWord, hid, params } = msg;
        if (magicWord !== DEBUG_MAGIC_WORD) {
            return resResult(STATUS.TOKEN_ERR);
        }
        let hero = await HeroModel.findByHidAndRole(hid, roleId);
        if(!hero) {
            let { resultHeroes } = await createHero(roleId, roleName, sid, serverId, { hid, count: 1, ...params });
            return resResult(STATUS.SUCCESS, { hero: resultHeroes[0] });
        } else {
            hero = await HeroModel.updateHeroInfo(roleId, hid, params);
            return resResult(STATUS.SUCCESS, { hero: new HeroParam(hero) });
        }
    
    }
}
