import { pinus } from "pinus";
import { GVGConfigModel, GVGConfigType } from "../../db/GVGConfig";
import { getTimeFun, getZeroPoint, getZeroPointOfTime, nowSeconds, WEEK_TO_SECOND } from "../../pubUtils/timeUtil";
import { getAllServerCreateTime, getServerCreateTime } from "../redisService";
import { GVG_PERIOD, GVG_SERVER_TYPE, LEAGUE_JOB, PUSH_ROUTE, SHOP_REFRESH_TYPE } from "../../consts";
import { GVGLeagueModel, GVGLeagueType } from "../../db/GVGLeague";
import { GVGLeaguePrepareModel } from "../../db/GVGLeaguePrepare";
import { GVGServerGroupModel } from "../../db/GVGServerGroup";
import { gameData } from "../../pubUtils/data";
import { getRandSingleEelm } from "../../pubUtils/util";
import { GVG } from "../../pubUtils/dicParam";
import { ServerlistModel } from "../../db/Serverlist";
import { GuildModel, GuildType } from "../../db/Guild";
import { GVGLeagueApplyModel } from "../../db/GVGLeagueApply";
import { GVGMainData, LeagueContributeInfo } from "../../domain/gvgField/returnData";
import { Contribute, GVGUserDataModel, GVGUserDataType } from "../../db/GVGUserData";
import { GVGUserItemModel } from "../../db/GVGUserItem";
import { GVGUserTaskModel } from "../../db/GVGUserTask";
import { getMyAuth } from "./gvgTeamService";
import { pick } from "underscore";
import { calFighterContribute, calProducerContribute } from "./gvgPrepareService";
import { calVestigeLeagueBoxRewards, getFightTimeByPeriod } from "./gvgFightService";
import { GVGVestigeLeagueRankModel } from "../../db/GVGVestigeLeagueRank";
import { GVGVestigeSumRankModel } from "../../db/GVGVestigeSumRank";
import { GVGTeamModel } from "../../db/GVGTeam";
import { sendMessageToGuildWithSuc } from "../pushService";
import { GVGCityModel } from "../../db/GVGCity";

// 定时器相关
export async function createNewGVGConfig() {
    let { teamTime, prepareTime, battleTime, scheduleTime } = getPeriodTime();
    const config = await GVGConfigModel.createNewConfig(teamTime, prepareTime, battleTime, scheduleTime);
    try {
        await pinus.app.rpc.guild.guildRemote.setGVGConfig.broadcast(config);
        await pinus.app.rpc.guild.guildRemote.setGVGServerGroup.broadcast();
        await pinus.app.rpc.chat.chatRemote.setGVGServerGroup.broadcast();
        await pinus.app.rpc.connector.connectorRemote.setGVGServerGroup.broadcast();
        await pinus.app.rpc.gm.gmRemote.setGVGServerGroup.broadcast();
        await pinus.app.rpc.systimer.systimerRemote.setGVGServerGroup.broadcast();
        await pinus.app.rpc.guild?.guildRemote?.clearBattleMemory.broadcast();
    } catch(e) {
        console.log('remote未初始完');
    }
    // 每周自动解散一些联军
    let autoLeagues = await GVGLeagueModel.findAutoCreateLeague();
    let needDissmissLeagueId: string[] = [], needDissmissLeagueCodes: string[] = [];
    for(let league of autoLeagues) {
        let groupKey = await getGroupKey(league.serverId);
        if(!await GVGCityModel.checkLeagueHasGuard(groupKey, league.leagueCode)) {
            needDissmissLeagueId.push(league._id);
            needDissmissLeagueCodes.push(league.leagueCode);
        }
    }
    await GVGLeagueModel.dismissByIds(needDissmissLeagueId);
    await GuildModel.leagueAutoDismiss(needDissmissLeagueCodes);
    for(let league of autoLeagues) {
        if(needDissmissLeagueCodes.indexOf(league.leagueCode) == -1) continue;
        for(let guildCode of league.guildCodes) {
            await sendMessageToGuildWithSuc(guildCode, PUSH_ROUTE.LEAGUE_DISSMISS, { leagueCode: league.leagueCode })
        }
    }

    await pinus.app.rpc.systimer.systimerRemote.initGVGConfigSchedule.broadcast();
    return config;
}

