/**
 * entry触发后引起的下发
 */
import { getMails } from './mailService';
import { recentGuildMsgs, recentLeagueMsgs, recentPrivateChatInfos, recentServerGroupMsgs, recentSysMsgs, recentWorldMsgs } from './chatService';
import { getCurTask, getPvpTask } from './task/taskService';

import { RoleType } from '../db/Role';
import { Application, FrontendOrBackendSession, pinus, RpcClient } from 'pinus';
import { getRandEelmWithWeight, resResult } from '../pubUtils/util';
import { STATUS, PUSH_BATCH, PUSH_INTERVAL, CONSUME_TYPE, HERO_SELECT, ENTERY_ROLE_PICK, JEWEL_SELECT, ITEM_SELECT, SKIN_SELECT, PUSH_ROUTE, ARTIFACT_SELECT } from '../consts';
import { getAllShopList } from './shopService';
import { getGeneralRank, getRankFirstReward } from './rankService';
import { getFriendList, getApplyList } from './friendService';
import { getDailyBattleList } from './dailyBattleService';
import { getExpeditionStatus } from './expeditionService';
import { getTowerStatus, getHungupRewards, getTasks } from './battleService';
import { getAllAssistCnt, getCapExtraCnt, getComBtlLvByPlayerLv } from './comBattleService';
import { getDungeonData } from './dungeonService';
import { PvpSeasonResultModel } from '../db/PvpSeasonResult';
import { nowSeconds, getZeroPoint } from '../pubUtils/timeUtil';
import { getGachaList, getVisitedHeroList } from './activity/gachaService';
import { getSchoolList } from './roleService';
import { addRoleToGuildChannel } from './chatChannelService';
import { getMyGuildInfo, getUserGuildWithRefActive, getWishPool, getInvitationList } from './guildService';
import { getAuction } from './auctionService';
import { getGuildTrainInstance, getTrainBoxRewardsResult } from './guildTrainService';
import { getBossInstanceInfo } from './guildBossService';
import { getEvent } from './eventSercive';
import { getBattleListOfMain, getMainChapter } from './normalBattleService';
import { GuildModel, GuildType } from '../db/Guild';
import UserGuild, { UserGuildType } from '../db/UserGuild';
import { getAllGuildActivityStatus } from './guildActivity/guildActivityService';
import { getAllOnlineRoles, getAllServers, incConnectorNum } from './redisService';
import Item, { ItemModel } from '../db/Item';
import { PvpDefenseModel } from '../db/PvpDefense';
import { getDonation } from './donateService';
import { refChallengeCnt, sendLastSeasonRewardIfNotSent } from './pvpService';
import { HeroModel } from '../db/Hero';
import { JewelModel } from '../db/Jewel';
import { SkinModel } from '../db/Skin';
import { getAp } from './actionPointService';
import { HeroParam } from '../domain/roleField/hero';
import { pick } from 'underscore';
import { sendMessageToAllWithSuc, sendMessageToServerWithSuc, sendMessageToUser, sendMessageToUserWithSuc } from './pushService';
import { getSurvey } from './gmService';
import { ComBattleTeamModel } from '../db/ComBattleTeam';
import { INFO_WINDOW } from '../pubUtils/dicParam';
import { getLadderData } from './ladderService';
import { dispatch } from '../pubUtils/dispatcher';
import { PvpDataReturn } from '../domain/battleField/pvp';
import { getHiddenData } from './dataService';
import { ArtifactModel } from '../db/Artifact';

/**
 * init: 初始的时候是否推送 true-推 false-不推
 * refresh: 凌晨5点刷新时是否推送 true-推 false-不推
 * guild: 是否一定有了军团才要推送 true-必须要 false-不用
 */
