import {existsSync, readFileSync} from 'fs';
import {join, resolve} from 'path';
import {LogLevel, WsServer} from 'tsrpc';
import {ServiceType as ServiceTypeCross, serviceProto as serviceProtoCross} from './cross/protocols/serviceProto';
import {Encrypt} from './encrypt';
import {FightFun} from './public/fight';
import {PayFun} from './public/pay';
import {Rank} from './public/rank/rank';
import {createWsClient} from './setWsClient';
import {MsgPay} from './shared/protocols/msg_c2s/MsgPay';
import {ServiceType, serviceProto} from './shared/protocols/serviceProto';
import {player} from './shared/protocols/user/type';
import {unQueueByConn} from './api_s2c/user/ApiLogin';
import {clusterPublish, setUidProcessId} from './clusterUtils';
import {clearGud, getGud} from './public/gud';
import { mylogger } from './gameLog';

export async function createWs() {

    if (G.argv.serverType == 'cross') {
        G.serverCross = new WsServer(serviceProtoCross, {
            //本地里，考虑到一台服务器可能既启动game，又启动cross
            //会导致端口冲突，所以本地特别配了一个crossPort
            //外网环境的config.json里，是没有crossPort的，无论游服还是跨服，都直接用msgPort
            port: G.config.crossPort || G.config.msgPort,
            json: true,
            //API超时时间5分钟，为登陆排队做准备
            apiTimeout: 300000,
            logLevel: G.argv.logModel as LogLevel,
            logger:mylogger
        }); 
        setCrossWs(G.serverCross);
        await G.serverCross.autoImplementApi(resolve(__dirname, 'api_cross'), true);
        await G.serverCross.start();
    } else {
        G.server = new WsServer(serviceProto, {
            port: G.config.msgPort,
            logLevel: G.argv.logModel as LogLevel,
            wss: getWssFile(),
            //API超时时间5分钟，为登陆排队做准备，只针对游服
            apiTimeout: 300000,
            logger:mylogger
        });
        setWs(G.server);
        await G.server.autoImplementApi(resolve(__dirname, 'api_s2c'), true);
        await G.server.start();

        G.argv.serverType == 'msg' && G.config.corssWsUrl && await createWsClient();
    }
}

function getWssFile() {

    if (G.config.wss.key && G.config.wss.cert && existsSync(join(__dirname, '../' + G.config.wss.key)) && existsSync(join(__dirname, '../' + G.config.wss.cert))) {

        return {
            key: readFileSync(join(__dirname, '../' + G.config.wss.key)),
            cert: readFileSync(join(__dirname, '../' + G.config.wss.cert))
        };
    } else {
        return null;
    }
}

const writeList = ['hongdian/Get']

