
import { scheduleJob, Job, scheduledJobs, } from 'node-schedule';
import { PVPConfigModel, PVPConfigType } from '../db/PvpConfig';
import { nowSeconds, getTimeFun, getSeconds } from '../pubUtils/timeUtil';
import { getTodayGuildActivity, gameData } from '../pubUtils/data';
import { pvpSeasonEnd } from './pvpService';
import { getAllOnlineRoles, getAllServers, delGuildActivityRank, getServerCreateTime } from './redisService';
import { GUILD_ACTIVITY_TYPE, REFRESH_TIME, COUNTER, AUCTION_TIME, GM_MAIL_TYPE, SERVER_TIMER, ACTIVITY_TYPE, PUSH_ROUTE, STATUS, LADDER_STATUS, LADDER_SERVER_GAP_TIME, GVG_PERIOD } from '../consts';
import { pinus } from 'pinus';
import { settleGuildWeekly } from './guildService';
import { SendMailFun, sendMailsByGmMail, } from './mailService';
import { sendEndMsgToAllServer, sendGuildActivityStatus, setPreDayActiveData, incCurGuildActivityIndex } from './guildActivity/guildActivityService';
import { sendUngotDividendJob, startGuildAuction, startWorldAuction, stopAuction } from './auctionService';
import { DicGuildActivity } from '../pubUtils/dictionary/DicGuildActivity';
import { dispatch } from '../pubUtils/dispatcher';
import { createNewServer, initMarquee, setServerMainten } from './gmService';
import moment = require('moment');
import { reportOneOnline } from './authenticateService';
import { GVG, LADDER, PVP } from '../pubUtils/dicParam';
import { fetch37Words } from './sdkService';
import { GMMailModel, GMMailType } from '../db/GMMail';
import { Maintenance, ServerlistModel, ServerlistType } from '../db/Serverlist';
import { createMarqueeMsg, pushMarqueeMsg } from './sysChatService';
import { RegionModel, RegionType } from '../db/Region';
import { CreateServerParam } from '../domain/backEndField/params';
import { infologger } from '../util/logger';
import { MailModel, MailType } from '../db/Mail';
import { GroupMailModel, GroupMailType } from '../db/GroupMail';
import { ServerMailModel, ServerMailType } from '../db/ServerMail';
import { ActivityModel, ActivityModelType } from '../db/Activity';
import { TimeLimitRankData } from '../domain/activityField/timeLimitRankField';
import { sendRankMail, takeSnapshot } from './activity/timeLimitRankService';
import { ActivityGroupModel } from '../db/ActivityGroup';
import { sendMessageToServer } from './pushService';
import { getRandEelm, getRandSingleEelm, resResult } from '../pubUtils/util';
import { checkPopUpConditionWhenGuildActivityEnd } from './activity/popUpShopService';
import { pushRefreshTime } from './connectorService';
import { sendUnReceivedActivityDailyCoin } from './activity/dailyCoinService';
import { ladderTimeout, ladderTimeWillout, sendLadderDailyReward } from './ladderService';
import { LadderMatchRecModel } from '../db/LadderMatchRec';
import { LadderMatchModel } from '../db/LadderMatch';
import { getGroupShopTimers, refundGroupShop, setGroupShopToSetSum } from './activity/groupShopService';
import { HiddenDataModel, HiddenDataModelType } from '../db/HiddenData';
import { setHiddenData, setHiddenDataToMemory } from './dataService';
import { GVGConfigModel } from '../db/GVGConfig';
import { createNewGVGConfig, initLeaguePrepare } from './gvg/gvgService';
import { getFightTimeByPeriod, saveVestigeRankSchedule, sendUngotVestigeReward } from './gvg/gvgFightService';
import { gvgBattleEnd } from './gvg/gvgBattleService';

const PER_SECOND = 1 * 1000;
const PER_DAY = 24 * 60 * 60;
const PER_HOUR = 1 * 60 * 60;
const PER_MINUTE = 1 * 60;
var seasonEndJob: Job;
var seasonMakeRewardTimJobId: Job;
var seasonRefreshTimeJobId: Job;
let guildWeeklyJobId: Job;
let guildActStartJobId: Job; // 军团活动开启后每10(or 1)秒循环的定时任务，到结束活动清除
let guildActSecondsJobId: Job; // 军团活动开启后每10(or 1)秒循环的定时任务，到结束活动清除
let guildActEndJobId: Job; // 军团活动开启后每10(or 1)秒循环的定时任务，到结束活动清除

/**
 * 初始定时器
 */
export async function init() {

    console.log('******* init systimer *******')

    // pvp赛季
    await setPvpSeasonSchedule();

    // 周功勋结算任务
    guildWeeklyJobId = scheduleJob('settleGuildWeekly', '0 0 0 * * 1', settleGuildWeekly);

    // 每5分钟汇报在线玩家在线情况
    scheduleJob('reportOnline', '0 0/5 * * * *', reportOnlineSchedule);

    // 每天拉取37词库
    scheduleJob('fetchWord', '0 0 4 * * ?', fetch37Words);

    // 每天邮件
    checkCircleMail();
    // 每小时查询邮件推送
    checkMailByHour();

    // 军团活动排行榜
    guildActivitySchedule();

    // 拍卖行刷新：拍卖阶段刷新，分红发放
    auctionSchedule();

    // 每天5点推送刷新时间消息
    // 顺便每天0点计算前一天活跃玩家中位数战力
    scheduleJob('everyDayRefresh', `0 0 ${REFRESH_TIME} * * ?`, everydayRefresh);

    // 跑马灯
    await initMarquee();

    // 自动开服
    await initAutoCreateServer();

    // 限时排行榜
    await initTimeLimitRank();

    // 名将擂台每日奖励
    await ladderDailyReward();

    // 团购定时器
    initGroupShopSchedule();

    // 隐藏数据定时器
    initHiddenDataSchedule(true);

    // gvg每周日
    initGVGConfigSchedule();
}

