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 { calculateSumCE, genCode } from '../../../pubUtils/util';
import { getPointRewardStatus, getResetRemainCnt, findOrCreateEnemies, getExpeditionStatus } from '../../../services/expeditionService';
import { EXPEDITION_WAR_RECORD_STATUS, KING_EXP_RATIO_TYPE, LINEUP_NUM, TASK_TYPE } from '../../../consts';
import { WarReward } from '../../../services/warRewardService';
import { addItems } from '../../../services/rewardService';
import { getAp, setAp } from '../../../services/actionPointService';
import { STATUS } from '../../../consts/statusCode';
import { resResult } from '../../../pubUtils/util';
import { checkBattleHeroes, roleLevelup } from '../../../services/normalBattleService';
import { checkActivityTask, checkTask, checkTaskInBattleEnd } from '../../../services/taskService';
import { gameData } from '../../../pubUtils/data';
import * as dicParam from '../../../pubUtils/dicParam';

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 calculateSumCE(roleId, 1, { num: LINEUP_NUM });
        // 每一关的挑战状态
        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: Array<number> }, session: BackendSession) {
        const { expeditionCode, expeditionId, battleId, heroes: seqIds = [] } = msg;
        let roleId = session.get('roleId');
        let roleName = session.get('roleName');
        let warInfo = gameData.war.get(battleId);
        if (!warInfo) {
            return resResult(STATUS.BATTLE_MISS_INFO);
        }

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

        // 前置关卡是否挑战过
        let previousGk = warInfo.previousGk;
        if (previousGk) {
            let preBattle = await BattleRecordModel.getBattleRecordByIdAndStatus(roleId, previousGk, 1);
            if (!preBattle) return resResult(STATUS.BATTLE_NEED_PREVIOUS_GK);
        }

        let { isOK, hids } = await checkBattleHeroes(roleId, seqIds);
        if (!isOK) return resResult(STATUS.BATTLE_HERO_NOT_FOUND);

        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);

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

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

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

        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 } } = 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(roleId, role.lv, isSuccess?-1 * warInfo.cost: 0, sid); // 扣除体力
        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 } = 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);// 主公升级经验
        if (isSuccess) {
            // 更新下一关状态
            await ExpeditionWarRecordModel.updateStatus(expeditionCode, expeditionId + 1, EXPEDITION_WAR_RECORD_STATUS.WAITING);
        }
        await checkTaskInBattleEnd(serverId, roleId, sid, battleId, dbHeroes, star);

        return resResult(STATUS.SUCCESS, {
            expeditionCode, expeditionId,
            battleCode, battleId,
            battleStatus: expeditionWarRecord.battleStatus,
            battleGoods: reward,
            expeditionPoint,
            ...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(roleId, sid, TASK_TYPE.BATTLE_EXPEDITION_BOX, 1, true, { point });
        await checkActivityTask(serverId, sid, roleId, TASK_TYPE.BATTLE_EXPEDITION_BOX, 1)

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

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

}
