import { Application, BackendSession, pinus } from 'pinus';
import { genCode, getRandSingleEelm, isTimestamp, resResult } from '../../../pubUtils/util';
import { STATUS } from '../../../consts/statusCode';
import { UpdateHiddenDataParam, CreatePvpConfigParam, CreateServerParam, UpdateRegionParams } from '../../../domain/backEndField/params';
import { RegionModel, RegionType } from '../../../db/Region';
import { gameData } from '../../../pubUtils/data';
import { Maintenance, ServerlistModel, ServerlistUpdate } from '../../../db/Serverlist';
import { nowSeconds, getFutureTime, getPastTime } from '../../../pubUtils/timeUtil';
import { calHiddenData, createNewServer } from '../../../services/gmService';
import { isNumber } from 'util';
import { MarqueeModel } from '../../../db/Marquee';
import { setApiIsCloseToRemote } from '../../../services/chatService';
import { PVPConfigModel } from '../../../db/PvpConfig';
import { initHiddenDataSchedule } from '../../../services/timeTaskService';
import { HiddenDataByIdModel, HiddenDataByIdModelTypeParam } from '../../../db/HiddenDataById';
import { isDevelopEnv } from '../../../services/utilService';
import { redisClient } from "../../../services/redisService";
import { REDIS_KEY } from "../../../consts";