// 每日刷新
export async function everydayRefresh() {
    let servers = await ServerlistModel.findByEnv(pinus.app.get('env'));
    pushRefreshTime();
    setPreDayActiveData(servers);
    sendUngotDividendJob();
    sendUnReceivedActivityDailyCoin(servers);
    pinus.app.rpc.guild.guildActivityRemote.clearActivityObj.broadcast();
}


// —————————————— PVP 及赛季相关 —————————————— //

export async function setPvpSeasonSchedule(fromBackend = false) {
    let pvpConfig = await PVPConfigModel.setCurrentPvp();
    await setSeasonEndJob(pvpConfig);
    await setPvpSeasonMakeRewardJob(pvpConfig); // 发送奖励定时器
    await setNextSeasonJob(pvpConfig); // 赛季开始定时器
    if(fromBackend) {
        setPvpSeasonNumToRemote(pvpConfig);
        setPvpSettleSeasonNumToRemote();
    } else {
        setPvpSeasonNum(pvpConfig);
        setPvpSettleSeasonNum();
    }
}

async function setSeasonEndJob(pvpConfig: PVPConfigType) {
    if (!!seasonEndJob) {
        seasonEndJob.cancel();
    }

    if(!pvpConfig || pvpConfig.seasonEndTime < nowSeconds()) {
        pvpConfig = await PVPConfigModel.findPVPConfig(pvpConfig? pvpConfig.seasonNum + 1: 1);
        if(!pvpConfig) return;
    }
    console.log('####### setSeasonEndJob', JSON.stringify(pvpConfig))

    seasonEndJob = scheduleJob('seasonEndJob', pvpConfig.seasonEndTime * 1000, async () => {
        console.log('************ setSeasonEndJob *********');
        setPvpSeasonNumToRemote(pvpConfig);
        let nextPvpConfig = await PVPConfigModel.findPVPConfig(pvpConfig.seasonNum + 1);
        await setSeasonEndJob(nextPvpConfig);
    });
}

async function setPvpSeasonMakeRewardJob(pvpConfig: PVPConfigType) {
    if (!!seasonMakeRewardTimJobId) {
        seasonMakeRewardTimJobId.cancel();
    }
    if(!pvpConfig || pvpConfig.seasonRewardTime - 60 < nowSeconds()) {
        pvpConfig = await PVPConfigModel.findPVPConfig(pvpConfig? pvpConfig.seasonNum + 1: 1);
        if(!pvpConfig) return;
    }
    console.log('####### setPvpSeasonMakeRewardJob', JSON.stringify(pvpConfig))
    seasonMakeRewardTimJobId = scheduleJob('seasonMakeRewardTimJobId', pvpConfig.seasonRewardTime * 1000 - 60 * 1000, async () => {
        console.log('************ seasonMakeRewardTimJobId *********');
        await pvpSeasonEnd(pvpConfig.seasonNum);
        let nextPvpConfig = await PVPConfigModel.findPVPConfig(pvpConfig.seasonNum + 1);
        await setPvpSeasonMakeRewardJob(nextPvpConfig);
    });
}

async function setNextSeasonJob(pvpConfig: PVPConfigType) {
    if (!!seasonRefreshTimeJobId) {
        seasonRefreshTimeJobId.cancel();
    }
    if(!pvpConfig || pvpConfig.seasonStartTime < nowSeconds()) {
        pvpConfig = await PVPConfigModel.findPVPConfig(pvpConfig? pvpConfig.seasonNum + 1: 1);
        if(!pvpConfig) return;
    }
    console.log('####### setNextSeasonJob', JSON.stringify(pvpConfig))
    
    seasonRefreshTimeJobId = scheduleJob('seasonRefreshTimeJobId', pvpConfig.seasonStartTime * 1000, async () => {
        console.log('************ setNextSeasonJob *********');
        setPvpSeasonNumToRemote(pvpConfig);
        await PVPConfigModel.setNextPvp(pvpConfig.seasonNum);
        let nextPvpConfig = await PVPConfigModel.findPVPConfig(pvpConfig.seasonNum + 1);
        await setNextSeasonJob(nextPvpConfig);
    });
}

export async function setPvpSeasonNumToRemote(pvpConfig: PVPConfigType) {
    await setPvpSeasonNum(pvpConfig);
    await pinus.app.rpc.battle.battleRemote.setPvpSeasonNum.broadcast(pvpConfig);
    await pinus.app.rpc.role.roleRemote.setPvpSeasonNum.broadcast(pvpConfig);
    await pinus.app.rpc.connector.connectorRemote.setPvpSeasonNum.broadcast(pvpConfig);
}

export async function setPvpSeasonNum(pvpConfig?: PVPConfigType) {
    if(!pvpConfig) {
        pvpConfig = await PVPConfigModel.findCurPVPConfig();
    }

    let now = nowSeconds();
    pinus.app.set('pvpSeasonNum', pvpConfig?.seasonNum||0);
    pinus.app.set('pvpSeasonStartTime', pvpConfig?.seasonStartTime||0);
    pinus.app.set('pvpSeasonEndTime', pvpConfig?.seasonEndTime||0);
    pinus.app.set('pvpSeasonRewardTime', pvpConfig?.seasonRewardTime||0);
    if(!pvpConfig || pvpConfig.seasonEndTime <= now) { // 赛季结束，需要显示下一赛季的倒计时
        let nextPvpConfig = await PVPConfigModel.findPVPConfig(pvpConfig? pvpConfig.seasonNum + 1: 1);
        if(nextPvpConfig) {
            pinus.app.set('pvpSeasonStartTime', nextPvpConfig.seasonStartTime);
        }
    }
}

export async function setPvpSettleSeasonNumToRemote(settledPvpConfig?: PVPConfigType) {
    setPvpSettleSeasonNum(settledPvpConfig);
    await pinus.app.rpc.battle.battleRemote.setPvpSettleSeasonNum.broadcast(settledPvpConfig);
    await pinus.app.rpc.role.roleRemote.setPvpSettleSeasonNum.broadcast(settledPvpConfig);
    await pinus.app.rpc.connector.connectorRemote.setPvpSettleSeasonNum.broadcast(settledPvpConfig);
}