function setWs(server: WsServer<ServiceType>) {

    encryptWs(server);

    server.uid_connections = {};

    //客户端连接后
    server.flows.postConnectFlow.push(conn => {
        conn.requstApiTime = {};
        conn.apiLock = {};
        return conn;
    });

    //客户端断开连接后
    server.flows.postDisconnectFlow.push(node => {
        unQueueByConn(node.conn);
        //玩家断开后以玩家uid对socket连接进行解绑
        if (node.conn.uid && server.uid_connections[node.conn.uid] && server.uid_connections[node.conn.uid] == node.conn) {
            server.uid_connections[node.conn.uid] = null;
            delete server.uid_connections[node.conn.uid];
        }
        if (node.conn.uid) {
            clearGud(node.conn.uid);
            G.emit('PLAYER_DISCONNECT', node.conn.uid);
        }
        //清理数据
        delete node.conn.gud;
        delete node.conn.requstApiTime
        delete node.conn.apiLock;
        delete node.conn.lshd;
        delete node.conn.onlineTime;

        return node;
    });

    //执行 API 接口实现之前
    server.flows.preApiCallFlow.push(async call => {
        // 临时停服维护方案
        // let lng = {
        //     "zh-TW": "停服維護中，請等待！",
        //     "ko": "서버 점검 중, 잠시만 기다려 주세요.",
        //     "ja": "サーバーメンテナンス中、しばらくお待ちください。",
        //     "en": "Server under maintenance. Please wait.",
        // }
        // call.error("", {code: -1, message: lng[call.req.lng] || lng["ja"]})
        // return null;

        //是否短时间内重复请求某个api
        // let timeIntervalLimit = call.service.conf?.timeIntervalLimit == undefined ? 500 : call.service.conf?.timeIntervalLimit;
        // if (new Date().getTime() - call.conn.requstApiTime[call.service.name] < timeIntervalLimit) {
        //     call.error('', { code: -1, message: '', time: timeIntervalLimit });
        // } else {
        //     call.conn.requstApiTime[call.service.name] = new Date().getTime();
        // }

        if (call.service.conf?.needGudKey?.length > 0) {
            let gud: player;
            gud = await getGud(call.uid);

            for (let key of call.service.conf.needGudKey) {
                if (!gud[key]) {
                    call.error(`you need has ${key}`);
                    return null;
                }
            }
        }

        //判断是否登录
        if (!['user/Ping', 'user/Login'].includes(call.service.name) && !call.conn.uid) {
            call.error("qingxiandenglu");
            return null;
        }

        //处理API锁，极限情况下只锁10s，防止死锁
        //在下方postApiReturnFlow里会解锁
        if (call.conn.apiLock[call.service.name] && new Date().getTime() - call.conn.apiLock[call.service.name] < 10000) {
            call.error('', {code: -100, message: '', time: 0});
            return null;
        }

        //API锁定
        if (!writeList.includes(call.service.name)) {
            call.conn.apiLock[call.service.name] = new Date().getTime();
        }
        //API耗时统计
        call.conn.requstApiTime[call.service.name] = new Date().getTime();

        //为了维持原有的逻辑，这里在每次请求时，将gud数据写入conn中
        if (call.conn.uid) {
            call.conn.gud = await getGud(call.conn.uid);
        }
        return call;
    });

    server.flows.preApiReturnFlow.push(node => {
        // if (!node.return.isSucc && node.return.err && node.return.err.type == "ApiError") {
        //     if (node.return.err?.message){
        //         node.return.err.message = ""
        //         node.call.return.err.message = ""
        //     }
        // }
        return node;
    });

    //API 接口返回结果（call.succ、call.error）之后
    server.flows.postApiReturnFlow.push(async node => {

        //接口响应速度统计
        let startTime = node.call.conn.requstApiTime[node.call.service.name]
        if (startTime && node.return.isSucc && G.mongodb) {
            let needTime = new Date().getTime() - startTime;
            G.mongodb.collection("apiCount").updateOne(
                {api: node.call.service.name},
                {$inc: {callnums: 1, needtimes: needTime}},
                {upsert: true}
            )
        }
        //玩家登陆后以玩家uid对socket连接进行绑定
        if (node.call.service.name == 'user/Login' && node.return.isSucc) {
            //玩家uid已经登陆在线 通知账号在其他地方登录
            const uid = node.return.res.gud.uid;
            server.uid_connections[uid] = node.call.conn;
            setUidProcessId(uid);
        }

        try {
            G.emit('API_CALL', node);
        } catch (e) {
            console.log('EMIT_API_CALL_ERROR', e);
        }

        //API解锁
        delete node.call.conn.apiLock[node.call.service.name];

        return node;
    });

    server.listenMsg(/^msg_c2s\//, async msgHandler => {

        switch (msgHandler.service.name as (keyof ServiceType['msg'] & `msg_c2s/${string}`)) {
            case 'msg_c2s/Pay':
                let msgPay = msgHandler.msg as MsgPay;
                let gud = await getGud(msgHandler.conn.uid);
                await PayFun.check(gud, msgPay.id, msgPay.args);
                break;
        }
    });
}

function setCrossWs(server: WsServer<ServiceTypeCross>) {

    // encryptWs(server);

    server.listenMsg('msg_cross/CrossChat', msg => {
        //跨服收到这条信息的时候，转发给连接到本跨服的所有客户端，即所有的服务端进程
        server.connections.forEach(conn => conn.sendMsg('msg_cross/CrossChat', msg.msg));
    });

    // server.listenMsg('msg_cross/HbzbSendUser', sendData => {
    //     Rank.list.hbzbCross.addNew(sendData.msg.user);
    // });

    server.listenMsg('msg_cross/HbzbJfsLog', handle => {
        FightFun.saveLog(handle.msg.uid, 'hbzbJfs', handle.msg.log);
        FightFun.saveLog(handle.msg.toUid, 'hbzbJfs', handle.msg.log);
    });

    server.listenMsg('msg_cross/HbzbZbsLog', handle => {
        FightFun.saveLog(handle.msg.uid, 'hbzbZbs', handle.msg.log);
        FightFun.saveLog(handle.msg.toUid, 'hbzbZbs', handle.msg.log);
    });

    // server.listenMsg('msg_cross/HbzbChangeRank', handle => {
    //     //Rank.list.hbzbZbsCross.changeRank(handle.msg.uid, handle.msg.toUid);
    // });
}

/**数据传输加解密 */
function encryptWs(server: WsServer) {
    //处理收到的数据前
    server.flows.preRecvDataFlow.push(node => {
        // 接收前解密
        node.data = Encrypt.decrypt(node.data);
        return node;
    });

    //发送数据前
    server.flows.preSendDataFlow.push(node => {
        // 发送前加密
        node.data = Encrypt.encrypt(node.data);
        return node;
    });
}

