import { Application, BackendSession, HandlerService, } from 'pinus';
import { BattleRecordModel } from '../../../db/BattleRecord';
import { ExpeditionRecordModel } from '../../../db/ExpeditionRecord';
import { ExpeditionWarRecordModel } from '../../../db/ExpeditionWarRecord';
import { ExpeditionPointModel } from '../../../db/ExpeditionPoint';
import { RoleModel } from '../../../db/Role';
import { genCode, getWarTypeName } from '../../../pubUtils/util';
import { getPointRewardStatus, getResetRemainCnt, findOrCreateEnemies, getExpeditionStatus } from '../../../services/expeditionService';
import { DEBUG_MAGIC_WORD, EXPEDITION_WAR_RECORD_STATUS, ITEM_CHANGE_REASON, KING_EXP_RATIO_TYPE, TASK_TYPE, TA_EVENT } from '../../../consts';
import { WarReward } from '../../../services/warRewardService';
import { addItems } from '../../../services/role/rewardService';
import { getAp, setAp } from '../../../services/actionPointService';
import { STATUS } from '../../../consts/statusCode';
import { resResult } from '../../../pubUtils/util';
import { calculateWarStar, checkBattleHeroes, roleLevelup } from '../../../services/normalBattleService';
import { checkTask, checkTaskInBattleEnd, checkTaskInBattleStart, checkTaskInSkipExpedition } from '../../../services/task/taskService';
import { gameData } from '../../../pubUtils/data';
import * as dicParam from '../../../pubUtils/dicParam';
import { getSeconds, nowSeconds } from '../../../pubUtils/timeUtil';
import { reportTAEvent } from '../../../services/sdkService';
import { getSumCe } from '../../../services/playerCeService';
import { vipCanSkipExpedition } from '../../../services/activity/monthlyTicketService';
import { isHeroHidden } from '../../../services/dataService';

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

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

    /** 
     * 获取初始数据
     * 获取当前远征挑战情况，远征点数，点数宝箱领取情况 */
    async getStatus(msg: {}, session: BackendSession) {

        let roleId = session.get('roleId');
        let roleName = session.get('roleName');

        let res = await getExpeditionStatus(roleId, roleName);
        if (!res) return resResult(STATUS.ROLE_IS_NOT_INIT);

        return resResult(STATUS.SUCCESS, res);
    }

    /** 
     * 重置远征本
     * 每天5点可以重置远征本 */
    async resetStatus(msg: {}, session: BackendSession) {

        let roleId = session.get('roleId');
        let roleName = session.get('roleName');

        let curTime = new Date();
        let { needRefresh, resetCnt } = await getResetRemainCnt(curTime, roleId);
        if (resetCnt <= 0) {
            return resResult(STATUS.EXPEDITION_RESET_NUM_NOT_ENOUGH)
        }

        await ExpeditionRecordModel.hideRecord(roleId); // 刷掉旧关卡

        // 我方战力
        let myCe = await getSumCe(roleId);
        // 每一关的挑战状态
        let { expeditionCode, heroes } = await ExpeditionRecordModel.createRecord({
            roleId, roleName, heroes: [], myCe
        });
        await findOrCreateEnemies(roleId, myCe, expeditionCode, 1, EXPEDITION_WAR_RECORD_STATUS.WAITING);
        await RoleModel.increaseExpeditionResetCnt(roleId, needRefresh, curTime);
        let expeditionWarRecord = await ExpeditionWarRecordModel.getRecordByCode(expeditionCode);
        let curLv = 0;
        if (expeditionWarRecord.length > 0) {
            curLv = expeditionWarRecord[expeditionWarRecord.length - 1].expeditionId;
        }

        return resResult(STATUS.SUCCESS, {
            expeditionCode,
            curLv,
            expeditionWarRecord,
            heroes,
            resetCnt: resetCnt - 1
        });
    }

    /**
     * 获取敌军数据
     * 匹配其他玩家，或机器人数据
     */
    async getEnemies(msg: { expeditionCode: string, expeditionId: number }, session: BackendSession) {

        const roleId = session.get('roleId');
        // const roleName = session.get('roleName');
        const { expeditionCode, expeditionId } = msg;

        let { myCe } = await ExpeditionRecordModel.getExpeditionRecordByCode(expeditionCode);

        let curExpeditionWarRecord = await findOrCreateEnemies(roleId, myCe, expeditionCode, expeditionId, EXPEDITION_WAR_RECORD_STATUS.WAITING);
        if (!curExpeditionWarRecord) {
            return resResult(STATUS.EXPEDITION_MATCH_NO_PLAYER);
        }
        let { battleId, mapseid, enemyFrom, enemies, battleStatus, ce: curCe } = curExpeditionWarRecord;
        let nextCe = 0; // 下一关战力

        if (gameData.expedition.has(expeditionId + 1)) {
            let nextExpeditionWarRecord = await findOrCreateEnemies(roleId, myCe, expeditionCode, expeditionId + 1, EXPEDITION_WAR_RECORD_STATUS.HIDE);
            if (nextExpeditionWarRecord) nextCe = nextExpeditionWarRecord.ce;
        }

        return resResult(STATUS.SUCCESS, {
            expeditionCode, expeditionId, battleId, mapseid,
            battleStatus,
            enemyFrom,
            enemies,
            curCe,
            nextCe
        });
    }

    /**
     * 进入战斗
     * 记录我军数据，生成战斗唯一表示，记录状态
     */
    async checkBattle(msg: { expeditionCode: string, expeditionId: number, battleId: number, heroes: number[] }, session: BackendSession) {
        const { expeditionCode, expeditionId, battleId, heroes: seqIds = [] } = msg;
        let roleId = session.get('roleId');
        let roleName = session.get('roleName');
        let ip = session.get('ip');
        let serverId = session.get('serverId');
        let sid = session.get('sid');
        let warInfo = gameData.war.get(battleId);
        if (!warInfo) {
            return resResult(STATUS.BATTLE_MISS_INFO);
        }

        let role = await RoleModel.findByRoleId(roleId, 'lv warStar');
        let apJson = await getAp(roleId, ip, role.lv);
        let { ap } = apJson;
        if (ap < warInfo.cost) {
            return resResult(STATUS.BATTLE_ACTION_POINT_LACK);
        }

        // 前置关卡是否挑战过
        let previousGk = warInfo.previousGk;
        if (previousGk) {
            let preBattle = role.warStar.findIndex(cur => cur.id == previousGk);
            if (preBattle == -1) return resResult(STATUS.BATTLE_NEED_PREVIOUS_GK);
        }

        let { isOK, hids } = await checkBattleHeroes(roleId, seqIds);
        if (!isOK) return resResult(STATUS.BATTLE_HERO_NOT_FOUND);
        if(isHeroHidden(...hids)) return resResult(STATUS.HERO_IS_HIDDEN);
        
        let expeditionWarRecord = await ExpeditionWarRecordModel.getRecordByCodeAndId(expeditionCode, expeditionId);
        if (!expeditionWarRecord) {
            return resResult(STATUS.EXPEDITION_MISS_WAR_RECORD);
        }
        if (expeditionWarRecord.battleStatus == EXPEDITION_WAR_RECORD_STATUS.SUCCESS) {
            return resResult(STATUS.EXPEDITION_DUPLICATE_CHALLENGE);
        }

        const battleCode = genCode(8);
        await BattleRecordModel.updateBattleRecordByCode(battleCode, {
            $set: {
                roleId, roleName, battleId,
                status: 0,
                warName: warInfo.gk_name,
                warType: warInfo.warType,
                record: { heroes: hids, seqIds }
            }
        }, true);

        let result = await ExpeditionWarRecordModel.updateBattleCode(expeditionCode, expeditionId, EXPEDITION_WAR_RECORD_STATUS.WAITING, battleCode);
        await checkTaskInBattleStart(serverId, roleId, sid, battleId);

        return resResult(STATUS.SUCCESS, {
            expeditionCode,
            expeditionId,
            battleId,
            battleCode,
            battleStatus: result.battleStatus
        });
    }

    /**
     * 跳过
     */
    async skipExpedition(msg: { expeditionCode: string, expeditionId: number, battleId: number }, session: BackendSession) {
        const { expeditionCode, expeditionId, battleId } = msg;
        let roleId = session.get('roleId');
        let roleName = session.get('roleName');
        let serverId = session.get('serverId');
        let sid = session.get('sid');

        let dicExpedition = gameData.expedition.get(expeditionId);
        if(dicExpedition.warId != battleId) return resResult(STATUS.WRONG_PARMS);
        
        let dicWar = gameData.war.get(battleId);
        if(!dicWar) return resResult(STATUS.DIC_DATA_NOT_FOUND);
        // 前置关卡是否挑战过      
        let role = await RoleModel.findByRoleId(roleId, 'warStar topLineupCe vipStartTime')      
        let { warStar, topLineupCe, vipStartTime } = role;
        let previousGk = dicWar.previousGk;
        if (previousGk) {
            let preBattle = warStar.findIndex(cur => cur.id == previousGk);
            if (preBattle == -1) return resResult(STATUS.BATTLE_NEED_PREVIOUS_GK);
        }

        // 检查record
        let expeditionRecord = await ExpeditionRecordModel.getExpeditionRecordByCode(expeditionCode);
        let expeditionWarRecord = await ExpeditionWarRecordModel.getRecordByCodeAndId(expeditionCode, expeditionId);
        if (!expeditionRecord || !expeditionWarRecord) {
            return resResult(STATUS.EXPEDITION_MISS_WAR_RECORD);
        }
        if(expeditionWarRecord.battleStatus == EXPEDITION_WAR_RECORD_STATUS.SUCCESS) {
            return resResult(STATUS.EXPEDITION_DUPLICATE_CHALLENGE);
        }

        // 检查战力是否足够
        if(!vipCanSkipExpedition(expeditionWarRecord.ce, topLineupCe, vipStartTime)) {
            return resResult(STATUS.EXPEDITION_SKIP_POWER_NOT_ENOUGH)
        }

        // 更新敌人剩余状态及战斗状态
        expeditionWarRecord = await ExpeditionWarRecordModel.updateEnemiesStatus(expeditionCode, expeditionId, EXPEDITION_WAR_RECORD_STATUS.SUCCESS, []);
        // 更新点数
        role = await RoleModel.increaseExpeditionPoint(roleId, dicParam.EXPEDITION_CONST.EXPEDITION_CONST_POINTS);

        // 关卡奖励
        let warReward = new WarReward(roleId, roleName, sid, battleId, true);
        let reward = await warReward.saveReward(1);
        
        let curWarStar = warStar.find(cur => cur.id == battleId);
        let { newWarStars, newStar } = calculateWarStar(warStar, battleId, []);
        if (!curWarStar || newStar > curWarStar.star) {
            role = await RoleModel.updateRoleInfo(roleId, { warStar: newWarStars });
        }

        // 更新下一关状态
        if (gameData.expedition.has(expeditionId + 1)) {
            await findOrCreateEnemies(roleId, expeditionRecord.myCe, expeditionCode, expeditionId + 1, EXPEDITION_WAR_RECORD_STATUS.WAITING);
        }
        await checkTaskInSkipExpedition(serverId, roleId, sid, battleId);

        return resResult(STATUS.SUCCESS, {
            expeditionCode,
            expeditionId,
            battleId,
            goods: reward,
            expeditionPoint: role.expeditionPoint
        });
    }

    /**
     * 战斗结算
     * 结算战斗奖励，更新远征状态
     */
    async battleEnd(msg: { expeditionCode: string, expeditionId: number, battleCode: string, battleId: number, isSuccess: boolean, heroes: Array<{ dataId: number, hp: number, ap: number, shield: number }>, enemies: Array<{ dataId: number, hp: number, ap: number }>, star: number, stars: number[] }, session: BackendSession) {

        const { expeditionCode, battleCode, battleId, expeditionId, isSuccess, heroes = [], star, stars = [], enemies } = msg;
        let roleId = session.get('roleId');
        let roleName = session.get('roleName');
        const serverId = session.get('serverId');
        let sid = session.get('sid');
        let ip = session.get('ip');

        let warInfo = gameData.war.get(battleId);
        if (!warInfo) {
            return resResult(STATUS.BATTLE_MISS_INFO);
        }

        const BattleRecord = await BattleRecordModel.getBattleRecordByCode(battleCode);
        if (!BattleRecord || BattleRecord.status != 0) {
            return resResult(STATUS.BATTLE_STATUS_WRONG);
        }

        let flag = 1; // 对比hero信息
        let { record: { heroes: dbHeroes, seqIds: dbSeqIds }, createdAt } = BattleRecord;
        for (let { dataId } of heroes) {
            if (dbSeqIds.indexOf(dataId) == -1) flag = 0;
        }
        if (!flag) {
            return resResult(STATUS.BATTLE_INFO_VALIDATE_ERR);
        }

        let role = await RoleModel.findByRoleId(roleId, 'lv');
        let apJson = await setAp(serverId, roleId, ip, role.lv, isSuccess?-1 * warInfo.cost: 0, sid, ITEM_CHANGE_REASON.EXPEDITION_BATTLE_END); // 扣除体力
        if (!apJson) {
            return resResult(STATUS.BATTLE_ACTION_POINT_LACK);
        }

        // 检查record
        let expeditionRecord = await ExpeditionRecordModel.getExpeditionRecordByCode(expeditionCode);
        let expeditionWarRecord = await ExpeditionWarRecordModel.getRecordByCodeAndId(expeditionCode, expeditionId);
        if (!expeditionRecord || !expeditionWarRecord) {
            return resResult(STATUS.EXPEDITION_MISS_WAR_RECORD);
        }
        // 更新我方剩余血量
        await ExpeditionRecordModel.updateHeroStatus(expeditionCode, expeditionRecord.heroes, heroes);
        // 更新敌人剩余状态及战斗状态
        let battleStatus = isSuccess ? EXPEDITION_WAR_RECORD_STATUS.SUCCESS : EXPEDITION_WAR_RECORD_STATUS.FAIL;
        expeditionWarRecord = await ExpeditionWarRecordModel.updateEnemiesStatus(expeditionCode, expeditionId, battleStatus, enemies);
        // 更新battleRecord状态
        await BattleRecordModel.updateBattleRecordByCode(battleCode, {
            $set: { status: isSuccess ? 1 : 2, star }
        }, true);
        // 更新点数
        role = await RoleModel.increaseExpeditionPoint(roleId, isSuccess ? dicParam.EXPEDITION_CONST.EXPEDITION_CONST_POINTS : 0);
        let { expeditionPoint = 0, warStar = [] } = role;

        // 关卡奖励
        let warReward = new WarReward(roleId, roleName, sid, battleId, isSuccess);
        let reward = await warReward.saveReward(1);

        let actordata = await roleLevelup(KING_EXP_RATIO_TYPE.BATTLE, roleId, isSuccess ? warInfo.kingExp : 0, session);// 主公升级经验
        
        let curWarStar = warStar.find(cur => cur.id == battleId);
        let { newWarStars, newStar } = calculateWarStar(warStar, battleId, stars);

        if (isSuccess) {
            // 更新下一关状态
            await ExpeditionWarRecordModel.updateStatus(expeditionCode, expeditionId + 1, EXPEDITION_WAR_RECORD_STATUS.WAITING);
            if(!curWarStar) { // 首通
                reportTAEvent(roleId, TA_EVENT.GK_FIRST_PASS, { war_id: battleId, war_type: getWarTypeName(warInfo.warType), pass_time: Date.now(), pass_duration: nowSeconds() - getSeconds(createdAt), hero_list: dbHeroes  });
            }
            if (!curWarStar || newStar > curWarStar.star) {
                await RoleModel.updateRoleInfo(roleId, { warStar: newWarStars });
            }
        }
        await checkTaskInBattleEnd(serverId, roleId, sid, battleId, dbHeroes, star, isSuccess);

        return resResult(STATUS.SUCCESS, {
            expeditionCode, expeditionId,
            battleCode, battleId,
            battleStatus: expeditionWarRecord.battleStatus,
            battleGoods: reward,
            expeditionPoint,
            createdAt: getSeconds(createdAt),
            ...actordata
        });
    }

    /**
     * 领取点数宝箱
     * 领取点数宝箱，不扣除点数，那么就需要记录领取状态并且有返回
     */
    async pointReward(msg: { point: number }, session: BackendSession) {

        const { point } = msg;
        let roleId = session.get('roleId');
        let roleName = session.get('roleName');
        let sid = session.get('sid');
        
        const serverId = session.get('serverId');

        let role = await RoleModel.findByRoleId(roleId);
        let { expeditionPoint } = role;
        let dicExpeditionPoint = gameData.expeditionPoint;
        let curDicExpeditionPoint = dicExpeditionPoint.get(point);
        if (!curDicExpeditionPoint) {
            return resResult(STATUS.EXPEDITION_MISS_POINT_INFO);
        }

        if (point > expeditionPoint) {
            return resResult(STATUS.EXPEDITION_POINT_NOT_ENOUGH);
        }
        let pointStatusInDatabase = await ExpeditionPointModel.getExpeditionPoint(roleId);
        if (pointStatusInDatabase) {
            let { rewards } = pointStatusInDatabase;
            let curReward = rewards.find(cur => cur.point == point);
            if (curReward && curReward.received) {
                return resResult(STATUS.EXPEDITION_WRONG_RECEIVE_STATUS);
            }
        }

        // 标记状态
        let { rewards: resultRewards } = await ExpeditionPointModel.updatePointStatus(roleId, point, curDicExpeditionPoint.reward);
        let hasReceivedAll = true, maxPoint = 0;
        for (let [point] of dicExpeditionPoint) {
            let curReward = resultRewards.find(cur => cur.point == point);
            if (!curReward || !curReward.received) {
                hasReceivedAll = false;
            }
            if (point > maxPoint) maxPoint = point;
        }
        if (hasReceivedAll) { // 全部领取了，刷新
            await ExpeditionPointModel.completeStatus(roleId);
            await RoleModel.increaseExpeditionPoint(roleId, maxPoint * -1);
        }
        let pointRewards = await getPointRewardStatus(roleId);

        // 任务
        await checkTask(serverId, roleId, sid, TASK_TYPE.BATTLE_EXPEDITION_BOX,  { point });

        let goods = await addItems(roleId, roleName, sid, curDicExpeditionPoint.reward, ITEM_CHANGE_REASON.EXPEDITION_POINT_REWARD);

        return resResult(STATUS.SUCCESS, {
            costPoint: hasReceivedAll ? maxPoint : 0,
            pointRewards,
            goods
        })
    }

    async debugResetResetNum(msg: { magicWord: string }, session: BackendSession) {
        const { magicWord } = msg;
        if (magicWord !== DEBUG_MAGIC_WORD) {
            return resResult(STATUS.TOKEN_ERR);
        }
        let roleId = session.get('roleId');
        await RoleModel.updateRoleInfo(roleId, { expeditionResetCnt: 0 });
        return resResult(STATUS.SUCCESS);
    }
}