export async function setPvpSettleSeasonNum(settledPvpConfig?: PVPConfigType) {
    if(!settledPvpConfig) {
        settledPvpConfig = await PVPConfigModel.getSettledConfig();
        if(!settledPvpConfig) return;
    }
    pinus.app.set('pvpSettleSeasonNum', settledPvpConfig.seasonNum);
}

export async function reportOnlineSchedule() {
    let allRoles = await getAllOnlineRoles();
    infologger.info('reportOnlineSchedule all roles count: ', allRoles.length);
    console.log('reportOnlineSchedule all roles count: ', allRoles.length)
    for (let { roleId, userCode, sid } of allRoles) {
        let result = reportOneOnline(roleId, userCode, sid, false);
        if (!result) continue;
    }
}

// —————————————— PVP 及赛季相关 end —————————————— //

// —————————————— 军团活动 —————————————— //
/**
 * 军团活动，每晚8点开启
 */
export async function guildActivitySchedule() {
    /***********guildActivitySchedule***********/
    if (guildActStartJobId) {
        guildActStartJobId.cancel();
    }

    let dicGuildActivity = getTodayGuildActivity();
    // console.log(dicGuildActivity)

    guildActStartJobId = scheduleJob('guildActivityStart', `${dicGuildActivity.startSeconds} ${dicGuildActivity.startMinute} ${dicGuildActivity.startTime} * * ?`, guildActivityStartSchedule);
}

// 军团晚间活动，每天8点开始
async function guildActivityStartSchedule() {
    await guildActivityStart();
}

export async function guildActivityStart(dicGuildActivity?: DicGuildActivity) {
    console.log('*******开始军团晚间活动****')
    if (!dicGuildActivity) dicGuildActivity = getTodayGuildActivity();
    if (!dicGuildActivity) return;

    console.log('********', dicGuildActivity.id, Date.now() + dicGuildActivity.duringTime * 1000)
    let servers = await getAllServers(); // 玩家serverId列表
    console.log('****** guildActSecondsJobId', guildActSecondsJobId)
    if (guildActSecondsJobId) {
        guildActSecondsJobId.cancel();
        guildActSecondsJobId = undefined;
    }
    if (guildActEndJobId) {
        guildActEndJobId.cancel();
        guildActEndJobId = undefined;
    }

    let aid = dicGuildActivity.id;
    await incCurGuildActivityIndex(aid);
    await pinus.app.rpc.guild.guildActivityRemote.guildActivityStart.broadcast(aid);

    if (aid == GUILD_ACTIVITY_TYPE.GATE_ACTIVITY) {
        guildActSecondsJobId = scheduleJob('guildActivitySeconds', '*/10 * * * * *', gateActivitySeconds);
        // 结束时间
        guildActEndJobId = scheduleJob('guildActivityEnd', Date.now() + dicGuildActivity.duringTime * 1000, gateActivityEnd);
    } else if (aid == GUILD_ACTIVITY_TYPE.CITY_ACTIVITY) {
        guildActSecondsJobId = scheduleJob('guildActivitySeconds', '*/10 * * * * *', cityActivitySeconds);
        // 结束时间
        guildActEndJobId = scheduleJob('guildActivityEnd', Date.now() + dicGuildActivity.duringTime * 1000, cityActivityEnd);
    } else if (aid == GUILD_ACTIVITY_TYPE.RACE_ACTIVITY) {

        // 开始活动
        let guildServers = pinus.app.getServersByType('guild');
        for (let serverId of servers) {
            let sid = dispatch(serverId.toString(), guildServers, 'guild');
            await pinus.app.rpc.guild.guildActivityRemote.raceActivityStart.toServer(sid.id, serverId);
        }

        guildActSecondsJobId = scheduleJob('guildActivitySeconds', '*/2 * * * * *', raceActivitySeconds);
        // 结束时间
        guildActEndJobId = scheduleJob('guildActivityEnd', Date.now() + dicGuildActivity.duringTime * 1000, raceActivityEnd);
    }

    for (let serverId of servers) {
        await sendGuildActivityStatus(serverId);
    }
    return true;
}

// 蛮夷入侵
// 结束军团活动
export async function gateActivityEnd() {

    console.log('*****gateActivityEnd');
    await sendEndMsgToAllServer();
    await pinus.app.rpc.guild.guildActivityRemote.guildActivityEnd.broadcast(GUILD_ACTIVITY_TYPE.GATE_ACTIVITY);

    if (guildActSecondsJobId) {
        guildActSecondsJobId.cancel();
        guildActSecondsJobId = undefined;
    }
    if (guildActEndJobId) {
        guildActEndJobId.cancel();
        guildActEndJobId = undefined;
    }
    checkPopUpConditionWhenGuildActivityEnd();
}

// 每10秒下发一次的任务
export async function gateActivitySeconds() {
    console.log('*****gateActivitySeconds')
    await pinus.app.rpc.guild.guildActivityRemote.sendRankToGuilds.broadcast(GUILD_ACTIVITY_TYPE.GATE_ACTIVITY);
}

// 诸侯混战
// 结束军团活动
export async function cityActivityEnd() {
    console.log('*****cityActivityEnd');
    await sendEndMsgToAllServer();
    await pinus.app.rpc.guild.guildActivityRemote.guildActivityEnd.broadcast(GUILD_ACTIVITY_TYPE.CITY_ACTIVITY);

    // 发完之后再做下周自动宣战
    // await autoDeclare();
    if (guildActSecondsJobId) {
        guildActSecondsJobId.cancel();
        guildActSecondsJobId = undefined;
    }
    if (guildActEndJobId) {
        guildActEndJobId.cancel();
        guildActEndJobId = undefined;
    }
    checkPopUpConditionWhenGuildActivityEnd();
}

