
import { HeroModel, HeroType } from '../db/Hero';
import Role, { RoleModel, RoleType, WarCount } from '../db/Role'
import { getLvByExp, getExpByLv, gameData, getDicApByLv } from '../pubUtils/data';
import { updateRoleOnlineInfo, updateUserInfo } from './redisService';
// import { switchOnFunc } from './funcSwitchService';
import { FUNC_OPT_TYPE, TASK_TYPE, WAR_TYPE, STATUS, KING_EXP_RATIO_TYPE, ITEM_CHANGE_REASON, POP_UP_SHOP_CONDITION_TYPE, HERO_SYSTEM_TYPE, PUSH_ROUTE } from '../consts';
import { BackendSession, pinus } from 'pinus';
import { REDIS_KEY } from '../consts';
import { Rank } from './rankService';
import { checkTask } from './task/taskService';
import { RScriptRecordModel } from '../db/RScriptRecord';
import { setAp } from './actionPointService';
import { resResult, shouldRefresh } from '../pubUtils/util';
import { GuildLeader, LineupParam } from '../domain/rank';
import { WarStar } from '../domain/dbGeneral';
import { uniq } from 'underscore';
import { checkPopUpCondition } from './activity/popUpShopService';
import { calculateCeWithRole } from './playerCeService';
import { sendMessageToUserWithSuc } from './pushService';
import { ActionPointModel } from '../db/ActionPoint';
import { GK_MAIN, GK_MAINELITE, RECHARGE } from '../pubUtils/dicParam';
import { rebateHistoryOrder } from './orderService';
import { updateGVGBattleRoleInfo } from './gvg/gvgTeamService';

export async function roleLevelup(type: KING_EXP_RATIO_TYPE, roleId: string, kingExp: number = 0, session: BackendSession) {
    const serverId = session.get('serverId');
    const sid = session.get('sid');
    const ip = session.get('ip');
    
    let role = await RoleModel.findByRoleId(roleId);
    let { lv = 1, exp = 0 } = role;
    let canGetExp = lv < gameData.maxPlayerLv.max; // 当主公超过最大级后，挑战结算不再获得经验值
    let ratio = gameData.kingExpRaio.get(lv)?.get(type)||0;
    let newExp = canGetExp ? exp + Math.floor(kingExp * ratio) : exp;
    let newLv = getLvByExp(newExp);

    role = await RoleModel.levelup(roleId, newLv, newExp);
    if (newLv > lv) { // 升级
        // await switchOnFunc(roleId, FUNC_OPT_TYPE.LEVEL_UP, newLv, session);
        await updateUserInfo(REDIS_KEY.USER_INFO, roleId, [{ field: 'lv', value: newLv }]);
        if(role.isGuildLeader) {
            await updateUserInfo(REDIS_KEY.GUILD_INFO, role.guildCode, [{ field: 'leader', value: new GuildLeader(role) }]);
        }

        let r = new Rank(REDIS_KEY.USER_LV, { serverId: role.serverId });
        await r.setRankWithRoleInfo(roleId, newLv, Date.now(), role);

        // 任务
        await checkTask(serverId, roleId, session.get('sid'), TASK_TYPE.ROLE_LV, { oldLv: lv, lv: newLv });
        // 弹出礼包
        await checkPopUpCondition(serverId, roleId, POP_UP_SHOP_CONDITION_TYPE.LV_TO, { oldLv: lv, newLv })
        // await calculateCeWithRole(HERO_SYSTEM_TYPE.ROLE_LV, roleId, serverId, sid, { lv: newLv });

        await updateRoleOnlineInfo(roleId, { lv: newLv });
        // 达到等级返利
        if(lv < RECHARGE.RECHARGE_RMB_RETURN_LEVEL_LIMITED && newLv >= RECHARGE.RECHARGE_RMB_RETURN_LEVEL_LIMITED) {
            rebateHistoryOrder(roleId, role.userInfo?.uid);
        }

        await updateGVGBattleRoleInfo(serverId, roleId, { lv });
    }
    let actordata: { lv: number, exp: number, getExp: number, mostExp: number }[] = [];
    for (let i = lv; i <= newLv; i++) {
        let lvObj = getExpByLv(i);

        if (!lvObj) break;
        let { sum, cur } = lvObj;
        let lastSum = sum - cur;
        // console.log(sum, cur, lastSum, exp, newExp);

        let startExp = exp > lastSum ? exp - lastSum : 0;
        let getExp = newExp > sum ? cur - startExp : newExp - lastSum - startExp;
        actordata.push({
            lv: i,
            exp: startExp, // 升级前经验
            getExp: getExp > 0 ? getExp : 0, // 获得的经验
            mostExp: cur
        });
        if(i != lv) { // 升级加体力
            let dicAp = getDicApByLv(i);
            let { ap, apBefore } = await setAp(serverId, roleId, ip, i, dicAp.restoreAp, sid, ITEM_CHANGE_REASON.LV_UP);
            await ActionPointModel.setLvRecord(roleId, i, ap, apBefore);
        }
    }
    // 推送
    if(canGetExp && kingExp >= 0) {
        sendMessageToUserWithSuc(roleId, PUSH_ROUTE.PLAYER_EXP_CHANGE, { isLvUp: newLv > lv, lv: newLv, exp: newExp }, sid);
    }

    return { actordata, lv: newLv, exp: newExp, kingExp: newExp - exp };
}


