import { Application, BackendSession, pinus } from 'pinus';
import { uniq, findWhere, findIndex } from 'underscore';
import { gameData, getPvpBoxs, getPLvByScore } from '../../../pubUtils/data';
import { refreshEnemies, getEnemies, getLvByScore, defaultHeroes, comsumeChallengeCnt, refresh, findPvpDefByRoleId, checkRoleIsRobot, getRefOppCnt, findPvpDefAllByRoleId, generPVPOppRecInfo, generMyRecInfo, robotIdComBack } from '../../../services/pvpService';
import { RoleModel, RoleType } from '../../../db/Role';
import { STATUS } from '../../../consts/statusCode';
import { resResult, genCode } from '../../../pubUtils/util';

import { PvpDefenseModel, OppPlayers } from '../../../db/PvpDefense';
import { pvpEndParamInter } from '../../../pubUtils/interface';
import { PlayerDetail, PlayerDetailHero } from '../../../domain/battleField/guild';
import { PVP_HERO_POS, PVP_HERO_ORDER, REDIS_KEY, LINEUP_NUM, TASK_TYPE } from '../../../consts';
import { PVP } from '../../../pubUtils/dicParam';
import { addItems } from '../../../services/rewardService';
import { HeroModel } from '../../../db/Hero';
import { checkBattleHeroesByHid } from '../../../services/normalBattleService';
import { BattleRecordModel } from '../../../db/BattleRecord';
import { PvpRecordModel } from '../../../db/PvpRecord';
import { handleCost } from '../../../services/rewardService';
import { nowSeconds, getSeconds } from '../../../pubUtils/timeUtil';
import { setPvpSeasonResult, resetPvpWarId, resetPvpSeasonTime } from '../../../services/timeTaskService';
import { PvpSeasonResultModel } from '../../../db/PvpSeasonResult';
import { PvpHistoryOppModel, PvpHistoryOppType } from '../../../db/PvpHistoryOpp';
import { SystemConfigModel } from '../../../db/SystemConfig';
import { Rank } from '../../../services/rankService';
import { checkActivityTask, checkTask, checkTaskInPvpEnd } from '../../../services/taskService';