// 每10秒下发一次的任务
export async function cityActivitySeconds() {
    console.log('*****cityActivitySeconds')
    await pinus.app.rpc.guild.guildActivityRemote.sendRankToGuilds.broadcast(GUILD_ACTIVITY_TYPE.CITY_ACTIVITY);
}

// 粮草先行
// 结束军团活动
export async function raceActivityEnd() {
    console.log('*****raceActivityEnd');
    await sendEndMsgToAllServer();
    await pinus.app.rpc.guild.guildActivityRemote.guildActivityEnd.broadcast(GUILD_ACTIVITY_TYPE.RACE_ACTIVITY);

    if (guildActSecondsJobId) {
        guildActSecondsJobId.cancel();
        guildActSecondsJobId = undefined;
    }
    if (guildActEndJobId) {
        guildActEndJobId.cancel();
        guildActEndJobId = undefined;
    }
    checkPopUpConditionWhenGuildActivityEnd();
}

// 每10秒下发一次的任务
export async function raceActivitySeconds() {
    console.log('*****raveActivitySeconds')
    let servers = await getAllServers(); // 玩家serverId列表
    let guildServers = pinus.app.getServersByType('guild');
    for (let serverId of servers) {
        let sid = dispatch(serverId.toString(), guildServers, 'guild');
        await pinus.app.rpc.guild.guildActivityRemote.calWoodenHorseAndSend.toServer(sid.id, serverId);
    }
}

// —————————————— 军团活动 end —————————————— //

// —————————————— 拍卖行 —————————————— //

let startGuildAuctionJobId: Job;
let startWorldAuctionJobId: Job;
let stopAuctionJobId: Job;

export async function auctionSchedule() {
    clearAuctionSchedule();
    let guildOpen = gameData.auctionTime.get(AUCTION_TIME.GUILD_OPEN);
    let worldOpen = gameData.auctionTime.get(AUCTION_TIME.WORLD_OPEN);
    let worldClose = gameData.auctionTime.get(AUCTION_TIME.WORLD_CLOSE);

    // console.log('***** auctionSchedule', guildOpen.hour, guildOpen.minute, guildOpen.seconds);
    // console.log('***** auctionSchedule', worldOpen.hour, worldOpen.minute, worldOpen.seconds);
    // console.log('***** auctionSchedule', worldClose.hour, worldClose.minute, worldClose.seconds);
    startGuildAuctionJobId = scheduleJob('startGuildAuction', `${guildOpen.seconds} ${guildOpen.minute} ${guildOpen.hour} * * ?`, startGuildAuction);
    startWorldAuctionJobId = scheduleJob('startWorldAuction', `${worldOpen.seconds} ${worldOpen.minute} ${worldOpen.hour} * * ?`, startWorldAuction);
    stopAuctionJobId = scheduleJob('stopAuction', `${worldClose.seconds} ${worldClose.minute} ${worldClose.hour} * * ?`, stopAuction);

}

function clearAuctionSchedule() {
    if (startGuildAuctionJobId) {
        startGuildAuctionJobId.cancel();
        startGuildAuctionJobId = undefined;
    }
    if (startWorldAuctionJobId) {
        startWorldAuctionJobId.cancel();
        startWorldAuctionJobId = undefined;
    }
    if (stopAuctionJobId) {
        stopAuctionJobId.cancel();
        stopAuctionJobId = undefined;
    }
}

// —————————————— 拍卖行 end —————————————— //

// —————————————— 邮件 —————————————— //
let circleMailJob: Job;
async function checkCircleMail() {
    if(circleMailJob) {
        circleMailJob.cancel();
    }
    circleMailJob = scheduleJob('circleMail', '0 0 20 * * ?', scheduleSendCircleMail);
    // scheduleSendCircleMail();
}

async function scheduleSendCircleMail() {
    let gmmails = await GMMailModel.findCircleMails(pinus.app.get('env'));
    await sendMailsByGmMail(gmmails);
}


let mailByHourJob: Job;
async function checkMailByHour() {
    if(mailByHourJob) {
        mailByHourJob.cancel();
    }
    // 每小时一次，为了和每天循环邮件任务不重复，设在每小时05分
    mailByHourJob = scheduleJob('setMailTimerByHour', '0 5 0/1 * * ?', async () => { 
        await setMailSchedule(false) 
    }); 
    setMailSchedule(true); // 初始时候启动一次
}

async function setMailSchedule(isInit: boolean) {
    let minuteNow = moment().minute();
    let beforeTime = minuteNow >= 5? moment().minute(5).unix(): moment().minute(5).add(-1, 'h').unix();
    let time = minuteNow >= 5? moment().minute(5).add(1, 'h').unix(): moment().minute(5).unix();

    let mails = await MailModel.findByTimeGap(beforeTime, time);
    let groupMails = await GroupMailModel.findByTimeGap(beforeTime, time);
    let serverMails = await ServerMailModel.findByTimeGap(beforeTime, time);

    let times: number[] = [];
    for(let { sendTime } of [...mails, ...groupMails, ...serverMails]) {
        if(times.indexOf(sendTime) == -1) times.push(sendTime);
    }
    
    for(let time of times) {
        if(Date.now() > time * 1000) {
            if(!isInit) await pushMailSchedule(time);
        } else {
            scheduleJob(`mailPush${time}`, time * 1000, async () => {
                await pushMailSchedule(time);
            });
        }
    }
}

// 当前时间到下一次定时器之间的定时器
export async function addMailsToSchedule(mails: MailType[], groupMails: GroupMailType[], serverMails: ServerMailType[]) {
    let minuteNow = moment().minute();
    let time = minuteNow >= 5? moment().minute(5).add(1, 'h').unix(): moment().minute(5).unix();

    let times: number[] = [];
    for(let { sendTime } of [...mails, ...groupMails, ...serverMails]) {
        if(sendTime * 1000 > Date.now() && sendTime < time && times.indexOf(sendTime) == -1) times.push(sendTime);
    }

    for(let time of times) {
        if(!scheduledJobs[`mailPush${time}`]) {
            scheduleJob(`mailPush${time}`, time * 1000, async () => {
                await pushMailSchedule(time);
            })
        }
    }
}

