import { Application, BackendSession, HandlerService, } from 'pinus';
import { resResult, genCode, getRandSingleEelm } from '../../../pubUtils/util';
import { DEBUG_MAGIC_WORD, ITEM_CHANGE_REASON, STATUS, TASK_TYPE, TRAIN_REWARD_TYPE } from '../../../consts';
import { GuildTrainModel } from '../../../db/GuildTrain';
import { BattleRecordModel } from '../../../db/BattleRecord';
import { nowSeconds, getTimeFun, getZeroPoint } from '../../../pubUtils/timeUtil';
import { refreshTrain, getGuildTrainInfo, unlockTrain, getGuildTrainRewards, getGuildTrainInstance, getTrainBoxRewardsResult, resetTrain, getTrainBoxRewardsResultWithoutAlreadyGeted } from '../../../services/guildTrainService';
import { findIndex, findWhere } from 'underscore'
import { lockData } from '../../../services/redLockService';
import { GUILD_REPORT_NUM, GUILD_POINT_WAYS } from '../../../consts/constModules/guildConst';
import { UserGuildModel } from '../../../db/UserGuild';
import { GuildModel } from '../../../db/Guild';
import { getArmyTrainJuDian, getTrainBaseByLv, gameData } from '../../../pubUtils/data';
import { CURRENCY_BY_TYPE, CURRENCY_TYPE } from '../../../consts/constModules/itemConst';
import { handleCost, addItems } from '../../../services/role/rewardService';
import { ARMY } from '../../../pubUtils/dicParam';
import { addActive } from '../../../services/guildService';
import { GuildTrainReportModel } from '../../../db/GuildTrainReport';
import { DATA_NAME } from '../../../consts/dataName';
import { pushGuildTrainSucMsg } from '../../../services/chatService';
import { checkTaskInGuildTrain } from '../../../services/task/taskService';
import { guildInter, RewardInter } from '../../../pubUtils/interface';
import { getGuildTrainGkInfo } from '../../../pubUtils/data';

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

export class GuildTrainHandler {
    constructor(private app: Application) {

    }

    //获得试炼的详情
    async getTrainInstance(msg: guildInter & {}, session: BackendSession) {
        const roleId: string = session.get('roleId');
        const serverId: number = parseInt(session.get('serverId'));
        const { myUserGuild } = msg;
        let userGuild = await refreshTrain(myUserGuild, roleId, serverId);
        if (!userGuild)
            return resResult(STATUS.WRONG_PARMS);
        const { guildCode: code } = userGuild;
        let guild = await GuildModel.findGuild(code, serverId, 'trainId trainLv code');
        let result = await getGuildTrainInstance(roleId, guild, userGuild);
        return resResult(STATUS.SUCCESS, result);
    }
    //获得试炼战报
    async getTrainReports(msg: guildInter & {}, session: BackendSession) {
        const roleId: string = session.get('roleId');
        const serverId: number = parseInt(session.get('serverId'));
        const { myUserGuild } = msg;
        let userGuild = await refreshTrain(myUserGuild, roleId, serverId);
        if (!userGuild)
            return resResult(STATUS.WRONG_PARMS);
        const { guildCode: code } = userGuild;
        let trainIds = [];
        let { trainId } = await GuildModel.findGuild(code, serverId, 'trainId');
        trainIds.push(trainId);
        if (trainId > 1)
            trainIds.push(trainId - 1);
        let trainReports = await GuildTrainReportModel.findGuildTrainByTrainIds(code, trainIds);
        let reports = [];
        trainReports.map(({ reports: resReports, trainId: resTrainId }) => {
            if (resTrainId != trainId) {
                let lenNum = resReports.length;
                resReports = resReports.splice(lenNum - GUILD_REPORT_NUM - 1, GUILD_REPORT_NUM);//获得上场的战报的信息与本次战报合并
            }
            reports.push(...resReports);
        });
        return resResult(STATUS.SUCCESS, { reports });
    }

