import {Application, RouteRecord, FrontendOrBackendSession, HandlerCallback, pinus, FrontendSession} from "pinus";
// import { checkEvent } from '../../../services/eventSercive';
import { refresh } from '../../../services/refreshService';
import { checkPrivateChat, checkGuildChat, checkOtherChat, checkRoleName, checkGuildName } from "../../../services/sdkService";
import { resResult, genCode } from "../../../pubUtils/util";
import { checkWhiteList, MsgEncrypt } from "../../../pubUtils/sysUtil";
import { BLOCK_TYPE, CHANNEL_PREFIX, MSG_TYPE, PUSH_ROUTE, STATUS } from "../../../consts";
import { nowSeconds } from "../../../pubUtils/timeUtil";
import { getServerMainten } from "../../../services/gmService";
import { errlogger, infologger } from "../../../util/logger";
import { gameData } from "../../../pubUtils/data";
import { sendMessageToUser } from "../../../services/pushService";
import { isCheckWord, isSkipEncode } from "../../../pubUtils/sdkUtil";
import { isDevelopEnv } from "../../../services/utilService";
import { isNumber } from "underscore";
import { getServerCreateTime, redisClient } from "../../../services/redisService";
import { checkRouteParam } from "../../../services/checkParam";
import { isApiClose } from "../../../services/chatService";

export function globalFilter(app: Application) {
    return new Filter(app);
}

class Filter {
    private app: Application;
    private accessTime = new Map<string, number>();

    constructor(app: Application) {
        this.app = app;
    }

    public async before (routeRecord: RouteRecord, msg: any, session: FrontendOrBackendSession, next: HandlerCallback) {
        const serverId: number = session.get('serverId');
        const sid: string = session.get('sid');
        const roleId: string = session.get('roleId');
        const uid: number = session.get('userid');
        const ip: string = session.get('ip');
        // console.log('*********** global before ip', ip)
        let guildCode = session.get('guildCode');
        let blockType = session.get('blockType');
        const logCode = genCode(10);
        infologger.debug('global before', logCode, routeRecord.route, session && session.get('roleId'));

        if(!this.checkPassEncrypt(routeRecord.route)) {
            msg = this.decryptMsg(msg, session);
            if(!msg) return next(new Error('globalFilter'), resResult(STATUS.WRONG_PARMS, { route: routeRecord.route }));

            if(!this.checkTimeStamp(msg)) return next(new Error('globalFilter'), resResult(STATUS.TIMESTAMP_ERR, { route: routeRecord.route })); 
            if(!this.checkDuplicateAccess(msg)) return next(new Error('globalFilter'), resResult(STATUS.DUPLICATE_ACCESS, { route: routeRecord.route }))
        }
        msg = this.setMsg(msg, { logCode });
        // 访问频率控制
        if(!this.checkFrequency(routeRecord.route, msg, roleId)) {
            return next(new Error('globalFilter'), resResult(STATUS.ACCESS_BUSY, { route: routeRecord.route }));
        }
        if(!this.checkFunction(routeRecord.route, msg)) {
            return next(new Error('globalFilter'), resResult(STATUS.FUNCTION_CLOSE, { route: routeRecord.route }));
        }

        if(!checkRouteParam(routeRecord.route, msg)) {
            return next(new Error('globalFilter'), resResult(STATUS.WRONG_PARMS, { route: routeRecord.route }));
        }

        // 玩家屏蔽
        if(blockType == BLOCK_TYPE.BLOCK) return next(new Error('globalFilter'), resResult(STATUS.BLOCKED, { route: routeRecord.route }));
        if(blockType == BLOCK_TYPE.BAN && this.checkBanRoute(routeRecord.route)) {
            return next(new Error('globalFilter'), resResult(STATUS.BANNED, { route: routeRecord.route }));
        }
    
        session.push('teamCode', () => {});
    
        if(isCheckWord(isDevelopEnv())) {
            let hasBlockWords = await this.checkHasBlockWord(routeRecord.route, msg, roleId, serverId, guildCode);
            if(hasBlockWords) return next(new Error('globalFilter'), resResult(STATUS.BLOCK_WORDS, { route: routeRecord.route }));
        }
    
        if(routeRecord.route == 'guild.guildHandler.createGuild') {
            let guildCode = genCode(6);
            msg.guildCode = guildCode;
        }

        if(await this.checkMainten(serverId, sid, ip, uid)) {
            sendMessageToUser(roleId, PUSH_ROUTE.SERVER_MAINTENANCE, resResult(STATUS.SERVER_MAINTENANCE), sid);
            return next(new Error('globalFilter'), resResult(STATUS.SERVER_MAINTENANCE, { route: routeRecord.route }));
        }

        next(null);
    };
    