// 获取各个周期的时间，大约每周日5点的时候刷的
export function getPeriodTime() {
    let now = new Date();
    let curDay = now.getDay(); // 今天星期几

    let teamConfig = gameData.gvgPeriod.get(GVG_PERIOD.TEAM);
    let prepareConfig = gameData.gvgPeriod.get(GVG_PERIOD.PREPARE);
    let battleConfig = gameData.gvgPeriod.get(GVG_PERIOD.BATTLE);

    // 如果现在是周日，那么就是本周期的组队开始时间，如果是其他日子（启动初始化），就是下周期的组队开始时间
    let _teamTime = <number>getTimeFun().getTimeWithWeek(teamConfig.startDay); 
    let _prepareTime = <number>getTimeFun().getTimeWithWeek(prepareConfig.startDay);
    let _battleTime = <number>getTimeFun().getTimeWithWeek(battleConfig.startDay);
    console.log('_teamTime: ', _teamTime);

    let teamTime = curDay == 0? _teamTime: _teamTime - 7 * 86400;
    let prepareTime = curDay == 0? _prepareTime + 7 * 86400: _prepareTime;
    let battleTime = curDay == 0? _battleTime + 7 * 86400: _battleTime;
    let scheduleTime = teamTime + 7 * 86400;

    return { teamTime, prepareTime, battleTime, scheduleTime }
}

export async function getGVGConfigFromRemote() {
    let guildServers = pinus.app.getServersByType('guild');
    let server = getRandSingleEelm(guildServers);
    return await pinus.app.rpc.guild.guildRemote.getGVGConfig.toServer(server.id);
}

export async function setGVGConfig(config?: GVGConfigType) {
    if(!config) {
        config = await GVGConfigModel.findConfig();
    }
    if(config.teamTime > nowSeconds()) {
        config = null;
    }
    pinus.app.set('gvgConfigId', config?.configId||0);
    pinus.app.set('gvgTeamTime', config?.teamTime||0);
    pinus.app.set('gvgPrepareTime', config?.prepareTime||0);
    pinus.app.set('gvgBattleTime', config?.battleTime||0);
    pinus.app.set('gvgScheduleTime', config?.scheduleTime||0);
}

/**
 * 获取当前的赛期id
 * @returns 
 */
export function getGVGConfig() {
    let configId: number = pinus.app.get('gvgConfigId');
    let teamTime: number = pinus.app.get('gvgTeamTime');
    let prepareTime: number = pinus.app.get('gvgPrepareTime');
    let battleTime: number = pinus.app.get('gvgBattleTime');
    let scheduleTime: number = pinus.app.get('gvgScheduleTime');
    return { configId, teamTime, prepareTime, battleTime, scheduleTime }
}

export function getServerTypeByTime(openTime: number) {
    let today = nowSeconds();
    if(today - openTime < WEEK_TO_SECOND) return GVG_SERVER_TYPE.NO;
    let toWeek = getCurrentTeamTime(today);
    let openWeek = getCurrentTeamTime(openTime);
    let n = Math.floor((toWeek - openWeek) / WEEK_TO_SECOND);
    let day = new Date(openTime * 1000).getDay();
    if(day == 6) n++;  // 周六开服的服务器延后一周开启跨服
    return n <= GVG.GVG_CROSS_SERVICE_STARTTIME? GVG_SERVER_TYPE.SINGLE: GVG_SERVER_TYPE.MULTI;
}

function getCurrentTeamTime(time: number) {
    let teamConfig = gameData.gvgPeriod.get(GVG_PERIOD.TEAM);
    // 本周日
    let _teamTime = <number>getTimeFun(time).getTimeWithWeek(teamConfig.startDay, teamConfig.startHour, teamConfig.startMinute, teamConfig.startSecond); 
    return _teamTime > time? _teamTime - WEEK_TO_SECOND: _teamTime;
}

export async function getGVGServerType(serverId: number) {
    let openTime = await getServerCreateTime(serverId);
    return getServerTypeByTime(openTime)
}