async function pushMailSchedule(time: number) {
    if(scheduledJobs[`mailPush${time}`]) scheduledJobs[`mailPush${time}`].cancel();

    let mails = await MailModel.findBySendTime(time);
    let groupMails = await GroupMailModel.findBySendTime(time);
    let serverMails = await ServerMailModel.findBySendTime(time);

    let f = new SendMailFun();
    f.setMails(mails, groupMails, serverMails);
    await f.pushToUsers();
}

// —————————————— 邮件 end —————————————— //

// —————————————— 维护 —————————————— //
let maintenInfos = new Map<string, { servers: ServerlistType[], maintenance: Maintenance }>(); // batchCode => {servers, maintenance}
export async function initMaintenance(servers?: ServerlistType[], fromApp = false) {
    if(!servers) servers = await ServerlistModel.findByEnv(pinus.app.get('env'));

    for(let server of servers) {
        let { maintenance } = server;
        if(maintenance && maintenance.isOpen) {
            if(!maintenInfos.has(maintenance.batchCode)) {
                maintenInfos.set(maintenance.batchCode, { servers: [], maintenance });
            }
            maintenInfos.get(maintenance.batchCode).servers.push(server);
        }
    }
    for(let [batchCode] of maintenInfos) {
        await setMaintenance(batchCode, fromApp);
    }
}

export async function initMaintenanaceInOtherServers() {
    const servers = await ServerlistModel.findByEnv(pinus.app.get('env'));

    for(let server of servers) {
        let { id, maintenance } = server;
        if(maintenance && maintenance.isOpen) {
            setServerMainten([id], maintenance.startTime, maintenance.endTime)
        }
    }
}

// 设置维护
async function setMaintenance(batchCode: string, fromApp: boolean) {
    let { maintenance } = maintenInfos.get(batchCode);

    console.log('***** maintenance', maintenance);

    let now = nowSeconds();
    // 发送消息
    if(maintenance.hasNotify && now < maintenance.startTime) { // 提前5分钟发通知
        if(now > maintenance.startTime - 5 * 60) { 
            maintenanceNotifySchedule(batchCode);
        } else {
            if(scheduledJobs[`maintenNotify${batchCode}`]) scheduledJobs[`maintenNotify${batchCode}`].cancel();
            scheduleJob(`maintenNotify${batchCode}`, (maintenance.startTime - 5 * 60) * 1000, () => {
                maintenanceNotifySchedule(batchCode);
            })
        }
    } else {
        if(scheduledJobs[`maintenNotify${batchCode}`]) scheduledJobs[`maintenNotify${batchCode}`].cancel();
    }
    // 开始维护
    console.log('******* setMaintenance', now, now < maintenance.endTime, now > maintenance.startTime)
    if(now < maintenance.endTime) {
        if(now > maintenance.startTime) {
            if(!fromApp) await startMaintenanceSchedule(batchCode);
        } else {
            scheduleJob(`startMainten${batchCode}`, maintenance.startTime * 1000, async () => {
                await startMaintenanceSchedule(batchCode);
            });
        }
    } else {
        if(scheduledJobs[`startMainten${batchCode}`]) scheduledJobs[`startMainten${batchCode}`].cancel();
    }

}

// 维护前通知
async function maintenanceNotifySchedule(batchCode: string) {
    let { servers, maintenance } = maintenInfos.get(batchCode);
    if(scheduledJobs[`maintenNotify${batchCode}`]) scheduledJobs[`maintenNotify${batchCode}`].cancel();
    if(scheduledJobs[`maintenSec${batchCode}`]) scheduledJobs[`maintenSec${batchCode}`].cancel();

    scheduleJob(`maintenSec${batchCode}`, `0 */1 * * * *`, async () => {
        for(let { id: serverId } of servers) {
            let msgData = await createMarqueeMsg('', '系统', serverId, '服务器即将维护，请玩家提前退出游戏');
            await pushMarqueeMsg(msgData);
        }
    })
}

// 开始维护
async function startMaintenanceSchedule(batchCode: string) {
    let { servers, maintenance } = maintenInfos.get(batchCode);
    let serverIds = servers.map(cur => cur.id);
    if(scheduledJobs[`maintenNotify${batchCode}`]) scheduledJobs[`maintenNotify${batchCode}`].cancel();
    if(scheduledJobs[`maintenSec${batchCode}`]) scheduledJobs[`maintenSec${batchCode}`].cancel();
    if(scheduledJobs[`startMainten${batchCode}`]) scheduledJobs[`startMainten${batchCode}`].cancel();

    // 向全服发送
    for(let { id: serverId } of servers) {
        await sendMessageToServer(serverId, PUSH_ROUTE.SERVER_MAINTENANCE, resResult(STATUS.SERVER_MAINTENANCE));
    }
    
    // 更新connectorRemote里面的维护服务器
    console.log('******** startMaintenanceSchedule', batchCode, serverIds, maintenance.startTime, maintenance.endTime)
    pinus.app.rpc.connector.connectorRemote.setServerMainten.broadcast(serverIds, maintenance.startTime, maintenance.endTime);
    pinus.app.rpc.activity.activityRemote.setServerMainten.broadcast(serverIds, maintenance.startTime, maintenance.endTime);
    pinus.app.rpc.battle.battleRemote.setServerMainten.broadcast(serverIds, maintenance.startTime, maintenance.endTime);
    pinus.app.rpc.chat.chatRemote.setServerMainten.broadcast(serverIds, maintenance.startTime, maintenance.endTime);
    pinus.app.rpc.guild.guildRemote.setServerMainten.broadcast(serverIds, maintenance.startTime, maintenance.endTime);
    pinus.app.rpc.order.orderRemote.setServerMainten.broadcast(serverIds, maintenance.startTime, maintenance.endTime);
    pinus.app.rpc.role.roleRemote.setServerMainten.broadcast(serverIds, maintenance.startTime, maintenance.endTime);
    // 数数flush
    pinus.app.rpc.activity.activityRemote.taflush.broadcast();
    pinus.app.rpc.battle.battleRemote.taflush.broadcast();
    pinus.app.rpc.chat.chatRemote.taflush.broadcast();
    pinus.app.rpc.connector.connectorRemote.taflush.broadcast();
    pinus.app.rpc.guild.guildRemote.taflush.broadcast();
    pinus.app.rpc.order.orderRemote.taflush.broadcast();
    pinus.app.rpc.role.roleRemote.taflush.broadcast();
    pinus.app.rpc.systimer.systimerRemote.taflush.broadcast();
}