const modules = [
    { id: 1, type: 'role', init: true, refresh: true, guild: false },
    { id: 2, type: 'battle', init: true, refresh: false, guild: false },
    { id: 3, type: 'chapter', init: true, refresh: false, guild: false },
    { id: 4, type: 'rank', init: false, refresh: false, guild: false },
    { id: 5, type: 'mail', init: false, refresh: false, guild: false },
    { id: 6, type: 'friend', init: false, refresh: true, guild: false },
    { id: 7, type: 'daily', init: false, refresh: true, guild: false },
    { id: 8, type: 'expedition', init: false, refresh: true, guild: false },
    { id: 9, type: 'tower', init: false, refresh: true, guild: false },
    { id: 10, type: 'comBattle', init: false, refresh: true, guild: false },
    { id: 11, type: 'dungeon', init: false, refresh: true, guild: false },
    { id: 12, type: 'pvp', init: false, refresh: true, guild: false },
    { id: 13, type: 'gacha', init: false, refresh: true, guild: false },
    // { id: 14, type: 'school', init: false, refresh: false, guild: false },
    { id: 15, type: 'task', init: false, refresh: true, guild: false },
    { id: 16, type: 'chat', init: false, refresh: false, guild: false },
    // { id: 17, type: 'event', init: false, refresh: true, guild: false },
    { id: 18, type: 'guild', init: false, refresh: true, guild: false },
    { id: 19, type: 'auction', init: false, refresh: false, guild: true },
    { id: 20, type: 'train', init: false, refresh: false, guild: true },
    { id: 21, type: 'boss', init: false, refresh: true, guild: true },
    { id: 22, type: 'wishPool', init: false, refresh: true, guild: true },
    { id: 23, type: 'guildActivity', init: false, refresh: true, guild: true },
    { id: 24, type: 'donate', init: false, refresh: true, guild: true },
    { id: 25, type: 'shop', init: false, refresh: true, guild: false },
    { id: 26, type: 'survey', init: false, refresh: true, guild: false },
    { id: 27, type: 'ladder', init: false, refresh: true, guild: false },
    { id: 28, type: 'hiddenData', init: true, refresh: true, guild: false },
]

export async function pushData(hasInit: boolean, role: RoleType, session: FrontendOrBackendSession, pushType: 'entry' | 'refresh' = 'entry') {
    try {
        const { roleId } = role;
        const sid = session.get('sid');

        pushEntryStart(roleId, sid);
        // 军团
        const guildData = await getGuildEntryData(role, sid, session);
        for(let { type, init, refresh, guild } of modules) {
            if(!hasInit && !init) continue;
            if(pushType == 'refresh' && !refresh) continue;
            if(!guildData.hasGuild && guild) continue;

            let data = await getModuleData(type, { role, session }, guildData);
            if (data) {
                pushEntryData(type, roleId, sid, data);
            }
        }

        pushEntryEnd(roleId, sid);
    } catch (e) {
        console.error(e.stack);
    }
}