    //获得试炼宝箱
    async getTrainBoxs(msg: guildInter & {}, session: BackendSession) {
        const roleId: string = session.get('roleId');
        const serverId: number = parseInt(session.get('serverId'));
        const { myUserGuild } = msg;
        let userGuild = await refreshTrain(myUserGuild, roleId, serverId);
        if (!userGuild)
            return resResult(STATUS.WRONG_PARMS);
        const { guildCode: code,  trainBoxRewards = []} = userGuild;
        let alreadyGetTrainBoxReards: { [hid: number]: string[] } = {}; // {英雄id:[军团id]}
        trainBoxRewards.forEach(item => {
            if (typeof item != 'object' || !item.hid || !item.guildCode) {
                return;
            }
            if (!alreadyGetTrainBoxReards[item.hid]) {
                alreadyGetTrainBoxReards[item.hid] = [];
            }
            alreadyGetTrainBoxReards[item.hid].push(item.guildCode);
        });
        let resTrainBoxs = await getTrainBoxRewardsResultWithoutAlreadyGeted(code, alreadyGetTrainBoxReards);
        return resResult(STATUS.SUCCESS, { trainBoxRewards: resTrainBoxs });
    }
    /**
     * 挑战练兵副本
     * @param msg 
     * @param session 
     */
    async trainBattleStart(msg: guildInter & { hid: number, trainId: number, difficulty: number, battleId: number }, session: BackendSession) {
        const { hid, difficulty, trainId, myUserGuild, battleId } = msg;
        const roleId = session.get('roleId');
        const roleName = session.get('roleName');
        const serverId = parseInt(session.get('serverId'));
        let userGuild = await refreshTrain(myUserGuild, roleId, serverId);
        if (!userGuild)
            return resResult(STATUS.WRONG_PARMS);
        const { guildCode: code } = userGuild;
        if (userGuild.trainCount <= 0)
            return resResult(STATUS.GUILD_TRAIN_BATTLE_COUNT_NOT_ENOUGH);
        let { trainId: curTeainId, trainLv } = await GuildModel.findGuild(code, serverId, 'trainId trainLv');
        if (curTeainId !== trainId)
            return resResult(STATUS.GUILD_TRAIN_LEVEL_IS_COMPLETE);
        let guildTrain = await GuildTrainModel.findTrainByTrainIdNotLock(code, trainId, 'trainId isComplete trainInstances');
        if (!guildTrain && guildTrain.isComplete)
            return resResult(STATUS.GUILD_TRAIN_SCRIPT_NOT_OPENED);
        let { trainInstances } = getArmyTrainJuDian(trainId);
        let instance = findWhere(trainInstances, { hid });
        if (!instance)
            return resResult(STATUS.WRONG_PARMS);
        let trainInstance = findWhere(guildTrain.trainInstances, { hid });
        if (!trainInstance)
            return resResult(STATUS.WRONG_PARMS);
        if (trainInstance.progress >= instance.progress) {
            return resResult(STATUS.GUILD_TRAIN_IS_COMPLETE);
        }
        const battleCode = genCode(8); // 关卡唯一值
        await BattleRecordModel.updateBattleRecordByCode(battleCode, {
            $set: {
                roleId, roleName, battleId,
                status: 0,
                record: { heroes: [], trainId, hid, guildCode: code, difficulty, trainLv },
            }
        }, true);
        return resResult(STATUS.SUCCESS, { battleCode });
    }
    /**
     * 战斗结算
     * @param msg 
     * @param session 
     */
    async trainBattleEnd(msg: guildInter & { battleCode: string, isSuccess: boolean }, session: BackendSession) {
        
        const { battleCode, isSuccess, myUserGuild } = msg;
        const roleId: string = session.get('roleId');
        const serverId: number = parseInt(session.get('serverId'));
        const roleName: string = session.get('roleName');
        const sid: string = session.get('sid');
        
        let userGuild = await refreshTrain(myUserGuild, roleId, serverId);
        if (!userGuild)
            return resResult(STATUS.WRONG_PARMS);
        const { guildCode: code } = userGuild;
        const battleRecord = await BattleRecordModel.getBattleRecordByCode(battleCode, true);
        if (!battleRecord || battleRecord.status != 0 || roleId != battleRecord.roleId || battleRecord.record.guildCode != code) {
            return resResult(STATUS.WRONG_PARMS);
        }
        let time = Math.floor(battleRecord.createdAt.getTime() / 1000);
        if (userGuild.trainCount - 1 < 0) {
            return resResult(STATUS.WRONG_PARMS);
        }

        let todayRef = <number>getTimeFun().getTimeWithHour(); // 今天的5点
        if (time < todayRef && nowSeconds() > todayRef) {
            return resResult(STATUS.GUILD_TRAIN_IS_RESETED);//重置前进入战斗，重置后回调该接口
        }
        if (time > getZeroPoint()) { // 刷新时间，如果是4点算前一天的5点
            userGuild = await UserGuildModel.updateInfo(roleId, {}, { trainCount: -1 });//扣除一次挑战次数
        }
        await BattleRecordModel.updateBattleRecordByCode(battleCode, {
            $set: { status: isSuccess ? 1 : 2 }
        }, true);//设置战斗状态

        let { trainId, trainLv, hid, difficulty } = battleRecord.record;
        let maxTrainId = gameData.lastGuildTrainIdOfLv.get(trainLv);
        let res: any = await lockData(serverId, DATA_NAME.TRAIN, code + '_' + trainId);//加锁
        if (!!res.err)
            return resResult(STATUS.REDLOCK_ERR);
        try {
            
        let guildTrain = await GuildTrainModel.findTrainByTrainIdNotLock(code, trainId);
        if (!guildTrain) {
            res.releaseCallback();//解锁
            return resResult(STATUS.GUILD_TRAIN_SCRIPT_NOT_OPENED);
        }
        let trainInstance = findWhere(guildTrain.trainInstances, { hid });
        if (!trainInstance) {
            res.releaseCallback();//解锁
            return resResult(STATUS.WRONG_PARMS);
        }

        let { trainInstances, soloRewardRatio } = getArmyTrainJuDian(trainId);
        let dicWar = gameData.war.get(battleRecord.battleId);
        if(!dicWar) return resResult(STATUS.DIC_DATA_NOT_FOUND);

        let instance = findWhere(trainInstances, { hid });
        if (!instance) {//校验是否存在该关卡
            res.releaseCallback();//解锁
            return resResult(STATUS.WRONG_PARMS);
        }
        let score = isSuccess ? dicWar.winReward.get(TRAIN_REWARD_TYPE.SCORE) : dicWar.failReward.get(TRAIN_REWARD_TYPE.SCORE);
        let item = isSuccess ? dicWar.winReward.get(TRAIN_REWARD_TYPE.ITEM) : dicWar.failReward.get(TRAIN_REWARD_TYPE.ITEM);
        let addScore = Math.floor(<number>score);//个人获得积分，也是压制进度
        //个人功勋奖励
        let goods = await addItems(roleId, roleName, sid, <RewardInter[]>item, ITEM_CHANGE_REASON.TRAIN_BATTLE_END);
        let { isComplete, ranks } = guildTrain;
        let reports = [];
        // let index = findIndex(ranks, { roleId });
        // if (index !== -1) {
        //     ranks[index].score += addScore;//更新原有的排名信息
        // } else {
        //     ranks.push({ score: addScore, roleId });//新增排名
        // }
        let needLockNext = false;
        let report = { roleName, trainId, hid, score: addScore, time: nowSeconds(), type: isSuccess ? 2 : 1, difficulty };//type 1：失败， 2：成功，3：表示系统战报即：被成功压制
        reports.push(report);
        if (trainInstance.progress < instance.progress) {
            if (trainInstance.progress + addScore >= instance.progress) {
                //压制成功
                if (!isComplete) {
                    isComplete = true;
                    guildTrain.trainInstances.forEach(({ hid: otherHid, progress }) => {
                        if (hid != otherHid && progress != instance.progress) {
                            isComplete = false;
                        }
                    });
                    if (isComplete) { //解锁下一关
                        needLockNext = true;
                    }
                }
                let progress = instance.progress;
                guildTrain = await GuildTrainModel.updateGuildTrainProgress(code, trainId, hid, progress, ranks, isComplete);

                //type 1：失败， 2：成功，3：表示系统战报即：被成功压制
                reports.push({ type: 3, time: nowSeconds(), score: addScore, roleName, trainId, hid, difficulty });
                pushGuildTrainSucMsg(roleId, roleName, code, hid);


                if (needLockNext && maxTrainId > trainId) {
                    guildTrain = await unlockTrain(code, trainId + 1);
                }
                res.releaseCallback();//解锁
            } else {
                //压制失败，更新排行榜进度等信息
                let progress = trainInstance.progress + addScore;
                guildTrain = await GuildTrainModel.updateGuildTrainProgress(code, trainId, hid, progress, ranks, isComplete);
                res.releaseCallback();//解锁
            }
        } else {
            // //玩家结算前已经完成进度，修改玩家的排名
            // guildTrain = await GuildTrainModel.updateGuildTrain(code, trainId, { ranks });
            res.releaseCallback();//解锁
        }

        await GuildTrainReportModel.pushGuildTrainReports(code, trainId, reports);//增加战报
        let { trainCount, trainRewards } = userGuild;
        let result: any = getGuildTrainInfo(guildTrain, roleId, trainCount, trainRewards);//战斗后更新练兵场信息
        result.battleGoods = goods;
        result.createdAt = time;
        await addActive(roleId, serverId, GUILD_POINT_WAYS.TRAIN);

        // 任务
        await checkTaskInGuildTrain(serverId, roleId, sid, battleRecord.battleId, isSuccess, isComplete);

        return resResult(STATUS.SUCCESS, result);

    } catch(e) {
        res.releaseCallback();//解锁
        throw e;
    }
    }
    /**
     * 领取练兵场宝箱 hid：副本id，index:领取位置
     * @param msg 
     * @param session 
     */
    async getTrainInstanceBox(msg: guildInter & { trainId: number, hid: number, index: number }, session: BackendSession) {
        let { trainId, hid, index, myUserGuild } = msg;
        const roleId: string = session.get('roleId');
        const serverId: number = parseInt(session.get('serverId'));
        const roleName: string = session.get('roleName');
        const sid = session.get('sid');
        let userGuild = await refreshTrain(myUserGuild, roleId, serverId);
        if (!userGuild)
            return resResult(STATUS.WRONG_PARMS);
        const { guildCode: code } = userGuild;
        let { trainLv } = await GuildModel.findGuild(code, serverId, 'trainLv');
        let res: any = await lockData(serverId, DATA_NAME.TRAIN_BOX, code + '_' + trainId + '_' + index);//加锁
        if (!!res.err)
            return resResult(STATUS.REDLOCK_ERR);

        let { trainInstances } = getArmyTrainJuDian(trainId);
        let trainInfo = getGuildTrainGkInfo(trainId, hid);
        let { progress } = findWhere(trainInstances, { hid });
        // console.log('progress = ' + progress);
        let guildTrain = await GuildTrainModel.findTrainInstanceBoxByIndex(code, trainId, hid, progress);
        if (!guildTrain) {
            res.releaseCallback();//解锁
            return resResult(STATUS.GUILD_TRAIN_SCRIPT_NOT_OPENED);
        }
        let trainInstance = findWhere(guildTrain.trainInstances, { hid });
        if (trainInstance.endTime < nowSeconds()) {
            //宝箱失效
            res.releaseCallback();//解锁
            return resResult(STATUS.GUILD_TRAIN_BOX_IS_OVER_TIME);
        }
        let trainBox = findWhere(trainInstance.trainBoxs, { index })
        if (!!trainBox) {
            res.releaseCallback();//解锁
            return resResult(STATUS.GUILD_TRAIN_BOX_INDEX_IS_GOT);
        }
        trainBox = findWhere(trainInstance.trainBoxs, { roleId })
        if (!!trainBox) {
            res.releaseCallback();//解锁
            return resResult(STATUS.GUILD_TRAIN_BOX_IS_GOT);
        }
        let good = getRandSingleEelm(trainInfo.heroRewards);
        let goods = await addItems(roleId, roleName, sid, [good], ITEM_CHANGE_REASON.TRAIN_BOX_REWARD);

        let resGuildTrain = await GuildTrainModel.receiveBoxByIndex(code, roleId, trainId, hid, index, good);
        let resUserGuild = await UserGuildModel.receiveTrainBoxRewards(roleId, hid, resGuildTrain.guildCode);
        res.releaseCallback();//解锁

        if (!resGuildTrain || !resUserGuild) {
            return resResult(STATUS.GUILD_GET_TRAIN_BOX_FAIL);
        }
        let result: any = getGuildTrainRewards(resGuildTrain);

        result.goods = goods;
        return resResult(STATUS.SUCCESS, result);
    }
    /**
     * 领取练兵试炼等级奖励
     * @param msg 
     * @param session 
     */
    async getTrainLvUpRewards(msg: guildInter & { trainId: number }, session: BackendSession) {
        let { trainId, myUserGuild } = msg;
        const roleId: string = session.get('roleId');
        const roleName: string = session.get('roleName');
        const sid: string = session.get('sid');
        const serverId: number = parseInt(session.get('serverId'));
        let userGuild = await refreshTrain(myUserGuild, roleId, serverId);
        if (!userGuild)
            return resResult(STATUS.WRONG_PARMS);
        const { guildCode: code } = userGuild;
        let guildTrain = await GuildTrainModel.findTrainByTrainIdNotLock(code, trainId);
        if (!guildTrain || !guildTrain.isComplete)
            return resResult(STATUS.GUILD_TRAIN_IS_NOT_COMPLETE);
        let { jinjieReward } = getArmyTrainJuDian(trainId);
        if (userGuild.trainRewards.indexOf(trainId) != -1)
            return resResult(STATUS.GUILD_TRAIN_QUALITY_REWARD_IS_GOT);
        userGuild = await UserGuildModel.receiveTrainRewards(roleId, trainId);
        if (!userGuild) {
            return resResult(STATUS.INTERNAL_ERR);
        }
        let goods = await addItems(roleId, roleName, sid, jinjieReward, ITEM_CHANGE_REASON.TRAIN_LV_REWARD);
        let { trainRewards } = userGuild;
        return resResult(STATUS.SUCCESS, { trainRewards, goods });
    }