export default function (app: Application) {
    return new PvpHandler(app);
}

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

    async addRoleScore(msg: { heroScores: Array<{ hid: number, score: number }> }, session: BackendSession) {
        let { heroScores: addHeroScores } = msg;
        let roleId = session.get('roleId');
        let sid = session.get('sid');
        const funcs: number[] = session.get('funcs');
        let { heroScores, hisScore } = await PvpDefenseModel.findByRoleId(roleId);
        let score = 0;
        for (let { hid, score: heroScore } of addHeroScores) {
            if (heroScore < 0) continue;
            let index = findIndex(heroScores, { hid });
            if (index != -1) {
                heroScores[index].score = heroScore;
            } else {
                heroScores.push({ hid, score: heroScore });
            }
        }
        for (let heroScore of heroScores) {
            score += heroScore.score;
        }
        if (score < 0) {
            score = 0;
        }
        if (hisScore < score) {
            hisScore = score;
        }
        let pLv = getPLvByScore(score);
        let pvpDefense = await PvpDefenseModel.updateInfoAndInclude(roleId, { score, hisScore, pLv, heroScores });
        let roleName = session.get('roleName');
        const role = await RoleModel.findByRoleId(roleId);

        // 加入排行榜
        let r = new Rank(REDIS_KEY.PVP_RANK, {});
        await r.setRankWithRoleInfo(roleId, pvpDefense.score, pvpDefense.updatedAt.getTime(), role);

        // 任务
        await checkTask(roleId, sid, funcs, TASK_TYPE.PVP_HERO_SCORE, 0, false, { heroScores });

        return resResult(STATUS.SUCCESS, { score, hisScore, heroScores });
    }

    async debugPvpSeasonReset(msg: {}, session: BackendSession) {
        let { seasonNum, seasonEndTime } = await setPvpSeasonResult({ name: 'exce setPvpSeasonResult debug' });
        return resResult(STATUS.SUCCESS, { seasonNum, seasonEndTime })
    }

    async debugPvpWarReset(msg: {}, session: BackendSession) {
        let systemConfig = await resetPvpWarId();
        return resResult(STATUS.SUCCESS, { systemConfig });
    }

    async debugPvpSeasonResetTime(msg: { day: number }, session: BackendSession) {
        let { day: hour } = msg;
        let { seasonNum, seasonEndTime } = await pinus.app.rpc.systimer.systimerRemote.resetPvpSeasonTime.toServer('systimer-server-1', hour);
        return resResult(STATUS.SUCCESS, { seasonNum, seasonEndTime });
    }

    async debugAddChallengeCnt(msg: { challengeCnt: number }, session: BackendSession) {
        let { challengeCnt } = msg;
        let roleId = session.get('roleId');
        if (challengeCnt > PVP.PVP_CHALLENGE_COUNTS) {
            challengeCnt = PVP.PVP_CHALLENGE_COUNTS;
        }
        let { challengeRefTime } = await PvpDefenseModel.updateInfoAndInclude(roleId, { challengeCnt, challengeRefTime: nowSeconds(), refOppCnt: 0 });
        return resResult(STATUS.SUCCESS, { challengeCnt, challengeRefTime });
    }

    async getData(msg: {}, session: BackendSession) {
        let roleId = session.get('roleId');
        let { pvpDefense, warId, seasonEndTime } = await findPvpDefAllByRoleId(roleId);
        let oppPlayers = await getEnemies(pvpDefense.oppPlayers, pvpDefense.winStreakNum);
        let { isDefaultHero, heroes, score, pLv, winStreakNum, refOppCnt, challengeCnt, challengeRefTime, receivedBox, hisScore, heroScores, isFirstEntry, seasonNum } = pvpDefense;
        if (isFirstEntry) {
            await PvpDefenseModel.updateInfo(roleId, { isFirstEntry: false });
        }
        let pvpSeasonResult = await PvpSeasonResultModel.getPvpSeasonResult(roleId);
        let flag = false;
        if (!!pvpSeasonResult) {
            var { oldSeasonData, show, heroGoods, rankGoods } = pvpSeasonResult;
            if (oldSeasonData.seasonEndTime > nowSeconds()) {//结算中锁定玩家信息，结算结束后统一展示修改完成的信息
                seasonEndTime = oldSeasonData.seasonEndTime;
                heroScores = oldSeasonData.heroScores;
                score = oldSeasonData.score;
                refOppCnt = oldSeasonData.refOppCnt;
                challengeCnt = oldSeasonData.challengeCnt;
                challengeRefTime = oldSeasonData.challengeRefTime;
            } else if (show) {//是否需要弹出结算奖励界面
                await PvpSeasonResultModel.updatePvpSeasonResult(roleId, { show: false });
                flag = true;
            }
        }

        let r = new Rank(REDIS_KEY.PVP_RANK, {});
        let myRank = await r.getMyRank({ roleId });//去redis中获取排名

        let data: any = {
            warId, seasonNum, seasonEndTime, myRank, oppPlayers, heroes: heroes.map(cur => {
                let { actorId, dataId, order } = cur;
                return { actorId, dataId, order }
            }), score, pLv, winStreakNum, refOppCnt, challengeCnt, challengeRefTime, receivedBox, hisScore, heroScores, isFirstEntry, isDefaultHero
        }
        if (flag) {//弹出结赛季结算奖励
            data.resultRecord = { oldSeasonData, heroGoods, rankGoods }
        }
        return resResult(STATUS.SUCCESS, data);
    }

    // 刷新对手
    async refreshOppPlayer(msg: {}, session: BackendSession) {
        let roleId = session.get('roleId');
        let sid = session.get('sid');

        let pvpDefense = await PvpDefenseModel.findByRoleIdIncludeAll(roleId);

        // 刷新次数及消耗
        let { refOppCnt, refOppTime, consume } = getRefOppCnt(pvpDefense.refOppCnt, pvpDefense.refOppTime);
        if (refOppCnt >= gameData.maxPvpRefreshCnt.max) {
            return resResult(STATUS.PVP_REFRESH_CNT_REACH_MAX);
        }

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

        // 刷新对手
        let role = <RoleType>pvpDefense.role;
        let oppPlayers = await refreshEnemies(role, pvpDefense.score, pvpDefense.pLv);

        pvpDefense = await PvpDefenseModel.updateInfoAndInclude(roleId, { oppPlayers, refOppCnt: refOppCnt + 1, refOppTime });
        let result = await getEnemies(pvpDefense.oppPlayers, pvpDefense.winStreakNum);


        return resResult(STATUS.SUCCESS, { oppPlayers: result, refOppCnt: pvpDefense.refOppCnt });
    }

    // 获取对手阵容具体信息
    async getOppPlayer(msg: { roleId: string }, session: BackendSession) {
        let roleId = session.get('roleId');

        let oppoRoleId = msg.roleId;
        let pvpDefense = await PvpDefenseModel.findByRoleIdIncludeAll(roleId);
        let { oppPlayers } = pvpDefense;

        let curOpp = oppPlayers.find(cur => cur.roleId == oppoRoleId);
        if (!curOpp) return resResult(STATUS.PVP_ROLE_NOT_FOUND);

        let { oppRoleId, heroes } = <PvpHistoryOppType>curOpp.oppDef;

        return resResult(STATUS.SUCCESS, { roleId: oppRoleId, pos: curOpp.pos, heroes });
    }

    // 开战
    async pvpStart(msg: { warId: number, heroes: number[], roleId: string }, session: BackendSession) {
        const { warId, heroes, roleId: oppRoleId } = msg;
        let roleId = session.get('roleId');
        let roleName = session.get('roleName');

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

        let { isOK } = await checkBattleHeroesByHid(roleId, heroes);
        if (!isOK) return resResult(STATUS.BATTLE_HERO_NOT_FOUND);

        const pvpDefense = await PvpDefenseModel.findByRoleId(roleId);
        if (!pvpDefense) return resResult(STATUS.PVP_NOT_OPEN);
        // 检查挑战次数
        let { seasonEndTime } = await SystemConfigModel.findSystemConfig();
        let { challengeCnt } = refresh(pvpDefense.challengeCnt, pvpDefense.challengeRefTime, seasonEndTime);
        if (challengeCnt == 0) {
            return resResult(STATUS.PVP_CHALLENGE_TIMES_NOT_ENOUGH);
        }
        let { oppPlayers } = pvpDefense;
        let curOpp = oppPlayers.find(cur => cur.roleId == oppRoleId);
        if (!curOpp) return resResult(STATUS.PVP_ROLE_NOT_FOUND)

        const battleCode = genCode(8); // 关卡唯一值

        await BattleRecordModel.updateBattleRecordByCode(battleCode, {
            $set: {
                roleId, roleName, battleId: warId,
                status: 0,
                warName: warInfo.gk_name,
                warType: warInfo.warType,
                record: { heroes, pos: curOpp.pos, oppRoleId }
            }
        }, true);

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

    // 结算
    async pvpEnd(msg: { battleCode: string, isSuccess: boolean, myHeroes: pvpEndParamInter[], oppHeroes: pvpEndParamInter[] }, session: BackendSession) {

        let roleId = session.get('roleId');
        let roleName = session.get('roleName');
        let sid = session.get('sid');
        const funcs: number[] = session.get('funcs');
        const serverId = session.get('serverId');

        const { battleCode, isSuccess, myHeroes, oppHeroes } = msg;

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

        let { record: { heroes: dbHeroes, oppRoleId, pos } } = BattleRecord;

        let flag = 1; // 对比hero信息
        for (let { hid } of myHeroes) {
            if (dbHeroes.indexOf(hid) == -1) flag = 0;
        }
        if (!flag) return resResult(STATUS.BATTLE_INFO_VALIDATE_ERR);

        // 更新军功
        let { pvpDefense, warId } = await findPvpDefByRoleId(roleId);
        if (!pvpDefense) return resResult(STATUS.PVP_NOT_OPEN);
        let { oppPlayers, winStreakNum, heroScores, score, hisScore } = pvpDefense;
        let curOpp = oppPlayers.find(cur => cur.roleId == oppRoleId && cur.pos == pos);
        if (!curOpp) return resResult(STATUS.PVP_ROLE_NOT_FOUND);

        const role = await RoleModel.findByRoleId(roleId, null, true);
        if (isSuccess) {
            winStreakNum++;
        } else {
            winStreakNum = 0;
        }

        const myRecInfo = await generMyRecInfo(heroScores, winStreakNum, role, isSuccess, pos, myHeroes);

        let { attackInfo, showHeroScores, addSumScore } = myRecInfo

        score += addSumScore;
        let pLv = getLvByScore(heroScores);

        const defenseInfo = await generPVPOppRecInfo(isSuccess, curOpp, oppHeroes, role.lv);

        // 战报记录
        await PvpRecordModel.createRec({ roleId1: roleId, roleId2: robotIdComBack(oppRoleId), warId: BattleRecord.battleId, attackInfo, defenseInfo, createTime: nowSeconds() });

        // 对手记录更新
        await PvpHistoryOppModel.setStatus(roleId, oppRoleId, 1);

        // 刷新对手
        let newOppPlayers: Array<OppPlayers> = await refreshEnemies(role, score, pLv);

        // 更新battleRecord
        await BattleRecordModel.updateBattleRecordByCode(battleCode, {
            $set: { status: isSuccess ? 1 : 2 }
        }, true);
        let { seasonEndTime } = await SystemConfigModel.findSystemConfig();

        // 减少挑战次数
        let { challengeCnt, challengeRefTime } = comsumeChallengeCnt(pvpDefense.challengeCnt, pvpDefense.challengeRefTime, seasonEndTime);
        if (hisScore < score) {
            hisScore = score;
        }
        pvpDefense = await PvpDefenseModel.updateInfoAndInclude(roleId, { oppPlayers: newOppPlayers, heroScores, score, pLv, winStreakNum, challengeCnt, challengeRefTime, hisScore });
        let result = await getEnemies(pvpDefense.oppPlayers, pvpDefense.winStreakNum);

        // 加入排行榜
        let r = new Rank(REDIS_KEY.PVP_RANK, {});
        await r.setRankWithRoleInfo(roleId, pvpDefense.score, pvpDefense.updatedAt.getTime(), role);
        let myRank = await r.getMyRank({ roleId });

        await checkTaskInPvpEnd(roleId, sid, funcs, isSuccess, heroScores);
        await checkActivityTask(serverId, sid, funcs, roleId, TASK_TYPE.PVP, 1)
        if (isSuccess) {
            await checkActivityTask(serverId, sid, funcs, roleId, TASK_TYPE.PVP_WIN, 1)
            await checkActivityTask(serverId, sid, funcs, roleId, TASK_TYPE.PVP_WIN_SERIES, 1)
        }

        return resResult(STATUS.SUCCESS, {
            battleCode, isSuccess,
            score, pLv, myRank, hisScore,
            heroScores: showHeroScores,
            oppPlayers: result,
            refOppCnt: pvpDefense.refOppCnt,
            challengeCnt: pvpDefense.challengeCnt,
            challengeRefTime: pvpDefense.challengeRefTime,
            winStreakNum: pvpDefense.winStreakNum,
            warId
        });
    }

    async saveDefense(msg: { heroes: Array<{ actorId: number, dataId: number, order: number }>, isDefaultHero: boolean }, session: BackendSession) {
        let { heroes, isDefaultHero } = msg;
        let roleId = session.get('roleId');
        if (heroes.length > LINEUP_NUM || heroes.length <= 0) {
            return resResult(STATUS.WRONG_PARMS);
        }
        heroes = uniq(heroes, function (item) {
            return item.order;
        });
        let { seasonEndTime } = await SystemConfigModel.findSystemConfig();
        let { heroes: defHeros, challengeCnt: lastChallengeCnt, challengeRefTime: lastChallengeRefTime } = await PvpDefenseModel.findByRoleId(roleId);
        let defCe = 0;
        let { challengeCnt, challengeRefTime } = refresh(lastChallengeCnt, lastChallengeRefTime, seasonEndTime);

        if (!!isDefaultHero) {
            let { topLineup } = await RoleModel.findByRoleId(roleId);
            let hids = topLineup.filter(cur => {
                return !heroes.find(ccur => ccur.actorId == cur.hid);
            }).map(cur => cur.hid);
            let dataIds = new Array<number>(), orders = new Array<number>();

            for (let hero of heroes) {
                if (hero.actorId) {
                    let isInTop = topLineup.find(cur => cur.hid == hero.actorId);
                    if (!isInTop) {
                        hero.actorId = hids.shift();
                    }
                    dataIds.push(hero.dataId);
                    orders.push(hero.order);
                }
            }
            for (let hid of hids) {
                let dataId = 0;
                for (let i = PVP_HERO_POS.START; i <= PVP_HERO_POS.END; i++) {
                    if (dataIds.includes(i)) continue;
                    dataIds.push(i);
                    dataId = i; break;
                }
                let order = 0;
                for (let i = PVP_HERO_ORDER.START; i <= PVP_HERO_ORDER.END; i++) {
                    if (orders.includes(i)) continue;
                    orders.push(i);
                    order = i; break;
                }
                heroes.push({
                    actorId: hid, dataId, order
                })
            }
        }

        for (let dataId = PVP_HERO_POS.START; dataId <= PVP_HERO_POS.END; dataId++) {
            let index = findIndex(heroes, { dataId });
            let defIndex = findIndex(defHeros, { dataId });
            if (defIndex == -1) {
                defIndex = defHeros.length;
                defHeros.push({ actorId: 0, order: 0, ce: 0, hero: null, dataId });
            }
            if (index == -1) {
                defHeros[defIndex].actorId = 0;
                defHeros[defIndex].order = 0;
                defHeros[defIndex].ce = 0;
                defHeros[defIndex].hero = null;
                defHeros[defIndex].dataId = dataId;
                continue;
            }
            if (heroes[index].order < 1 || heroes[index].order > LINEUP_NUM) {
                return resResult(STATUS.WRONG_PARMS);
            }
            let hero = await HeroModel.findByHidAndRole(heroes[index].actorId, roleId);
            if (!hero) {
                continue;
            }
            defHeros[defIndex].actorId = heroes[index].actorId;
            defHeros[defIndex].order = heroes[index].order;
            defHeros[defIndex].ce = hero.ce;
            defHeros[defIndex].hero = hero._id;
            defCe += hero.ce;
        }
        let { heroes: resHeroes } = await PvpDefenseModel.updateInfo(roleId, { defCe, heroes: defHeros, challengeCnt, challengeRefTime, isDefaultHero });
        return resResult(STATUS.SUCCESS, { heroes: resHeroes, challengeCnt, challengeRefTime, isDefaultHero });

    }

    async getRank(msg: {}, session: BackendSession) {

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

        let r = new Rank(REDIS_KEY.PVP_RANK, {});
        let { ranks, myRank } = await r.getRankListWithMyRank({ roleId });

        if (!myRank) {
            let pvpDefense = await PvpDefenseModel.findByRoleId(roleId);

            myRank = await r.generMyRankWithRole(roleId, pvpDefense.score, getSeconds(pvpDefense.refOppTime));
        }

        return resResult(STATUS.SUCCESS, { ranks, myRank });
    }

    async getPlayerDetail(msg: { roleId: string }, session: BackendSession) {

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

        let { roleId: oppoRoleId } = msg;
        let isRobot = checkRoleIsRobot(oppoRoleId);

        let result: PlayerDetail;

        if (isRobot) { // 如果是机器人，从自己的pvpDefense中寻找
            let pvpHistoryOpp = await PvpHistoryOppModel.findByRoleIdAndOppId(roleId, oppoRoleId);
            if (!pvpHistoryOpp) {
                return resResult(STATUS.PVP_ROLE_NOT_FOUND);
            }

            let heroes = new Array<PlayerDetailHero>();
            for (let hero of pvpHistoryOpp.heroes) {
                let newHero = new PlayerDetailHero();
                newHero.setPvpHeroInfo(hero);
                heroes.push(newHero);
            }
            for (let hero of pvpHistoryOpp.otherHeroes) {
                let newHero = new PlayerDetailHero();
                newHero.setPvpHeroInfo(hero);
                heroes.push(newHero);
            }
            result = new PlayerDetail({ ...pvpHistoryOpp, roleId: pvpHistoryOpp.oppRoleId, heroes, rank: pvpHistoryOpp.rankLv });
        } else { // 查询对方pvpDefense
            let pvpDefense = await PvpDefenseModel.findByRoleId(oppoRoleId);
            let dbHeroes = await HeroModel.findByRole(oppoRoleId);
            let role = await RoleModel.findByRoleId(oppoRoleId);

            let heroes = new Array<PlayerDetailHero>();
            for (let { hid, lv, star, colorStar, quality } of dbHeroes) {
                let heroScore = pvpDefense.heroScores.find(cur => cur.hid == hid);
                heroes.push({
                    actorId: hid, lv, star, colorStar, quality,
                    score: heroScore ? heroScore.score : 0
                });
            }
            heroes.sort((a, b) => b.score - a.score);
            let r = new Rank(REDIS_KEY.PVP_RANK, {});
            let rank = await r.getMyRank({ roleId: oppoRoleId });//去redis中获取排名

            result = new PlayerDetail({ ...role, ...pvpDefense, heroes, rank });
        }

        return resResult(STATUS.SUCCESS, result);
    }

    async receiveBox(msg: { id: number }, session: BackendSession) {
        let { id } = msg;
        let roleId = session.get('roleId');
        let sid: string = session.get('sid');
        let roleName = session.get('roleName');
        const funcs: number[] = session.get('funcs');
        let { seasonEndTime } = await SystemConfigModel.findSystemConfig();
        let { hisScore, receivedBox, challengeCnt: lastChallengeCnt, challengeRefTime: lastChallengeRefTime } = await PvpDefenseModel.findByRoleIdIncludeAll(roleId);
        let pvpBoxs = getPvpBoxs();
        let pvpBox = findWhere(pvpBoxs, { id });
        if (hisScore < pvpBox.score) {
            return resResult(STATUS.PVP_NOT_REACH_BOX_SCORE);
        }
        if (receivedBox.indexOf(id) != -1) {
            return resResult(STATUS.PVP_BOX_IS_GOT);
        }
        receivedBox.push(id);
        let { challengeCnt, challengeRefTime } = refresh(lastChallengeCnt, lastChallengeRefTime, seasonEndTime);
        await PvpDefenseModel.updateInfo(roleId, { receivedBox, challengeCnt, challengeRefTime });
        let result = await addItems(roleId, roleName, sid, pvpBox.reward);
        // 任务
        await checkTask(roleId, sid, funcs, TASK_TYPE.PVP_RECEIVE_BOX, 1, true, {});
        return resResult(STATUS.SUCCESS, { goods: result, receivedBox, challengeCnt, challengeRefTime });
    }

    async getRec(msg: {}, session: BackendSession) {
        let roleId = session.get('roleId');
        let pvpRecords = await PvpRecordModel.getRecByRoleId(roleId);
        return resResult(STATUS.SUCCESS, { list: pvpRecords });
    }
}