export async function getModuleData(type: string, data: { role: RoleType, session: FrontendOrBackendSession }, guildData: { hasGuild: boolean, guild?: GuildType, userGuild?: UserGuildType, guildResult?: any } = { hasGuild: false }) {
    try {
        let { role, session } = data;
        const { roleId, serverId, roleName, guildCode } = role;
        let { hasGuild, guild, userGuild, guildResult } = guildData;
        switch (type) {
            case 'role': 
                let heros = await HeroModel.findByRole(role.roleId, [], HERO_SELECT.ENTRY, true);
                let jewels = await JewelModel.findbyRole(role.roleId, JEWEL_SELECT.ENTRY);
                let items = await ItemModel.findbyRole(role.roleId, ITEM_SELECT.ENTRY);
                let skins = await SkinModel.findbyRole(role.roleId, SKIN_SELECT.ENTRY);
                let artifacts = await ArtifactModel.findbyRole(role.roleId, ARTIFACT_SELECT.ENTRY);

                role['heros'] = heros.map(hero => new HeroParam(hero));
                role['jewels'] = jewels;
                role['consumeGoods'] = items;
                role['skins'] = skins;
                let apJson = await getAp(role.roleId, '', role.lv);
                role['apJson'] = apJson;
                role['ipLocation'] = role.fixedIpLocation||role.ipLocation||'未知';
                role['artifacts'] = artifacts;

                if (!role.showLineup) role.showLineup = role.topLineup.map(cur => cur.hid);
                role.heads = role.heads.filter(cur => cur.status);
                role.frames = role.frames.filter(cur => cur.status);
                role.spines = role.spines.filter(cur => cur.status);
                return pick(role, ENTERY_ROLE_PICK);
            case 'shop': // 商店
                return await getAllShopList(roleId, serverId);
            case 'rank': // 排名
                const rewards = await getRankFirstReward(role, serverId);
                return { rewards }
            case 'mail': // 邮件
                return await getMails(roleId, serverId);
            case 'friend': // 好友
                const friendList = await getFriendList(role);
                const applyList = await getApplyList(roleId);
                return { friendList, applyList }
            case 'daily': // 每日
                return await getDailyBattleList(role);
            case 'expedition': // 远征
                return await getExpeditionStatus(roleId, roleName, true);
            case 'tower': // 镇念塔
                return await getTowerEntryData(role);
            case 'comBattle': // 寻宝
                return await getComBattleEntryData(role);
            case 'dungeon': // 秘境
                return await getDungeonData(role);
            case 'pvp': // pvp
                return await getPvpEntryData(roleId);
            case 'gacha':
                const gachalist = await getGachaList(roleId);
                const visitList = await getVisitedHeroList(roleId);
                return { hasInit: true, gachalist, visitList }
            case 'school':
                return await getSchoolList(roleId);
            case 'guild':
                let invitation = await getInvitationList(roleId, '');
                return { ...guildResult, invitation };
            case 'auction':
                return hasGuild ? await getAuction(guildCode, session) : null;
            case 'train':
                if (hasGuild) {
                    let trainInstance = await getGuildTrainInstance(roleId, guild, userGuild);
                    let trainBoxRewards = await getTrainBoxRewardsResult(guildCode);
                    return { trainInstance, trainBoxRewards }
                }
                break;
            case 'boss':
                if (hasGuild) {
                    return await getBossInstanceInfo(userGuild, guild);
                }
                return null;
            case 'wishPool':
                return hasGuild ? await getWishPool(userGuild) : null;
            case 'donate':
                if (hasGuild) {
                    const { guildCode: code, donateCnt, receiveBoxs } = userGuild;
                    let { donateFund, reports, donationLv } = await getDonation(code, guild);
                    return { receiveBoxs, donateFund, reports, donateCnt: donateCnt || 0, donationLv };
                }
                break;
            case 'task':
                let tasks = await getCurTask(role.roleId, session);
                let pvpTask = await getPvpTask(role.roleId);
                return {...tasks, pvpTask}
            case 'chat':
                const worldMsgs = await recentWorldMsgs(serverId);
                const sysMsgs = await recentSysMsgs(serverId);
                const guildMsgs = await recentGuildMsgs(guildCode);
                const gvgMsgs = await recentServerGroupMsgs(serverId);
                const leagueMsgs = await recentLeagueMsgs(guildCode);
                const recentPrivateChats = await recentPrivateChatInfos(roleId, roleName);
                return { worldMsgs, sysMsgs, guildMsgs, gvgMsgs, leagueMsgs, recentPrivateChats };
            case 'event':
                return await getEvent(role.eventStatus, roleId, roleName);
            case 'battle':
                return await getBattleListOfMain(role);
            case 'guildActivity':
                return getAllGuildActivityStatus(role.guildCode, role.serverId);
            case 'chapter':
                return getMainChapter(role);
            case 'survey':
                return await getSurvey(role.roleId, role.lv);
            case 'ladder':
                return await getLadderData(role.roleId, false);
            case 'hiddenData':
                return getHiddenData();
            default:
                return null;
        }
    } catch (e) {
        console.error(e.stack);
        return false
    }
}