    public async after (err: Error, routeRecord: RouteRecord, msg: any, session: FrontendOrBackendSession, resp: any, next: HandlerCallback) {
        //刷新
        await refresh(session);

        infologger.debug('global after', msg && msg.logCode, routeRecord.route, msg && JSON.stringify(msg),  resp && JSON.stringify(resp), session && session.get('roleId'));
        if(!this.checkPassEncrypt(routeRecord.route)) {
            let data = this.encryptMsg(resp, session);
            if(!data) return next(new Error('response data err'));
            resp = this.setResp(resp, data);
        }

        next(null);
    };

    // 检查调用频率
    private checkFrequency (route: string, msg: any, roleId: string) {
        let protect = gameData.serverConst.PROTECT_API.find(cur => {
            if(cur.route == route) {
                let paramOK = true;
                for(let key in cur.param) {
                    if(msg[key] != cur.param[key]) {
                        paramOK = false; break;
                    }
                }
                if(paramOK) return true;
            } 
            return false;
        });
        if(!protect) {
            return true;
        }
        let keyOfRouteWithUser = `${protect.id}_${roleId}`;
        if(this.accessTime && this.accessTime.has(keyOfRouteWithUser)) {
            console.log(this.accessTime.get(keyOfRouteWithUser) - Date.now());
            if(Date.now() - this.accessTime.get(keyOfRouteWithUser) < protect.interval) {
                errlogger.info(`${keyOfRouteWithUser} 间隔时间小于${protect.interval}ms，为 ${Date.now() - this.accessTime.get(keyOfRouteWithUser)}`);
                return false;
            }
        }
        if(!this.accessTime) this.accessTime = new Map<string, number>();
        this.accessTime.set(keyOfRouteWithUser, Date.now());
        return true;
    }
    
    // 检查该接口是否关闭
    private checkFunction (route: string, msg: any) {
        if(isApiClose()) {
            let isClose = gameData.serverConst.CLOSE_APIS.find(cur => {
                if(cur.route == route) {
                    if(cur.param == null) return true;
                    for(let key in cur.param) {
                        if(cur.param[key] == true && msg[key] != undefined) return true;
                        if(cur.param[key] && msg[key] == cur.param[key]) return true;
                    }
                } 
                return false;
            });
            return !isClose
        }
        return true;
    }
    
    // 这个接口是否需要加密解密
    private checkPassEncrypt (route: string) {
        return isSkipEncode(isDevelopEnv()) || route.startsWith('gm.') || route == 'connector.entryHandler.gmEnter' || route == 'gate.gateHandler.queryEntry';
    }
    
    // 解密接口
    private decryptMsg (msg: any, session: FrontendOrBackendSession) {
        let msgEncrypt = new MsgEncrypt({ encodeK: msg?.k, encodeV: msg?.v });
        let { aesKey, aesIV } = msgEncrypt.getKv();
        msg = this.setMsg(msg, msgEncrypt.decryptMsg(msg?.data));
        if(!msg) return false;
    
        session.set('k', aesKey);
        session.set('v', aesIV);
        session.push('k', () => {});
        session.push('v', () => {});
        return msg
    }
    
    // 解密接口
    private encryptMsg (resp: any, session: FrontendOrBackendSession) {
        let msgEncrypt = new MsgEncrypt({ k: session.get('k'), v: session.get('v')});
        let data = msgEncrypt.encryptMsg(resp);
        return data;
    }

    // 传入的msg的修改
    private setMsg (msg:any, targetObj: any) {
        if(!msg || !targetObj || typeof targetObj != 'object') {
            return false;
        }
        delete msg.k;
        delete msg.v;
        if(typeof msg.data == 'string') delete msg.data;
        for(let key in targetObj) {
            if(targetObj.hasOwnProperty(key)) {
                msg[key] = targetObj[key];
            }
        }
        return msg;
    }

    // 传出的resp的修改
    private setResp(resp: any, data: string) {
        for(let key in resp) {
            if(resp.hasOwnProperty(key)) {
                delete resp[key];
            }
        }
        resp.data = data;
        return resp;
    }
    