export function getGVGPeriodData() {
    let { configId, teamTime, prepareTime, battleTime, scheduleTime } = getGVGConfig();
    let period = getCurPeriod();
    let countdownTime = teamTime;
    switch(period) {
        case GVG_PERIOD.TEAM: {
            countdownTime = prepareTime; break;
        }
        case GVG_PERIOD.PREPARE: {
            countdownTime = battleTime; break;
        }
        case GVG_PERIOD.BATTLE: {
            countdownTime = scheduleTime; break;
        }
    }

    console.log('######## getGVGPeriodData', configId, teamTime, prepareTime, battleTime, scheduleTime)
    return { configId, period, countdownTime };
}

export function getCurPeriod() {
    let { teamTime, prepareTime, battleTime, scheduleTime } = getGVGConfig();
    return getPeriodByTime(teamTime, prepareTime, battleTime, scheduleTime)
}

export function getPeriodByTime(teamTime: number, prepareTime: number, battleTime: number, scheduleTime: number) {
    let now = nowSeconds();
    if(now >= teamTime && now < prepareTime) {
        return GVG_PERIOD.TEAM;
    } else if (now >= prepareTime && now < battleTime) {
        return GVG_PERIOD.PREPARE;
    } else if (now >= battleTime && now < scheduleTime){
        return GVG_PERIOD.BATTLE;
    } else {
        return GVG_PERIOD.NO;
    }
}

export async function initLeaguePrepare() {
    const config = await GVGConfigModel.findConfig();
    if(!config) return;
    const leagues = await GVGLeagueModel.findAllLeague();
    let groupKeyByServerId = new Map<number, string>();
    for(let { leagueCode, memberCnt, serverId } of leagues) {
        let groupKey = groupKeyByServerId.get(serverId);
        if(!groupKey) {
            groupKey = await getGroupKey(serverId);
            groupKeyByServerId.set(serverId, groupKey);
        }

        await GVGLeaguePrepareModel.initData(config.configId, groupKey, leagueCode, memberCnt);
    }
}

export async function setGVGServerGroup() {
    const servers = await ServerlistModel.findByEnv(pinus.app.get('env'));
    const serverGroups = await GVGServerGroupModel.findByTime(nowSeconds());
    pinus.app.set('gvgServerGroup', servers.map(obj => {
        let server = serverGroups.find(cur => cur.serverId == obj.id);
        return { serverId: obj.id, groupId: server? server.groupId: obj.groupId }
    }));
}

// 查询本小区所在的战区
// gvgServerGroup: id-小区唯一id serverId-小区在大区下的id
export async function getGroupIdOfServer(id: number) {
    let arr: { serverId: number; groupId: number }[] = pinus.app.get('gvgServerGroup')||[];
    let obj = arr.find(cur => cur.serverId == id);
    return obj?.groupId||0;
}

// 查询当前和本小区同一个战区的其他小区
export async function getServersOfSameGroup(type: GVG_SERVER_TYPE, id: number) {
    if(type == GVG_SERVER_TYPE.SINGLE) return [id];
    
    let groupId = await getGroupIdOfServer(id);
    return await getServersByGroupId(groupId);
}

export async function getServersByGroupId(groupId: number) {
    let arr: { serverId: number; groupId: number }[] = pinus.app.get('gvgServerGroup')||[];
    let serverCreateTimes = await getAllServerCreateTime();

    return arr.filter(obj => {
        let openTime = parseInt(serverCreateTimes[obj.serverId]);
        return obj.groupId == groupId && getServerTypeByTime(openTime) == GVG_SERVER_TYPE.MULTI;
    }).map(obj => obj.serverId);
}

export async function getGVGInfoInGuild(roleId: string, guild: GuildType) {
    let hasLeague = !!guild.leagueCode;
    let serverId = guild.serverId;
    let { configId, period, countdownTime } = getGVGPeriodData();
    let { startFightTime, endFightTime } = getFightTimeByPeriod(period);
    let type = await getGVGServerType(serverId);
    
    if(hasLeague && configId > 0) {
        return {
            hasLeague,
            configId, period, countdownTime,
            type,
            applyCnt: await getApplyCnt(guild.leagueCode),
            ...await getMainData(roleId, guild.leagueCode, configId, period, countdownTime, type),
            leagueDetailRank: await getLeagueRankBox(configId, guild.leagueCode, roleId),
            myTeamCnt: await GVGTeamModel.getTeamCntByRole(roleId),
            startFightTime, endFightTime
        }
    } else {
        return {
            hasLeague,
            configId, period, countdownTime,
            startFightTime, endFightTime,
            type,
            invitationCnt: await getInviteCnt(guild.code)
        }
    }
}

