import { Application, BackendSession, HandlerService, } from 'pinus';
import { BattleRecordModel } from '../../../db/BattleRecord';
import { BattleSweepRecordModel } from '../../../db/BattleSweepRecord';
import { genCode, getReasonByWarType, getWarTypeName } from '../../../pubUtils/util';
import { WAR_TYPE, EVENT_STATUS, REDIS_KEY, KING_EXP_RATIO_TYPE, TA_EVENT, ITEM_CHANGE_REASON } from '../../../consts';
import { checkDaily, checkDailyAndIncrease } from '../../../services/dailyBattleService';
import { checkTowerWar, towerBattleEnd } from '../../../services/battleService';
import { WarReward } from '../../../services/warRewardService';
import { getAp, setAp } from '../../../services/actionPointService';
import { setBattleStatus, checkEventBattle } from '../../../services/eventSercive';
import { STATUS } from '../../../consts/statusCode';
import { resResult } from '../../../pubUtils/util';
import { RoleModel } from '../../../db/Role';
import { RScriptRecordModel } from '../../../db/RScriptRecord';
import { checkBattleHeroes, roleLevelup, calculateWarStar, getBattleListOfTypes, getMainChapter, getStarOfChapter, checkWarCountAndInc } from '../../../services/normalBattleService';
import { checkDungeonNum, checkDungeonAndIncrease, saveDungeonFirst } from '../../../services/dungeonService';
// import { switchOnFunc } from '../../../services/funcSwitchService';
import { gameData } from '../../../pubUtils/data';
import { pushMysteryFirstMsg, pushVestigeFirstMsg } from '../../../services/chatService';
import { getSeconds, nowSeconds } from '../../../pubUtils/timeUtil';
import { Rank } from '../../../services/rankService';
import { checkTaskInBattleEnd, checkTaskInBattleStart, checkTaskInBattleSweep } from '../../../services/task/taskService';
import { ActivitySelfServiceModel } from '../../../db/ActivitySelfService';
import { getSelfServiceShopActivityData } from '../../../services/activity/selfServiceShopActivityService';
import { challengeDailyGK } from '../../../services/activity/dailyGKService';
import { challengeNewHeroGK } from '../../../services/activity/newHeroService';
import { reportTAEvent } from '../../../services/sdkService';
import { getVipRegretCnt } from '../../../services/activity/monthlyTicketService';
import { isArray, isNumber } from 'underscore';
import { RewardInter } from '../../../pubUtils/interface';
import { addItems } from '../../../services/role/rewardService';
import { treasureHuntChallengeConsume } from '../../../services/activity/treasureHuntService';
import { combineItems } from '../../../services/role/util';
import { isHeroHidden } from '../../../services/dataService';

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

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

    // 获取关卡列表
    async checkBattle(msg: { battleId: number, heroes: Array<number> }, session: BackendSession) {
        const { battleId, heroes: seqIds = [] } = msg;
        let roleId = session.get('roleId');
        let roleName = session.get('roleName');
        let 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);
        }
        let role = await RoleModel.findByRoleId(roleId, 'lv warStar warCount roleId refWarCount');
        let apJson = await getAp(roleId, ip, role.lv);
        let { ap } = apJson;
        if (ap < warInfo.cost) {
            return resResult(STATUS.BATTLE_ACTION_POINT_LACK);
        }
        if(role.lv < warInfo.lvLimited) {
            return resResult(STATUS.COM_BATTLE_LV_NOT_ENOUGH);
        }
        // 前置关卡是否挑战过            
        let { warStar } = role;

        let previousGk = warInfo.previousGk;
        if (previousGk) {
            let preBattle = warStar.findIndex(cur => cur.id == previousGk);
            if (preBattle == -1) return resResult(STATUS.BATTLE_NEED_PREVIOUS_GK);
        }

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

        const battleCode = genCode(8); // 关卡唯一值
        let dailyNum = {};
        let towerData = {};
        let dungeonNum = {};
        if (warInfo.warType == WAR_TYPE.NORMAL || warInfo.warType == WAR_TYPE.MAIN_ELITE) {
            let checkResult = await checkWarCountAndInc(battleId, 0, role, true);
            if (checkResult.code != 0) {
                return checkResult
            }
        } else if (warInfo.warType == WAR_TYPE.DAILY) {
            let checkResult = await checkDaily(roleId, battleId, 1);
            if (checkResult.status == -1) {
                return checkResult.resResult
            }
            dailyNum = Object.assign(dailyNum, checkResult.data);
        } else if (warInfo.warType == WAR_TYPE.TOWER) {
            let checkResult = await checkTowerWar(roleId, battleId, seqIds, heroes);
            if (checkResult.status == -1) {
                return checkResult.resResult
            }
            towerData = Object.assign(towerData, checkResult.data);
        } else if (warInfo.warType == WAR_TYPE.EVENT) {
            // 记录事件状态
            let eventStatus = session.get('eventStatus') || EVENT_STATUS.WAITING;
            let checkResult = await checkEventBattle(roleId, eventStatus, battleId, battleCode);
            if (checkResult.status == -1) {
                return checkResult.resResult
            }
        } else if (warInfo.warType == WAR_TYPE.MYSTERY) {
            let checkResult = await checkDungeonNum(roleId, 1, battleId);
            if (checkResult.status == -1) {
                return checkResult.resResult
            }
            dungeonNum = Object.assign(dungeonNum, checkResult.data);
        }

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

        let { status } = BattleRecord;

        await checkTaskInBattleStart(serverId, roleId, sid, battleId);
        return resResult(STATUS.SUCCESS, {
            battleId, battleCode, status, dailyNum, towerData, dungeonNum
        });
    }

    // 关卡列表
    async getBattleList(msg: { types: number[] }, session: BackendSession) {
        const { types } = msg;
        let roleId = session.get('roleId');

        let role = await RoleModel.findByRoleId(roleId);
        let result = await getBattleListOfTypes(role, types);
        return resResult(STATUS.SUCCESS, {
            result
        });
    }

    // 关卡结算，记录使用的武将，获得奖励
    async battleEnd(msg: { battleCode: string, battleId: number, isSuccess: boolean, stars: number[], heroes: number[], activityId: number }, session: BackendSession) {
        const { battleCode, battleId, isSuccess, heroes = [], stars = [], activityId } = msg;
        let roleId = session.get('roleId');
        let roleName = session.get('roleName');
        let sid = session.get('sid');
        let ip = session.get('ip');
        let serverId = session.get('serverId');

        let warInfo = gameData.war.get(battleId);
        if (!warInfo) {
            return resResult(STATUS.BATTLE_MISS_INFO);
        }
        // 寻宝不使用通用战斗模块结算
        if (warInfo.warType == WAR_TYPE.COM_BATTLE) {
            return resResult(STATUS.BATTLE_END_WRONG_TYPE);
        }
        // console.log('####### warInfo.warType', warInfo.warType)

        const BattleRecord = await BattleRecordModel.getBattleRecordByCode(battleCode, true);
        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 seqId of dbSeqIds) {
            if (dbSeqIds.indexOf(seqId) == -1) flag = 0;
        }
        if (!flag) {
            return resResult(STATUS.BATTLE_INFO_VALIDATE_ERR);
        }

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

        let { warStar } = role;
        let isFirst = warStar.findIndex(cur => cur.id == battleId) == -1;
        let curWarStar = warStar.find(cur => cur.id == battleId);
        let { newWarStars, newStar } = calculateWarStar(warStar, battleId, stars);

        let warReward = new WarReward(roleId, roleName, sid, battleId, isSuccess);

        let dailyNum = {};
        let towerStatus = null;
        let dungeonNum = {};
        if(warInfo.warType == WAR_TYPE.NORMAL || warInfo.warType == WAR_TYPE.MAIN_ELITE) {
            if(!isFirst) {
                let checkResult = await checkWarCountAndInc(battleId, isSuccess?1:0, role, false);
                if (checkResult.code != 0) {
                    return checkResult
                }
            }
        } 
        
        if (warInfo.warType == WAR_TYPE.DAILY) {
            let checkResult = await checkDailyAndIncrease(roleId, warStar, battleId, isSuccess?1:0, false, stars, role);
            if (checkResult.status == -1) {
                return checkResult.resResult;
            }
            dailyNum = Object.assign(dailyNum, checkResult.data)
        } else if (warInfo.warType == WAR_TYPE.EVENT) {
            // 记录事件状态
            await setBattleStatus(session, roleId, battleId, isSuccess, battleCode);
        } else if (warInfo.warType == WAR_TYPE.TOWER) {
            let towerEndResult = await towerBattleEnd(sid, roleId, serverId, battleCode, battleId, isSuccess, heroes);
            if (towerEndResult) {
                if (towerEndResult.status == -1) {
                    return towerEndResult.resResult;
                }
                towerStatus = towerEndResult.data.towerStatus;

                console.log('##### towerReward', JSON.stringify(towerEndResult.data.towerReward));

                if (towerEndResult.data.towerReward)
                    warReward.setFixReward(towerEndResult.data.towerReward)
            }
        } else if (warInfo.warType == WAR_TYPE.MYSTERY) {
            let checkResult = await checkDungeonAndIncrease(roleId, isSuccess?1:0, false, battleId);
            let role = await RoleModel.saveDungeonHero(roleId, battleId, heroes, isSuccess);
            if (role) {
                await saveDungeonFirst(role, battleId, BattleRecord);
            }

            if (checkResult.status == -1) {
                return checkResult.resResult;
            }
            dungeonNum = Object.assign(dungeonNum, checkResult.data)
        } else if (warInfo.warType == WAR_TYPE.NORMAL) {
            if(isSuccess) {
                let role = await RoleModel.updateMainWarId(roleId, battleId, nowSeconds());
                if (role) {
                    let r = new Rank(REDIS_KEY.MAIN_RANK, { serverId });
                    await r.setRankWithRoleInfo(roleId, role.mainWarId, role.mainUpdatedAt, role);
                }
            }
        } else if (warInfo.warType == WAR_TYPE.MAIN_ELITE) {
            if(isSuccess) {
                let role = await RoleModel.updateMainEliteWarId(roleId, battleId, nowSeconds());
                if (role) {
                    let r = new Rank(REDIS_KEY.MAIN_ELITE_RANK, { serverId });
                    await r.setRankWithRoleInfo(roleId, role.mainEliteWarId, role.mainEliteUpdatedAt, role);
                }
            }
        } else if (warInfo.warType == WAR_TYPE.ACT_SELF_SHOP) {
            //糜家商队挑战成功后记录挑战次数
            if (isSuccess) {
                let playerData = await getSelfServiceShopActivityData(serverId, roleId)
                await ActivitySelfServiceModel.addChallengeRecord(serverId, playerData.activityId, roleId, playerData.roundIndex, 1, new Date())
            }
        } else if (warInfo.warType == WAR_TYPE.ACT_DAILY_GK) {
            if (isSuccess) {
                await challengeDailyGK(serverId, roleId, activityId, battleId)
            }
        } else if (warInfo.warType == WAR_TYPE.ACT_NEW_HERO_GK) {
            // console.log('##### warInfo.warType', warInfo.warType, isSuccess)
            if (isSuccess) {
                isFirst = await challengeNewHeroGK(serverId, roleId, activityId, battleId)
            }
        } else if (warInfo.warType == WAR_TYPE.ACT_TREASURE_HUNT) {
            if(isSuccess) {
                await treasureHuntChallengeConsume(serverId, roleId, sid);
            }
        }

        if (isSuccess) { // 挑战胜利
            // 是否首通
            if (isFirst) {
                // await switchOnFunc(roleId, FUNC_OPT_TYPE.BATTLE_END, battleId, session);
                warReward.setCondition(0, true);
                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  });
                pushMysteryFirstMsg(roleId, roleName, serverId, warInfo.warType, warInfo.war_id);
                pushVestigeFirstMsg(roleId, roleName, serverId, warInfo.warType, warInfo.war_id);
            }
            // 是否首次3星
            if (newStar == 3 && (isFirst || curWarStar.star != 3)) {
                warReward.setCondition(1, true);
            }

            if (!curWarStar || newStar > curWarStar.star) {
                await RoleModel.updateRoleInfo(roleId, { warStar: newWarStars });
            }
        }

        let reward = await warReward.saveReward(1);

        const updateResult = await BattleRecordModel.updateBattleRecordByCode(battleCode, {
            $set: { status: isSuccess ? 1 : 2, star: newStar, stars, record: { heroes } }
        }, true);
        let { status } = updateResult;

        let actordata = await roleLevelup(KING_EXP_RATIO_TYPE.BATTLE, roleId, isSuccess ? warInfo.kingExp : 0, session);// 主公升级经验

        await checkTaskInBattleEnd(serverId, roleId, sid, battleId, dbHeroes, newStar, isSuccess);
        // 返回值：
        // towerStatus: false-本层未通过, true-本层已通过
        return resResult(STATUS.SUCCESS, {
            battleCode, battleId, status,
            battleGoods: reward,
            towerStatus, dailyNum, dungeonNum,
            createdAt: getSeconds(createdAt),
            ...actordata
        });
    }

    async battleSweep(msg: { battleId: number, count: number }, session: BackendSession) {

        const { battleId, count } = msg;
        let roleId = session.get('roleId');
        let roleName = session.get('roleName');
        let sid = session.get('sid');
        let serverId = session.get('serverId');
        let ip = session.get('ip');
                
        let warInfo = gameData.war.get(battleId);
        if (!warInfo) {
            return resResult(STATUS.BATTLE_MISS_INFO);
        }
        // 校验是否三星通关过
        let { warStar } = await RoleModel.findByRoleId(roleId);
        let curWar = warStar.find(cur => cur.id == battleId);
        if (!curWar || curWar.star < 3) {
            return resResult(STATUS.BATTLE_SWEEP_CONDITION_STAR);
        }
        let role = await RoleModel.findByRoleId(roleId, 'lv warCount refWarCount roleId');
        // 扫荡次数
        let dailyNum = {};
        let dungeonNum = {};
        if(warInfo.warType == WAR_TYPE.NORMAL || warInfo.warType == WAR_TYPE.MAIN_ELITE) {
            let checkResult = await checkWarCountAndInc(battleId, count, role, true);
            if (checkResult.code != 0) {
                return checkResult
            }
        } else if (warInfo.warType == WAR_TYPE.DAILY) {
            let checkResult = await checkDailyAndIncrease(roleId, warStar, battleId, count, true);
            if (checkResult.status == -1) {
                return checkResult.resResult
            }
            dailyNum = Object.assign(dailyNum, checkResult.data)
        } else if (warInfo.warType == WAR_TYPE.MYSTERY) {
            let checkResult = await checkDungeonAndIncrease(roleId, count, true, battleId);
            if (checkResult.status == -1) {
                return checkResult.resResult
            }
            dungeonNum = Object.assign(dungeonNum, checkResult.data)
        }


        // 扣体力
        let apJson = await setAp(serverId, roleId, ip, role.lv, -1 * warInfo.cost * count, sid, getReasonByWarType(warInfo.warType)); // 扣除体力
        if (!apJson) {
            return resResult(STATUS.BATTLE_ACTION_POINT_LACK);
        }

        // 发奖励
        let warReward = new WarReward(roleId, roleName, sid, battleId, true);
        let result = await warReward.saveReward(count, true);

        let actordata = await roleLevelup(KING_EXP_RATIO_TYPE.BATTLE, roleId, warInfo.kingExp * count, session)// 主公升级经验

        // 扫荡记录
        await BattleSweepRecordModel.saveBattleSweepRecordById(roleId, battleId, {
            $set: {
                roleName,
                warName: warInfo.gk_name,
                warType: warInfo.warType
            },
            $inc: { count }
        });

        await checkTaskInBattleSweep(serverId, roleId, sid, battleId, count);

        return resResult(STATUS.SUCCESS, {
            battleId, count,
            battleGoods:  result,
            dailyNum, dungeonNum,
            ...actordata
        });
    }

    async saveScript(msg: { battleId: number, type: number, script: string }, session: BackendSession) {

        const { battleId, type, script } = msg;
        let roleId = session.get('roleId');
        let warInfo = gameData.war.get(battleId);
        let result = await RScriptRecordModel.setScript(roleId, battleId, warInfo.warType, type, script);
        if (result) {
            if (!result.scriptBefore) result.scriptBefore = '';
            if (!result.scriptAfter) result.scriptAfter = '';
            return resResult(STATUS.SUCCESS, result);
        } else {
            console.error('script not created');
            return resResult(STATUS.INTERNAL_ERR);
        }

    }

    async getScriptByBattle(msg: { battleIds: Array<number> }, session: BackendSession) {

        const { battleIds } = msg;
        let roleId = session.get('roleId');
        let list = new Array();
        for (let battleId of battleIds) {
            let result = await RScriptRecordModel.findbyRoleAndBattle(roleId, battleId);
            if (result) {
                let { scriptBefore = '', scriptAfter = '' } = result;

                list.push({ battleId, scriptBefore, scriptAfter });
            } else {
                list.push({ battleId, scriptBefore: '', scriptAfter: '' });
            }
        }
        return resResult(STATUS.SUCCESS, { list });
    }

    // 获取悔棋次数
    async getRegretCnt(msg: { warId: number, battleCode: string }, session: BackendSession) {

        const { warId, battleCode } = msg;
        let roleId: string = session.get('roleId');
        let vipStartTime: number = session.get('vipStartTime');

        let battleRecord = await BattleRecordModel.getBattleRecordByCode(battleCode);
        if(!battleRecord || battleRecord.roleId != roleId || battleRecord.battleId != warId) {
            return resResult(STATUS.WRONG_PARMS);
        }
        let maxRegretCnt = getVipRegretCnt(vipStartTime);
        return resResult(STATUS.SUCCESS, { regretCnt: battleRecord.regretCnt||0, maxRegretCnt });
    }

    // 记录悔棋次数
    async incRegretCnt(msg: { warId: number, battleCode: string, count: number }, session: BackendSession) {

        const { warId, battleCode, count } = msg;
        let roleId: string = session.get('roleId');
        let vipStartTime: number = session.get('vipStartTime');

        let battleRecord = await BattleRecordModel.getBattleRecordByCode(battleCode);
        if(!battleRecord || battleRecord.roleId != roleId || battleRecord.battleId != warId) {
            return resResult(STATUS.WRONG_PARMS);
        }
        let maxRegretCnt = getVipRegretCnt(vipStartTime);
        let regretCnt = battleRecord.regretCnt||0;
        if(regretCnt + count > maxRegretCnt) {
            return resResult(STATUS.BATTLE_REGRET_MAX);
        }
        battleRecord = await BattleRecordModel.incRegretCnt(battleCode, count);

        return resResult(STATUS.SUCCESS, { regretCnt: battleRecord.regretCnt||0, maxRegretCnt });
    }

    async getMainStarBox(msg: {}, session: BackendSession) {
        let roleId = session.get('roleId');
        let role = await RoleModel.findByRoleId(roleId, 'warStar receivedBox');

        let result = getMainChapter(role);
        return resResult(STATUS.SUCCESS, result);
    }

    // 关卡列表
    async receiveChapterBox(msg: { chapter: number, id: number, warType: number }, session: BackendSession) {
        const { id, chapter, warType } = msg;
        if(!isNumber(id)) return resResult(STATUS.WRONG_PARMS);
        
        let roleId = session.get('roleId');
        let roleName = session.get('roleName');
        let sid = session.get('sid');

        let role = await RoleModel.findByRoleId(roleId, 'warStar receivedBox');
        let ids: number[] = [];
        let rewards: RewardInter[] = [];
        let star = getStarOfChapter(role.warStar, warType, chapter);

        let boxIds = id == 0? gameData.mainBoxByChapter.get(`${warType}_${chapter}`): [id];

        for(let id of boxIds) {
            let dicBox = gameData.mainBox.get(id);
            if(dicBox && dicBox.chapter == chapter && dicBox.warType == warType) {
                if(star >= dicBox.star && role.receivedBox.indexOf(id) == -1) {
                    rewards.push(...dicBox.reward);
                    ids.push(id);
                }
            }
        }
        if(ids.length <= 0) return resResult(STATUS.BATTLE_NO_BOX_CAN_RECEIVE);
        role = await RoleModel.receivedBox(roleId, ids);
        let goods = await addItems(roleId, roleName, sid, rewards, ITEM_CHANGE_REASON.RECEIVE_CHAPTER_BOX)

        let result = getMainChapter(role);
        return resResult(STATUS.SUCCESS, {
            ...result, goods
        });
    }

    // 关卡列表
    async receiveWarReward(msg: { chapter: number, warType: number }, session: BackendSession) {
        const { chapter, warType } = msg;
        
        let roleId = session.get('roleId');
        let roleName = session.get('roleName');
        let sid = session.get('sid');

        let role = await RoleModel.findByRoleId(roleId, 'receivedWarIds warStar');
        let { warStar, receivedWarIds = [] } = role;
        let warIds: number[] = [];
        let rewards: RewardInter[] = [];
        let mainWarRewards = gameData.mainWarReward.get(`${warType}_${chapter}`)??[];

        for(let { warId, cityReward } of mainWarRewards) {
            let index = warStar.findIndex(cur => cur.id == warId);
            if(index != -1 && receivedWarIds.indexOf(warId) == -1) {
                rewards.push(...cityReward);
                warIds.push(warId);
            }
        }
        if(warIds.length <= 0) return resResult(STATUS.BATTLE_NO_WAR_REWARD_CAN_RECEIVE);
        role = await RoleModel.receiveWarRewards(roleId, warIds);
        let goods = await addItems(roleId, roleName, sid, rewards, ITEM_CHANGE_REASON.RECEIVE_CHAPTER_BOX)

        return resResult(STATUS.SUCCESS, {
            receivedWarIds: warIds, goods: combineItems(goods)
        });
    }
}