    // 禁言的接口
    private checkBanRoute (route: string) {
        return [
            'chat.chatHandler.sendPrivateMessage',
            'chat.chatHandler.sendGroupMessage',
            'battle.comBattleHandler.sendTeamMsg',
            'battle.barrageHandler.sendBarrage',
            'guild.guildHandler.sendMail',
            'guild.guildHandler.setGuildInfo',
            'guild.gvgManageHandler.sendMail',
            'guild.gvgManageHandler.setGuildInfo',
            'guild.gvgManageHandler.setLeagueInfo',
        ].indexOf(route) != -1
    }
    
    // 检查聊天屏蔽词
    private async checkHasBlockWord (route: string, msg: any, roleId: string, serverId: number, guildCode: string) {
        let hasBlockWords = false;
        switch(route) {
            case 'chat.chatHandler.sendPrivateMessage':
                {
                    if(msg.type == MSG_TYPE.IMG) break;
                    let result = await checkPrivateChat(roleId, msg.targetRoleId, msg.content);
                    if(!result) hasBlockWords = true; 
                    break;
                }
            case 'chat.chatHandler.sendGroupMessage':
                {
                    if(msg.type == MSG_TYPE.IMG) break;
                    if(msg.channel == CHANNEL_PREFIX.GUILD) {
                        let result = await checkGuildChat(roleId, guildCode, msg.content);
                        if(!result) hasBlockWords = true; 
                        break;
                    } else {
                        let result = await checkOtherChat(roleId, msg.channel, msg.content);
                        if(!result) hasBlockWords = true; 
                        break;
                    }
                }
            case 'battle.comBattleHandler.sendTeamMsg':
                {
                    let result = await checkOtherChat(roleId, CHANNEL_PREFIX.TEAM, msg.content);
                    if(!result) hasBlockWords = true; 
                    break;
                }
            case 'battle.barrageHandler.sendBarrage':
                {
                    let result = await checkOtherChat(roleId, CHANNEL_PREFIX.WORLD, msg.content);
                    if(!result) hasBlockWords = true; 
                    break;
                }
            case 'guild.guildHandler.sendMail':
                {
                    let result = await checkGuildChat(roleId, guildCode, msg.info);
                    if(!result) hasBlockWords = true; 
                    break;
                }
            case 'role.roleHandler.rename':
            case 'role.roleHandler.initRole':
                {
                    let result = await checkRoleName(roleId, msg.roleName);
                    if(!result) hasBlockWords = true; 
                    break;
                }
            case 'guild.guildHandler.createGuild':
            case 'guild.guildHandler.setGuildInfo':
                {
                    let result = await checkGuildName(guildCode, serverId, msg.name, msg.notice);
                    if(!result) hasBlockWords = true; 
                    break;
                }
            case 'guild.gvgManageHandler.createLeague':
            case 'guild.gvgManageHandler.setLeagueInfo':
                {
                    let result = await checkGuildName(guildCode, serverId, msg.name, msg.notice);
                    if(!result) hasBlockWords = true; 
                    break;
                }
            case 'guild.gvgManageHandler.sendMail':
            case 'guild.gvgManageHandler.recruit':
                {
                    let result = await checkGuildChat(roleId, guildCode, msg.content);
                    if(!result) hasBlockWords = true; 
                    break;
                }
        }
        return hasBlockWords;
    }

    // 检查是否维护中
    private async checkMainten(serverId: number, sid: string, ip: string, uid: number) {
        if(!sid) return false;

        const mainten = getServerMainten(serverId);
        if(!mainten) return false // 该服务器维护
        if(mainten.startTime > nowSeconds() || mainten.endTime < nowSeconds()) return false;
    
        let isWhiteList = await checkWhiteList(this.app.get('env'), ip, uid);
        if(isWhiteList) return false;
    
        return true
    }

    private checkTimeStamp(msg: { ts: number }) {
        let ts = msg.ts;
        let result = isNumber(ts) && Date.now() - ts <= gameData.serverConst.TIME_STAMP_OVER && ts - Date.now() <= gameData.serverConst.TIME_STAMP_OVER;
        delete msg.ts;
        return result;
    }

    private async checkDuplicateAccess(msg: { cs: string }) {
        let cs = msg.cs;
        delete msg.cs;
        let redis = redisClient();
        if(await redis.existsAsync(`cs: ${cs}`)) {
            return false
        }

        await redis.setAsync(`cs: ${cs}`, '');
        await redis.expireAsync(`cs: ${cs}`, gameData.serverConst.ACCESS_CODE_EXPIRE_TIME/1000);
        return true;
    }
}