export async function checkBattleHeroes(roleId: string, seqIds: Array<number> = []) {
    let hids: number[] = [], lineup: LineupParam[] = [], heroes: HeroType[] = [];
    let flag = true;
    // if (seqIds.length <= 0) flag = false;

    const findHeroes = await HeroModel.findBySeqIdRange(seqIds, roleId);
    if (findHeroes.length != seqIds.length) flag = false;
    for (let seqId of seqIds) {
        let hero = findHeroes.find(cur => cur.seqId == seqId);
        if (!hero) {
            flag = false; break;
        }
        hids.push(hero.hid);
        lineup.push(new LineupParam(hero));
        heroes.push(hero);
    }
    return { isOK: flag, hids, heroes, seqIds, lineup };
}

export async function checkBattleHeroesByHid(roleId: string, heroes: Array<number> = []) {
    let flag = true;
    // if (heroes.length <= 0) flag = false;

    const findHeroes = await HeroModel.findByHidRange(heroes, roleId);
    if (findHeroes.length != heroes.length) flag = false;
    for (let hid of heroes) {
        let hero = findHeroes.find(cur => cur.hid == hid);
        if (!hero) flag = false;
    }
    return { isOK: flag, heroes: findHeroes };
}

export function calculateWarStar(warStar: WarStar[] = [], battleId: number, stars: number[] = []) {
    let newWarStars: WarStar[] = [];
    let newStars: number[] = [], newStar = 0;
    let hasFound = false;
    for(let curWarStar of warStar) {
        if(curWarStar.id == battleId) {
            let oldStars = curWarStar.stars||[];
            newStars = uniq([...stars, ...oldStars]);
            newStar = newStars.filter(cur => cur > 0).length;
            newWarStars.push({...curWarStar, star: newStar, stars: newStars});
            hasFound = true;
        } else {
            newWarStars.push(curWarStar);
        }
    }
    if(!hasFound) {
        newStar = stars.filter(cur => cur > 0).length;
        newStars = stars;
        let dicWar = gameData.war.get(battleId);
        if(dicWar) newWarStars.push({ id: battleId, warType: dicWar.warType, star: newStar, stars: newStars});
    }
    return { newWarStars, newStars, newStar };
}

export async function getBattleListOfMain(role: RoleType) {
    let types = [ WAR_TYPE.NORMAL, WAR_TYPE.VESTIGE, WAR_TYPE.MAIN_ELITE, WAR_TYPE.TOWER, WAR_TYPE.MYSTERY, WAR_TYPE.BRANCH_ELITE ];
    return await getBattleListOfTypes(role, types);
}