export async function calLeagueCe(league: GVGLeagueType) {
    const guildCodes = league?.guildCodes||[];
    const guilds = await GuildModel.findByCodes(guildCodes);
    return guilds.reduce((pre, guild) => pre + guild.guildCe, 0);
}

// 检查当前赛季阶段
export async function checkGVGPeriod(p : GVG_PERIOD) {
    let { period } = getGVGPeriodData();
    return period == p;
}

// —————— 红点相关 —————— //
// 邀请
export async function getInviteCnt(guildCode: string) {
    let invitation = await GVGLeagueApplyModel.findInviteFromGuildWithoutPopulate(guildCode);
    return invitation.length
}

// 申请
export async function getApplyCnt(leagueCode: string) {
    let applies = await GVGLeagueApplyModel.findApplyFromLeagueWithoutPopulate(leagueCode);
    return applies.length
}

export async function getMainData(roleId: string, leagueCode: string, configId: number, period: number, countdownTime: number, serverType: number) {
    let data = new GVGMainData(configId, period, countdownTime, serverType);

    let league = await GVGLeagueModel.findByCode(leagueCode);
    if(league) {
        data.setLeague(league, {});
    
        let leaguePrepare = await GVGLeaguePrepareModel.findByLeague(configId, league.leagueCode);
        data.setLeagueParpare(leaguePrepare);
        data.setTechQueue(leaguePrepare?.techQueue||[]);
        
        let userData = await GVGUserDataModel.findByRole(configId, league.leagueCode, roleId);
        let items = await GVGUserItemModel.findByRole(configId, league.leagueCode, roleId);
        let tasks = await GVGUserTaskModel.findByRole(configId, league.leagueCode, roleId);
        data.setPlayerInfo(userData, getMyAuth(league, roleId), undefined, undefined, items, tasks);
        data.setLeagueCe(await calLeagueCe(league));
        data.setConstribute(await getContribute(userData));
    }

    let myInfo = pick(data.myInfo, ['auth', 'items', 'tasks', 'receivedLv']);
    return { ...pick(data, ['guildCnt', 'leagueLv', 'techQueue', 'constribute']), myInfo }
}

export async function getContribute(myUserData: GVGUserDataType) {
    const { contribute = new Contribute(), box = [] } = myUserData||{};

    const result: LeagueContributeInfo[] = [];
    for(let curJob of [LEAGUE_JOB.PRODUCER, LEAGUE_JOB.FIGHTER]) {
        let sumContribute = curJob == LEAGUE_JOB.PRODUCER? calProducerContribute(contribute): calFighterContribute(contribute); 
        let boxReceived = box.filter(boxId => {
            let dicBox = gameData.gvgContributeBox.get(boxId);
            return dicBox.job == curJob;
        });
        let disObj = new LeagueContributeInfo(curJob, sumContribute, boxReceived);
        result.push(disObj)
    }
    return result;
}

export async function getLeagueRankBox(configId: number, leagueCode: string, roleId: string) {
    let canReceiveLeagueRanks = await GVGVestigeLeagueRankModel.getCanReceiveRanks(configId, leagueCode, roleId);
    let canReceivePlayerRanks = await GVGVestigeSumRankModel.getCanReceiveRanks(configId, roleId);
    let { canReceiveBox } = calVestigeLeagueBoxRewards(canReceiveLeagueRanks, canReceivePlayerRanks);
    return { canReceiveBox }
}

export async function getGroupKey(serverId: number) {
    let serverType = await getGVGServerType(serverId);
    if(serverType == GVG_SERVER_TYPE.SINGLE) {
        return `s${serverId}`;
    } else {
        let groupId = await getGroupIdOfServer(serverId);
        return `g${groupId}`;
    }
}