// 提前结束维护
export async function stopMaintenance(batchCode: string, serverIds: number[]) {
    // console.log('***********', serverIds)
    let { servers = [], maintenance = {} as Maintenance } = maintenInfos.get(batchCode)||{};
    for(let id of serverIds) {
        let index = servers.findIndex(cur => cur.id == id);
        if(index != -1) servers.splice(index);
    }
    if(servers.length == 0) {
        if(scheduledJobs[`maintenNotify${batchCode}`]) scheduledJobs[`maintenNotify${batchCode}`].cancel();
        if(scheduledJobs[`maintenSec${batchCode}`]) scheduledJobs[`maintenSec${batchCode}`].cancel();
        if(scheduledJobs[`startMainten${batchCode}`]) scheduledJobs[`startMainten${batchCode}`].cancel();
        maintenInfos.delete(batchCode);
    }

    // 更新connectorRemote里面的维护服务器
    pinus.app.rpc.connector.connectorRemote.stopServerMainten.broadcast(serverIds);
    pinus.app.rpc.activity.activityRemote.stopServerMainten.broadcast(serverIds);
    pinus.app.rpc.battle.battleRemote.stopServerMainten.broadcast(serverIds);
    pinus.app.rpc.chat.chatRemote.stopServerMainten.broadcast(serverIds);
    pinus.app.rpc.guild.guildRemote.stopServerMainten.broadcast(serverIds);
    pinus.app.rpc.order.orderRemote.stopServerMainten.broadcast(serverIds);
    pinus.app.rpc.role.roleRemote.stopServerMainten.broadcast(serverIds);
}

// —————————————— 维护 end —————————————— //
// —————————————— 自动开服 —————————————— //
export async function initAutoCreateServer(region?: RegionType) {
    if(!region) region = await RegionModel.findRegionByEnv(pinus.app.get('env'));
    if(region && region.stategy && region.stategy.isOpen) {
        for(let timer of region.stategy.timers) {
            setStategyTimer(timer, region)
        }
    } else {
        if(scheduledJobs[`autoServer${SERVER_TIMER.FIVE_HALF}`]) scheduledJobs[`autoServer${SERVER_TIMER.FIVE_HALF}`].cancel();
        if(scheduledJobs[`autoServer${SERVER_TIMER.TEN_HALF}`]) scheduledJobs[`autoServer${SERVER_TIMER.FIVE_HALF}`].cancel();
        if(scheduledJobs[`autoServer${SERVER_TIMER.FIFTEEN_HALF}`]) scheduledJobs[`autoServer${SERVER_TIMER.FIVE_HALF}`].cancel();
        if(scheduledJobs[`autoServer${SERVER_TIMER.NINETEEN_HALF}`]) scheduledJobs[`autoServer${SERVER_TIMER.FIVE_HALF}`].cancel();
    }
}

function setStategyTimer(timerId: number, region: RegionType) {
    if(scheduledJobs[`autoServer${timerId}`]) scheduledJobs[`autoServer${timerId}`].cancel();
    let cron = '';
    switch(timerId) {
        case SERVER_TIMER.FIVE_HALF:
            cron = '0 30 5 * * *'; break;
        case SERVER_TIMER.TEN_HALF:
            cron = '0 30 10 * * *'; break;
        case SERVER_TIMER.FIFTEEN_HALF:
            cron = '0 30 15 * * *'; break;
        case SERVER_TIMER.NINETEEN_HALF:
            cron = '0 30 19 * * *'; break;
    }
    if(cron) {
        scheduleJob(`autoServer${timerId}`, cron, () => {
            autoCreateServerSchedule(region);
        })
    }
}

async function autoCreateServerSchedule(region: RegionType) {
    console.log('******* createNewServer *******')
    let latestServer = await ServerlistModel.findByServerId(region.latestServerUniqId);
    if(!latestServer || latestServer.playerCnt >= region.stategy.maxPlayerCnt) {
        let params = new CreateServerParam();
        params.setByRegionStategy(region, nowSeconds())
        await createNewServer(region, (latestServer?.serverId||0) + 1, params);
    }
}
// —————————————— 自动开服 end —————————————— //

// —————————————— 活动 start —————————————— //
async function initTimeLimitRank() {
    let activities = await ActivityModel.findActivityByType(ACTIVITY_TYPE.TIME_LIMIT_RANK);
    await updateTimeLimitRank(activities);
}

export async function updateTimeLimitRank(activities: ActivityModelType[]) {
    for(let activity of activities) {
        let { activityId } = activity
        let group = await ActivityGroupModel.findByActivity(activityId);
        for(let { serverIds } of group) {
            let servers = await ServerlistModel.findByServerIds(serverIds);
            for(let { env, openTime, id: serverId } of servers) {
                if(env == pinus.app.get('env')) {
                    let data = new TimeLimitRankData(activity, 0, openTime);
                    if(data.sendMailTime > Date.now()) {
                        await setSendRankMailSchedule(activityId, serverId, data);
                    }
                    if(data.rankEndTime > Date.now() && data.needSnapshot()) {
                        await setTakeRankSnapshotSchedule(activityId, serverId, data);
                    }
                }
            }
        }
    }
}