export async function getBattleListOfTypes(role: RoleType, types: number[]) {
    let result = [];
    for(let type of types) {
        let list = await getBattleList(role, type);
        result.push({ type, list });
    }
    return result
}

export async function getBattleList(role: RoleType, type: number) {
    let { roleId, warStar, warCount = [], refWarCount } = role;
    if(shouldRefresh(refWarCount, new Date())) {
        warCount = [];
    }

    let scripts = await RScriptRecordModel.findbyRole(roleId, type);

    let result = []; // 去重
    for (let { battleId, scriptBefore = '', scriptAfter = '' } of scripts) {

        result.push({
            battleId,
            status: 0,
            star: 0,
            stars: [],
            scriptBefore,
            scriptAfter
        });
    }
    for (let { id, star, stars = [], warType } of warStar) {
        if (warType == type) {
            let curResult = result.find(cur => cur.battleId == id);
            if (curResult) {
                curResult.status = 1;
                curResult.star = star;
                curResult.stars = stars;
            } else {
                result.push({
                    battleId: id,
                    status: 1,
                    star,
                    stars,
                    scriptBefore: '',
                    scriptAfter: ''
                });
            }
        }
    }
    if(type == WAR_TYPE.NORMAL || type == WAR_TYPE.MAIN_ELITE) {
        for(let data of result) {
            let curWarCount = warCount.find(cur => cur.warId == data.battleId);
            data.count = curWarCount? curWarCount.count: 0;
        }
    }
    result = result.sort((a, b) => { return a.battleId - b.battleId });
    return result;
}

export async function checkWarCountAndInc(warId: number, inc: number, role: RoleType, needRefresh: boolean) {
    let { roleId, warCount, refWarCount } = role;
    let dicWar = gameData.war.get(warId);
    let max = dicWar.warType == WAR_TYPE.NORMAL? GK_MAIN.GK_MAIN_SWEEP_TIMES: GK_MAINELITE.GK_MAINELITE_SWEEP_TIMES;
    if(needRefresh && shouldRefresh(refWarCount, new Date())) {
        let role = await RoleModel.updateRoleInfo(roleId, { refWarCount: new Date(), warCount: [] });
        warCount = role.warCount;
    }
    let curWarCount = warCount.find(cur => cur.warId == warId);
    let count = curWarCount? curWarCount.count: 0;
    if(inc <= 0) { // checkBattle
        if(count >= max) return resResult(STATUS.BATTLE_OR_SWEEP_CNT_NOT_ENOUGH);
    } else { // battleEnd & battleSweep
        if(count + inc > max) return resResult(STATUS.BATTLE_OR_SWEEP_CNT_NOT_ENOUGH);
    }
    if(inc > 0) {
        if(curWarCount) {
            curWarCount.count += inc;
        } else {
            warCount.push({ warId, count: inc });
        }
        await RoleModel.updateRoleInfo(roleId, { warCount });
    }
    return resResult(STATUS.SUCCESS);
}

export function getStarOfChapter(warStar: WarStar[], warTye: number, chapter: number) {
    let starOfChapter = 0
    for(let { id, star } of warStar ) {
        let dicWar = gameData.war.get(id);
        if(dicWar.warType == warTye && star > 0) {
            starOfChapter += star;
        }
    }
    return starOfChapter;
}

export function getMainChapter(role: RoleType) {
    let chapterArr: { chapter: number, star: number, warType: number }[] = [];
    for(let { id, star } of role.warStar ) {
        let dicWar = gameData.war.get(id);
        if((dicWar && (dicWar.warType == WAR_TYPE.NORMAL || dicWar.warType == WAR_TYPE.MAIN_ELITE)) && star > 0) {
            let curChapter = chapterArr.find(cur => cur.warType == dicWar.warType && cur.chapter == dicWar.chapter);
            if(!curChapter) {
                chapterArr.push({ chapter: dicWar.chapter, star, warType: dicWar.warType });
            } else {
                curChapter.star += star;
            }
        }
    }
    
    return {
        star: chapterArr,
        receivedBox: role.receivedBox||[],
        receivedWarIds: role.receivedWarIds||[],
    }
}