export default function (app: Application) {
    return new GmHandler(app);
}

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

    // 保存大区配置
    async saveRegionConf(msg: UpdateRegionParams, session: BackendSession) {
        let params = new UpdateRegionParams(msg);
        let uid = session.get('uid');
        if(!params.checkParams()) return resResult(STATUS.WRONG_PARMS);
        let region: RegionType;
        if(params.id == 'new') {
            let update = params.getUpdateParam();
            region = await RegionModel.createNewRegion(update, uid);
        } else {
            let oldRegion = await RegionModel.findRegionById(params.id);
            if(!oldRegion) return resResult(STATUS.WRONG_PARMS);
            let update = params.getUpdateParam(oldRegion);
            region = await RegionModel.updateRegion(params.id, update, uid);
        }
        await pinus.app.rpc.systimer.systimerRemote.initAutoCreateServer.broadcast(region);
        
        return resResult(STATUS.SUCCESS);
    }

    // 手动开服
    async createNewServer(msg: CreateServerParam, session: BackendSession) {
        let params = new CreateServerParam(msg);
        let uid = session.get('uid');
        if(!params.checkParams()) return resResult(STATUS.WRONG_PARMS);

        console.log('*******', params);
        let curRegion = await RegionModel.findRegionByEnv(params.env);
        console.log('******* curRegion', curRegion);
        if(!curRegion) return resResult(STATUS.WRONG_PARMS);

        let { latestServer = 0 } = curRegion;
        let dicServerName = gameData.serverNames.get(latestServer + 1);
        if(!dicServerName) return resResult(STATUS.DIC_DATA_NOT_FOUND);

        await createNewServer(curRegion, latestServer + 1, params, uid);
        return resResult(STATUS.SUCCESS);
    }

    // 开始维护
    async startMaintenance(msg: { id: number, startTime: number, endTime: number, hasNotify: boolean }, session: BackendSession) {
        const { id, startTime, endTime, hasNotify } = msg;
        if(!id || !isNumber(startTime) || !isNumber(endTime)) return resResult(STATUS.WRONG_PARMS);
        if(endTime < nowSeconds()) return resResult(STATUS.WRONG_PARMS, '结束时间不能比现在早');
        const uid = session.get('uid');
        let server = await ServerlistModel.findByServerId(id);
        if(server && server.maintenance) {
            await pinus.app.rpc.systimer.systimerRemote.stopMaintenance.broadcast(server.maintenance.batchCode, [server.id]);
        }

        let newMaintenance: Maintenance = { batchCode: genCode(10), startTime, endTime, hasNotify, isOpen: true }
        server = await ServerlistModel.updateByServerId(id, { maintenance: newMaintenance });
        await pinus.app.rpc.systimer.systimerRemote.initMaintenance.broadcast([server]);

        return resResult(STATUS.SUCCESS,);
    }

    // 大区一起维护
    async startRegionMaintenance(msg: { startTime: number, endTime: number, hasNotify: boolean }, session: BackendSession) {
        const { startTime, endTime, hasNotify } = msg;
        if(!isNumber(startTime) || !isNumber(endTime)) return resResult(STATUS.WRONG_PARMS);
        if(endTime < nowSeconds()) return resResult(STATUS.WRONG_PARMS, '结束时间不能比现在早');
        const uid = session.get('uid');
        let newMaintenance: Maintenance = { batchCode: genCode(10), startTime, endTime, hasNotify, isOpen: true }
        let servers = await ServerlistModel.findByEnv(this.app.get('env'));
        let serverIdsOfBatchCode = new Map<string, number[]>();
        let serverIds: number[] = [];
        for(let server of servers) {
            if(server.maintenance) {
                let { batchCode } = server.maintenance;
                if(!serverIdsOfBatchCode.has(batchCode)) {
                    serverIdsOfBatchCode.set(batchCode, []);
                }
                serverIdsOfBatchCode.get(batchCode).push(server.id);
            }
            serverIds.push(server.id);
        }
        for(let [ batchCode, serverIds ] of serverIdsOfBatchCode) {
            await pinus.app.rpc.systimer.systimerRemote.stopMaintenance.broadcast(batchCode, serverIds);
        }

        await ServerlistModel.updateByServerIds(serverIds, { maintenance: newMaintenance });
        servers = await ServerlistModel.findByEnv(this.app.get('env'));
        await pinus.app.rpc.systimer.systimerRemote.initMaintenance.broadcast(servers);

        return resResult(STATUS.SUCCESS,);
    }

    // 提前结束维护
    async stopMaintenance(msg: { id: number }, session: BackendSession) {
        const { id } = msg;
        const uid = session.get('uid');
        let server = await ServerlistModel.findByServerId(id);
        let maintenance = server.maintenance;
        if(maintenance) {
            // 更新serverlist上的status
            await ServerlistModel.updateByServerId(server.id, { 'maintenance.isOpen': false } as ServerlistUpdate);
            
            // console.log('&&&', server, server.id)
            await pinus.app.rpc.systimer.systimerRemote.stopMaintenance.broadcast(maintenance.batchCode, [server.id]);
        }

        return resResult(STATUS.SUCCESS,);
    }

    // 大区一起结束维护
    async stopRegionMaintenance(msg: {}, session: BackendSession) {
        const { } = msg;
        const uid = session.get('uid');
        let servers = await ServerlistModel.findByEnv(this.app.get('env'));
        let serverIdsOfBatchCode = new Map<string, number[]>();
        let serverIds: number[] = [];
        for(let server of servers) {
            if(server.maintenance) {
                let { batchCode } = server.maintenance;
                if(!serverIdsOfBatchCode.has(batchCode)) {
                    serverIdsOfBatchCode.set(batchCode, []);
                }
                serverIdsOfBatchCode.get(batchCode).push(server.id);
                serverIds.push(server.id);
            }
        }

        for(let [ batchCode, serverIds ] of serverIdsOfBatchCode) {
            await pinus.app.rpc.systimer.systimerRemote.stopMaintenance.broadcast(batchCode, serverIds);
        }
        // 更新serverlist上的status
        await ServerlistModel.updateByServerIds(serverIds, { 'maintenance.isOpen': false } as ServerlistUpdate);

        return resResult(STATUS.SUCCESS,);
    }

    async sendMarquee(msg: { code: string }, session: BackendSession) {
        const { code } = msg;
        let marquee = await MarqueeModel.findByCode(code);
        if(!marquee) return resResult(STATUS.DB_DATA_NOT_FOUND);

        await MarqueeModel.updateData(code, { isEnable: true });
        let result = await this.app.rpc.systimer.systimerRemote.setMarquee.broadcast(marquee);
        if (!result) return resResult(STATUS.GM_MARQUEE_ERR);
        return resResult(STATUS.SUCCESS);
    }

    async cancelMarquee(msg: { code: string }, session: BackendSession) {
        const { code } = msg;
        await MarqueeModel.updateData(code, { isEnable: false });
        let result = await this.app.rpc.systimer.systimerRemote.cancelMarquee.broadcast(code);
        if (!result) return resResult(STATUS.GM_MARQUEE_CANCEL_ERR);
        return resResult(STATUS.SUCCESS);
    }

    async setApiIsClose(msg: { isCloseApi: boolean }, session: BackendSession) {
        const { isCloseApi } = msg;
        let region = await RegionModel.findRegionByEnv(pinus.app.get('env'));
        if(!region) return resResult(STATUS.SERVER_NOT_FOUND);

        region = await RegionModel.updateRegion(region.id, { isCloseApi });
        setApiIsCloseToRemote(region.isCloseApi);
        return resResult(STATUS.SUCCESS);
    }

    async savePvpConfig(msg: CreatePvpConfigParam, session: BackendSession) {
        let params = new CreatePvpConfigParam(msg);
        if(!params.checkParams()) return resResult(STATUS.WRONG_PARMS);
        if(params.seasonStartTime >= params.seasonEndTime) return resResult(STATUS.WRONG_PARMS, null, '开始时间不可晚于结束时间');
        if(params.seasonEndTime >= params.seasonRewardTime) return resResult(STATUS.WRONG_PARMS, null, '结束时间不可晚于奖励时间');

        if(await PVPConfigModel.checkTime(params.seasonNum, params.seasonStartTime, params.seasonRewardTime)) {
            return resResult(STATUS.WRONG_PARMS, null, '不可与其他赛季时间重叠');
        }

        let uid = session.get('uid');
        await PVPConfigModel.createPVPConfig(params.seasonNum, params.getUpdateParam(), uid);
        pinus.app.rpc.systimer.systimerRemote.setPvpSeasonSchedule.broadcast(true);

        return resResult(STATUS.SUCCESS);
    }

    async initHiddenData(msg: {pubArr: number[], hideArr: number[], type: number}, session: BackendSession) {
        let { pubArr = [], hideArr = [], type } = msg;
        let uid = session.get('uid');
        let datas = await HiddenDataByIdModel.findExistData(type);
        let existIds = datas.map(cur => cur.id);
        let insertParam: HiddenDataByIdModelTypeParam[] = [];
        for(let id of pubArr) {
            if(existIds.indexOf(id) == -1) {
                insertParam.push({ type, id, publishTime: getPastTime(), createdBy: uid, updatedBy: uid });
            }
        }
        for(let id of hideArr) {
            if(existIds.indexOf(id) == -1) {
                insertParam.push({ type, id, publishTime: getFutureTime(), createdBy: uid, updatedBy: uid });
            }
        }
        await HiddenDataByIdModel.createDatas(insertParam);
        await calHiddenData(uid);
        await initHiddenDataSchedule(false);

        return resResult(STATUS.SUCCESS);
    }

    async saveHiddenData(msg: UpdateHiddenDataParam, session: BackendSession) {
        let params = new UpdateHiddenDataParam(msg);
        if(!params.checkParams()) return resResult(STATUS.WRONG_PARMS);
        let uid = session.get('uid');
        let heroes: number[] = [], goods: number[] = [];
        for(let {type, id} of params.arr) {
            if(type == 1) heroes.push(id);
            if(type == 2) goods.push(id);
        }
        if(!isDevelopEnv()) {
            if(!await HiddenDataByIdModel.checkById(1, heroes, params.publishTime) || !await HiddenDataByIdModel.checkById(2, goods, params.publishTime)) {
                return resResult(STATUS.GM_OLD_REF_TIME_ERR);
            }
        }
        for(let {type, id} of params.arr) {
            await HiddenDataByIdModel.updateData(type, id, params.publishTime, uid);
        }
        await calHiddenData(uid);
        await initHiddenDataSchedule(false);
        return resResult(STATUS.SUCCESS);
    }

    async setServerOpenTime(msg: { serverId: number, serverOpenTime: number }, session: BackendSession) {
        let { serverId, serverOpenTime } = msg;
        if(!isTimestamp(serverOpenTime)) return resResult(STATUS.WRONG_PARMS);
        let server = await ServerlistModel.findByServerId(serverId);
        if(!server) return resResult(STATUS.WRONG_PARMS);

        server = await ServerlistModel.updateByServerId(serverId, { openTime: serverOpenTime });
        await redisClient().hsetAsync(REDIS_KEY.SERVER_OPEN_TIME, `${server.id}`, `${server.openTime}`);
        return resResult(STATUS.SUCCESS);
    }
}