async function setSendRankMailSchedule(activityId: number, serverId: number, data: TimeLimitRankData) {
    console.log('########### setSendRankMailSchedule1 ########', activityId, serverId, data.sendMailTime)
    if(scheduledJobs[`rankMail_${serverId}_${activityId}`]) {
        scheduledJobs[`rankMail_${serverId}_${activityId}`].cancel();
    }

    scheduleJob(`rankMail_${serverId}_${activityId}`, data.sendMailTime, async () => {
        console.log('########### setSendRankMailSchedule2 ########')
        await sendRankMail(data, serverId);
    });
}


async function setTakeRankSnapshotSchedule(activityId: number, serverId: number, data: TimeLimitRankData) {
    console.log('########### setTakeRankSnapshotSchedule1 ########', activityId, serverId, data.rankEndTime)

    if(scheduledJobs[`snapshot_${serverId}_${activityId}`]) {
        scheduledJobs[`snapshot_${serverId}_${activityId}`].cancel();
    }

    scheduleJob(`snapshot_${serverId}_${activityId}`, data.rankEndTime, async () => {
        console.log('########### setTakeRankSnapshotSchedule2 ########')
        await takeSnapshot(data, serverId);
    })
}
// —————————————— 活动 end —————————————— //

// —————————————— 名将擂台 start —————————————— //
export async function setLadderCountDown(battleCode: string, time: number, status: LADDER_STATUS) {
    if(scheduledJobs[`ladder${battleCode}`]) {
        scheduledJobs[`ladder${battleCode}`].cancel();
    }
    let endTime = time + (status == LADDER_STATUS.CHECK? LADDER.LADDER_BATTLE_PREPARE_COUNTDOWN: LADDER.LADDER_BATTLE_COUNTDOWN) * 1000;
    scheduleJob(`ladder${battleCode}`, endTime, async () => {
        await ladderTimeWillout(battleCode, status);

        scheduledJobs[`ladder${battleCode}`].cancel();
        scheduleJob(`ladder${battleCode}`, endTime + LADDER_SERVER_GAP_TIME * 1000, async () => {
            await ladderTimeout(battleCode, status);
            scheduledJobs[`ladder${battleCode}`].cancel();
        });
    })
}

export async function cancelLadderCountDown(battleCode: string) {
    if(scheduledJobs[`ladder${battleCode}`]) {
        scheduledJobs[`ladder${battleCode}`].cancel();
    }
}

async function ladderDailyReward() {
    scheduleJob('ladderDailyReward', '0 0 22 * * ?', async () => {
        let servers = await ServerlistModel.findByEnv(pinus.app.get('env'));
        for(let { id } of servers) {
            await sendLadderDailyReward(id);
        }
    });
    let recs = await LadderMatchRecModel.getUncompleteData();
    let battleCodes: string[] = [];
    for(let { battleCode, checkTime, battleTime, status, serverId, defenseInfo } of recs) {
        if(status == LADDER_STATUS.CHECK) {
            if(Date.now() > checkTime + LADDER.LADDER_BATTLE_PREPARE_COUNTDOWN * 1000) {
                battleCodes.push(battleCode);
                await LadderMatchModel.unlock(serverId, defenseInfo?.roleId);
            } else {
                await setLadderCountDown(battleCode, checkTime, status)
            }
        } else if (status == LADDER_STATUS.BATTLE) {
            if(Date.now() > battleTime + LADDER.LADDER_BATTLE_COUNTDOWN * 1000 ) {
                battleCodes.push(battleCode);
                await LadderMatchModel.unlock(serverId, defenseInfo?.roleId);
            } else {
                await setLadderCountDown(battleCode, battleTime, status)
            }
        }
    }
    await LadderMatchRecModel.timeoutMany(battleCodes);
}
// —————————————— 名将擂台 end —————————————— //

// —————————————— 团购定时器 start —————————————— //
async function initGroupShopSchedule() {
    initSumSchedule();
    scheduleJob('groupShopRefund', '0 30 5 * * ?', () => {
        refundGroupShop();
    });
}

let initSumJobs = [];
export async function initSumSchedule() {
    let servers = await ServerlistModel.findByEnv(pinus.app.get('env'));
    let groupIds: number[] = [];
    for(let { id } of servers) {
        let groups = await ActivityGroupModel.findByServerId(id);
        for(let { groupId } of groups) {
            groupIds.push(groupId);
        }
    }

    let activities = await ActivityModel.findOpenActivityByType(groupIds, ACTIVITY_TYPE.GROUP_SHOP);
    let scheduleMap = new Map<number, { activityId: number, itemId: number, sum: number }[]>(); // 时间 => data
    for(let activity of activities) {
        let timers = await getGroupShopTimers(activity);
        for(let { time, itemId, sum } of timers) {
            if(time < Date.now()) continue;
            if(!scheduleMap.has(time)) {
                scheduleMap.set(time, []);
            }
            scheduleMap.get(time).push({ activityId: activity.activityId, itemId, sum });
        }
    }

    console.log('#### initSumJobs', initSumJobs.length);
    console.log('#### scheduleMap', scheduleMap);
    for(let job of initSumJobs) {
        job.cancel();
    }
    for(let [time, arr] of scheduleMap) {
        if(scheduledJobs[`groupShopSetSum${time}`]) {
            scheduledJobs[`groupShopSetSum${time}`].cancel();
        }
        let job = scheduleJob(`groupShopSetSum${time}`, time, async () => {
            await setGroupShopToSetSum(arr);
        });
        initSumJobs.push(job);
    }
}

// —————————————— 团购定时器 end —————————————— //