    //购买挑战次数
    async purchaseTrainCount(msg: guildInter & { count: number }, session: BackendSession) {
        let { count, myUserGuild } = msg;
        const roleId: string = session.get('roleId');
        const serverId: number = parseInt(session.get('serverId'));
        const sid: string = session.get('sid');
        let userGuild = await refreshTrain(myUserGuild, roleId, serverId);
        if (!userGuild)
            return resResult(STATUS.WRONG_PARMS);
        if (userGuild.buyTrainCount >= ARMY.ARMY_TRAIN_BUYTIMES)
            return resResult(STATUS.GUILD_BUY_TRAIN_COUNT_REACH_MAX);
        let result = await handleCost(roleId, sid, [{ id: CURRENCY_BY_TYPE.get(CURRENCY_TYPE.GOLD), count: ARMY.ARMY_TRAIN_TIMESCOST }], ITEM_CHANGE_REASON.TRAIN_BATTLE_BUY_CNT);
        if (!result)
            return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);
        let { trainCount, buyTrainCount } = await UserGuildModel.addTrainCount(roleId, count);
        return resResult(STATUS.SUCCESS, { trainCount, buyTrainCount });
    }

    // debug接口： 添加挑战次数
    async debugAddTrainCount(msg: { magicWord: string }, session: BackendSession) {
        const { magicWord } = msg;
        if (magicWord !== DEBUG_MAGIC_WORD) {
            return resResult(STATUS.TOKEN_ERR);
        }
        const roleId: string = session.get('roleId');
        await UserGuildModel.addTrainCount(roleId, 1);
        return resResult(STATUS.SUCCESS);
    }
    
    // debug接口： 重置试炼场
    async debugResetTrainInstance(msg: { magicWord: string }, session: BackendSession) {
        const { magicWord } = msg;
        if (magicWord !== DEBUG_MAGIC_WORD) {
            return resResult(STATUS.TOKEN_ERR);
        }

        const guildCode: string = session.get('guildCode');
        const serverId: number = session.get('serverId');
        await GuildModel.updateInfo(guildCode, { resetTrainTimeDaily: new Date(Date.now() - 86400000)})
        await resetTrain(guildCode, serverId);
        return resResult(STATUS.SUCCESS);
    }
}