function pushEntryData<T>(type: string, roleId: string, sid: string, param: T) {
    let data = { type };
    data[type] = param;
    sendMessageToUserWithSuc(roleId, PUSH_ROUTE.ENTRY_DATA, data, sid);
}

function pushEntryStart(roleId: string, sid: string) {
    sendMessageToUserWithSuc(roleId, PUSH_ROUTE.ENTRY_DATA_START, {}, sid);
}

function pushEntryEnd(roleId: string, sid: string) {
    sendMessageToUserWithSuc(roleId, PUSH_ROUTE.ENTRY_DATA_END, {}, sid);
}


/**
 * 镇念塔初始数据
 * @param role 
 */
async function getTowerEntryData(role: RoleType) {
    const { roleId } = role;
    const status = await getTowerStatus(role);
    const hungUp = await getHungupRewards(roleId);
    if (!hungUp) {
        return { status, hungUp: {} }
    } else {
        return { status, hungUp: hungUp }
    }
}

/**
 * 获取是否存在pvp赛季奖励
 * @param roleId 
 */
async function getPvpEntryData(roleId: string) {
    let seasonNum: number = pinus.app.get('pvpSeasonNum');
    let seasonEndTime: number = pinus.app.get('pvpSeasonEndTime');
    let seasonStartTime: number = pinus.app.get('pvpSeasonStartTime');
    let seasonRewardTime: number = pinus.app.get('pvpSeasonRewardTime');

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

        let result = new PvpDataReturn(); // 返回对象
        result.setPvpConfig(seasonNum, seasonStartTime, seasonEndTime, seasonRewardTime);
        result.setPvpDefense(pvpDefense);
        result.calHasSaveDefense();
        let refChallengeObj = await refChallengeCnt(pvpDefense.challengeCnt, pvpDefense.challengeRefTime, seasonEndTime, roleId);
        let { challengeCnt } = refChallengeObj;
        result.setChallengeCnt(challengeCnt);
        return pick(result, ['challengeCnt', 'score', 'seasonWinNum', 'receivedBox', 'hasSaveDefense', 'seasonNum', 'seasonEndTime', 'seasonStartTime', 'seasonRewardTime'])
    } else {
        let result = new PvpDataReturn(); // 返回对象
        result.setPvpConfig(seasonNum, seasonStartTime, seasonEndTime, seasonRewardTime);
        return pick(result, ['seasonNum', 'seasonEndTime', 'seasonStartTime', 'seasonRewardTime'])
    }
}

/**
 * 获取军团信息，并加入军团频道
 * @param role 
 * @param sid 
 * @param session 
 */
async function getGuildEntryData(role: RoleType, sid: string, session: FrontendOrBackendSession) {

    if (role.hasGuild) {
        let userGuild = await getUserGuildWithRefActive(role.roleId);
        if (userGuild) {
            let guild = await GuildModel.findByCode(userGuild.guildCode, role.serverId);
            if (guild) {
                addRoleToGuildChannel(role.roleId, sid, userGuild.guildCode);

                let result = await getMyGuildInfo(role.roleId, sid, userGuild, guild, role.serverId, session);
                return { hasGuild: true, guild, userGuild, guildResult: result };
            }
        }
    }
    return { hasGuild: false }
}

async function getComBattleEntryData(role: RoleType) {
    let { roleId, lv, topLineupCe } = role;
    const assistCnt = await getAllAssistCnt(roleId);
    const blueprts = await Item.findByRoleAndType(roleId, CONSUME_TYPE.BLUEPRT);
    let { minLv, maxLv } = getComBtlLvByPlayerLv(lv);
    let refreshTime = nowSeconds() - INFO_WINDOW.TEAM_INFORMATION_TIME;
    const invitations = await ComBattleTeamModel.findInvitations(roleId, minLv, maxLv, topLineupCe, refreshTime, 1);
    let capExtraCnt = await getCapExtraCnt(roleId);
    return { assistCnt, blueprts, hasInvitation: invitations.length > 0, invitationTime: invitations.length > 0?invitations[0].inviteTime: 0, capExtraCnt }
}