// —————————————— 配表兼容 start —————————————— //
let hiddenDataJob: Job;
export async function initHiddenData(data?: HiddenDataModelType, nextData?: HiddenDataModelType) {
    let now = nowSeconds();
    if(!data) data = await HiddenDataModel.findCurData(now);
    if(!nextData) nextData = await HiddenDataModel.findNextData(now);
    setHiddenData(data?.heroes, data?.goods, nextData?.refTime);
}

export async function initHiddenDataSchedule(isInit: boolean, data?: HiddenDataModelType) {
    if(hiddenDataJob) hiddenDataJob.cancel();
    let now = nowSeconds();
    if(!data) data = await HiddenDataModel.findCurData(now);
    let nextData = await HiddenDataModel.findNextData(data?.refTime??now);

    if(isInit) {
        await initHiddenData(data, nextData)
    } else {
        await setHiddenDataToMemory(data, nextData);
    }
    console.log('######## initHiddenDataSchedule', nextData);
    if(nextData) {
        scheduleJob(`hiddenData`, nextData.refTime * 1000, async () => {
            console.log('######## hiddenDataSchedule', nextData);

            await initHiddenDataSchedule(false, nextData);
        });
    }
}
// —————————————— 配表兼容 end —————————————— //

// —————————————— gvg start —————————————— //
let gvgBattleSecondJob: Job; // gvg每5秒的定时器
let gvgBattleCatapultJob: Job; // gvg投石车定时器

export async function initGVGConfigSchedule() {
    let config = await GVGConfigModel.findConfig();
    if(!config || nowSeconds() > config.scheduleTime) {
        config = await createNewGVGConfig();
    }
    
    if(scheduledJobs[`gvgTeam`]) scheduledJobs[`gvgTeam`].cancel();
    scheduleJob(`gvgTeam`, config.teamTime * 1000, async () => {
        await pinus.app.rpc.guild.guildRemote.setGVGConfig.broadcast(config);
    })
    if(scheduledJobs[`gvgConfig`]) scheduledJobs[`gvgConfig`].cancel();
    scheduleJob(`gvgConfig`, config.scheduleTime * 1000, createNewGVGConfig);

    if(nowSeconds() > config.prepareTime) {
        initLeaguePrepare();
    } else {
        if(scheduledJobs[`gvgPrepare`]) scheduledJobs[`gvgPrepare`].cancel();
        scheduleJob(`gvgPrepare`, config.prepareTime * 1000, initLeaguePrepare);
    }

    if(scheduledJobs[`gvgFight`]) scheduledJobs[`gvgFight`].cancel();
    scheduleJob(`gvgFight`, '0 0 22 * * ?', saveVestigeRankSchedule);
    
    if(scheduledJobs[`gvgBattle`]) scheduledJobs[`gvgBattle`].cancel();
    scheduleJob(`gvgBattle`, config.battleTime * 1000, sendUngotVestigeReward);

    let { startFightTime, endFightTime } = getFightTimeByPeriod(GVG_PERIOD.BATTLE, config.battleTime);

    if(nowSeconds() > startFightTime - GVG.GVG_GUARD_START_TIME && nowSeconds() < endFightTime) {
        if(gvgBattleSecondJob) gvgBattleSecondJob.cancel();
        gvgBattleSecondJob = scheduleJob('gvgBattleSecond', '*/5 * * * * *', gvgBattleSecondSchedule);
        if(gvgBattleCatapultJob) gvgBattleCatapultJob.cancel();
        gvgBattleCatapultJob = scheduleJob('gvgBattleCatapult', `*/${GVG.GVG_CATAPULT_TIME} * * * * *`, gvgBattleCatapult);
    } else {
        scheduleJob(`gvgBattleStartSchedule`, (startFightTime - GVG.GVG_GUARD_START_TIME) * 1000, gvgBattleStartSchedule);
    }
    if(scheduledJobs[`gvgBattleEndSchedule`]) scheduledJobs[`gvgBattleEndSchedule`].cancel();
    scheduleJob(`gvgBattleEndSchedule`, endFightTime * 1000, gvgBattleEndSchedule);
}

// gvg激战期开始定时器
export async function gvgBattleStartSchedule() {
    console.log('##### gvgBattleStartSchedule', Date.now())
    if(gvgBattleSecondJob) gvgBattleSecondJob.cancel();
    gvgBattleSecondJob = scheduleJob('gvgBattleSecond', '*/5 * * * * *', gvgBattleSecondSchedule);
    
    // 初始化投石车
    let guildServers = pinus.app.getServersByType('guild');
    if(guildServers.length > 0) {
        pinus.app.rpc.guild.guildRemote.gvgBattleStart.toServer(getRandSingleEelm(guildServers).id);
    }
    if(gvgBattleCatapultJob) gvgBattleCatapultJob.cancel();
    gvgBattleCatapultJob = scheduleJob('gvgBattleCatapult', `*/${GVG.GVG_CATAPULT_TIME} * * * * *`, gvgBattleCatapult);

}

// 每隔5秒的积分计算定时器
async function gvgBattleSecondSchedule() {
    console.log('********** gvgBattleSecondJob *************');
    pinus.app.rpc.guild.guildRemote.gvgBattleSeconds.broadcast();
}

// 每隔10秒的投石车投机定时器
async function gvgBattleCatapult() {
    console.log('********** gvgBattleCatapultJob *************');
    pinus.app.rpc.guild.guildRemote.catapultHurt.broadcast();
}

// gvg激战期结束定时器
export async function gvgBattleEndSchedule() {
    let guildServers = pinus.app.getServersByType('guild');
    if(guildServers.length > 0) {
        pinus.app.rpc.guild.guildRemote.gvgBattleEnd.toServer(getRandSingleEelm(guildServers).id);
    }
    // 定时器关闭
    if(gvgBattleSecondJob) {
        gvgBattleSecondJob.cancel();
        gvgBattleSecondJob = undefined;
    }
    if(gvgBattleCatapultJob) {
        gvgBattleCatapultJob.cancel();
        gvgBattleCatapultJob = undefined;
    }
         
}
// —————————————— gvg end —————————————— //