export async function pushRefreshTime() {
    let todayZeroPoint = getZeroPoint();
    await sendMessageToAllWithSuc(PUSH_ROUTE.REFRESH_TIME, { todayZeroPoint });
}

export async function kickUser(app: Application, uid: string, message = STATUS.LOGIN_ERR) {
    let sessionService = app.get('sessionService');
    let sessions = sessionService.getByUid(uid);
    if (!!sessions) {
        sessions.forEach(session => {
            let roleId = session.get('roleId');
            let sid = session.get('sid');
            sendMessageToUser(roleId, PUSH_ROUTE.REMOTE_LOGIN, resResult(message), sid);
        });
    }
    await sessionService.akick(uid);
}

export async function assignServer(roleId: string, session: FrontendOrBackendSession) {
    try {
        let activity = _assignServer(roleId, 'activity');
        let battle = _assignServer(roleId, 'battle');
        let chat = _assignServer(roleId, 'chat');
        let guild = _assignServer(roleId, 'guild');
        let role = _assignServer(roleId, 'role');
        let order = _assignServer(roleId, 'order');
        if(activity) {
            session.set('activityServer', activity);
            session.push('activityServer', () => { });
        }
        if(battle) {
            session.set('battleServer', battle);
            session.push('battleServer', () => { });
        }
        if(chat) {
            session.set('chatServer', chat);
            session.push('chatServer', () => { });
        }
        if(guild) {
            session.set('guildServer', guild);
            session.push('guildServer', () => { });
        }
        if(role) {
            session.set('roleServer', role);
            session.push('roleServer', () => { });
        }
        if(order) {
            session.set('orderServer', order);
            session.push('orderServer', () => { });
        }
    } catch(e) {
        console.log(e)
    }
}

function _assignServer(roleId: string, serverType: string) {
    let servers = pinus.app.getServersByType(serverType)||[];
    let sum = servers.reduce((pre, cur) => pre + (cur['num']||0), 0);
    let id = '';
    if(sum > 0) {
        let serversWithWeight = servers.map(cur => ({...cur, weight: sum - (cur['num']||0)}));
        let { dic: server } = getRandEelmWithWeight(serversWithWeight);
        id = server?.id;
    }
    if(!id) {
        id = dispatch(roleId, servers, serverType)?.id;
    }
    return id
}

export function incServerNum(sid: string, inc: number) {
    if(!pinus.app.getServerById(sid)) return;
    if(!pinus.app.getServerById(sid)['num']) {
        pinus.app.getServerById(sid)['num'] = 0;
    }
    pinus.app.getServerById(sid)['num'] += inc;
    if(pinus.app.getServerById(sid)['num'] < 0) {
        pinus.app.getServerById(sid)['num'] = 0;
    }
}

export async function leaveServer(session: FrontendOrBackendSession) {
    let activity = session.get('activityServer');
    let battle = session.get('battleServer');
    let chat = session.get('chatServer');
    let guild = session.get('guildServer');
    let role = session.get('roleServer');
    let order = session.get('orderServer');
    let sid = session.get('sid');
    pinus.app.rpc.connector.connectorRemote.incServerNum.broadcast(activity, -1);
    pinus.app.rpc.connector.connectorRemote.incServerNum.broadcast(battle, -1);
    pinus.app.rpc.connector.connectorRemote.incServerNum.broadcast(chat, -1);
    pinus.app.rpc.connector.connectorRemote.incServerNum.broadcast(guild, -1);
    pinus.app.rpc.connector.connectorRemote.incServerNum.broadcast(role, -1);
    pinus.app.rpc.connector.connectorRemote.incServerNum.broadcast(order, -1);

    incServerNum(sid, -1);
    incConnectorNum(sid, -1);
}