import {ApiCall, BaseConnection} from "tsrpc";
import {FriendManage} from '../../public/friend/manage';
import {GHManage} from '../../public/gonghui/manage';
import {HeroFun} from "../../public/hero";
import {PlayerFun} from '../../public/player';
import {TaskFun} from "../../public/task";
import {defaultUserAppend, UserFun} from '../../public/user';
import {ReqLogin, ResLogin} from "../../shared/protocols/user/PtlLogin";
import {setUidProcessId} from "../../clusterUtils";
import * as crypto from 'crypto';
import {EmailFun} from "../../public/email";
import {PublicShared} from "../../shared/public/public";
import {getGud, initGud} from "../../public/gud";

//登陆排队中的conn.id
let queueLogin = [
    //{call:call,binduid,binduid,conn:conn}
]
//排队间隔 ms
let delay = 1000;

setInterval(function () {
    doLoginQueue();
}, delay);

//获取排队顺序
function getQueueIndex(bindUid) {
    for (let i = 0; i < queueLogin.length; i++) {
        if (queueLogin[i].bindUid == bindUid) {
            return i;
        }
    }
    return null;
}

//conn断开了链接，取消排队
export function unQueueByConn(conn: BaseConnection) {
    //从queueLogin删除指定conn
    for (let i = 0; i < queueLogin.length; i++) {
        if (queueLogin[i].conn == conn) {
            queueLogin.splice(i, 1);
            break;
        }
    }
}

//默认入口
export default async function (call: ApiCall<ReqLogin, ResLogin>) {
    let bindUid = call.req.bindUid;
    let index = getQueueIndex(bindUid);
    if (index == null) {
        //增加到排队队列
        queueLogin.push({call: call, bindUid: bindUid, conn: call.conn});
    } else {
        queueLogin[index].call = call;
        queueLogin[index].conn = call.conn;
    }
    //需要排队的时间
    let needTime = delay * queueLogin.length;

    //需要等待的时间
    G.server.sendMsgByUid(call.uid, 'msg_s2c/LoginQueue', {
        delay: needTime
    })
}

//每N时间消费1个排队
async function doLoginQueue() {
    let data = queueLogin.shift();
    if (!data) return;
    let kickDelay = await kickUser(data.call.req.bindUid, data.call.req.sid);
    console.log("排队登陆中，剩余数量", queueLogin.length, "kickDelay", kickDelay);
    setTimeout(async function () {
        return await doLogin(data.call);
    }, kickDelay);
}


function md5String(password: string): string {
    const md5 = crypto.createHash('md5');
    md5.update(password);
    return md5.digest('hex');
}

async function kickUser(bindUid: string, sid: string) {
    let delayTime = 0;
    let user = await G.mongodb.collection('user').findOne({bindUid: bindUid, sid: sid}, {
        projection: {
            uid: 1
        }
    });
    if (user == null) {
        return delayTime;
    }
    let uid = user.uid;
    const loginConn = G.server.uid_connections[uid];
    let checkPid = await G.redis.hGet("uid2processId", uid);

    if (checkPid) {
        if (~~checkPid == process.pid && loginConn) {
            // 本进程踢线
            loginConn.sendMsg('msg_s2c/OtherLogin', loginConn.ip).then(v => {
                loginConn.close('otherLogin');
            });
        } else {
            // 跨进程踢线，注意这里是异步的，留200ms等原进程清理数据
            G.server.sendMsgByUid(uid, 'msg_s2c/OtherLogin', '1', String(process.pid));
            delayTime = 200;
        }
    }
    return delayTime;
}

async function doLogin(call: ApiCall<ReqLogin, ResLogin>) {
    //合法性检测
    let sign = call.req.sign;
    //本地测试时，直接用通用鉴权秘钥
    if (sign != "BPtqdLH9QTVbZk7v" && sign != md5String(call.req.bindUid + "ajklsdhjhquieb")) {
        return call.error("shibai");
    }

    // 查询账号
    let user = await G.mongodb.collection('user').findOne({bindUid: call.req.bindUid, sid: call.req.sid});
    let {_id, ...gud} = user ? user : await UserFun.createUser(call.conn, call.req.bindUid, call.req.sid);

    // 判断账号是否被封禁
    if (gud.status == 1) {
        return call.error(lng.user_10);
    }

    await initGud(gud.uid, gud);

    // 修复公会id
    await fixUnionData(gud);

    //记录玩家所在的进程，change: 更换到处理完踢线操作在写入。
    // setUidProcessId(gud.uid); 

    let obj = await UserFun.check(gud, call.req);
    call.conn.onlineTime = G.time;
    call.conn.uid = gud.uid;
    call.conn.bindUid = gud.bindUid;
    call.conn.gud = gud;
    call.conn.lshd = {};

    if (!!!user) {
        await firstLoginFun(call);
        firstLoginSendEmail(call)
    }

    G.mongodb.collection('loginLog').insertOne({
        uid: gud.uid,
        bindUid: gud.bindUid,
        sid: gud.sid,
        loginTime: G.time
    })

    call.succ({
        gud: gud,
        curTime: G.time,
        openTime: G.config.openTime,
        openDay: PublicShared.getOpenServerDay(),
        firstLogin: !!!user,
        otherData: obj
    });

    if (obj.todayFirstLogin) {
        // gud.loginTime 在check时已近被赋值为G.time
        G.emit('FIRST_LOGIN_EVERY_DAY', gud, (gud.loginTime || G.time) - 24 * 3600, G.time);
    }

    LoginFun(call);
}

/**新玩家首次登陆后执行 */
async function firstLoginFun(call: ApiCall<ReqLogin, ResLogin>) {
    await Promise.all([
        //插入探险数据
        G.mongodb.collection('tanxian').insertOne({
            uid: call.uid,
            eventTime: G.time,
            lastGuaJiTime: G.time,
            useFastGuaJiNum: 0,
            receivePrize: [],
            resetTime: G.time
        }),
        // 首号奖励
        await PlayerFun.sendPrize(call, G.gc.playerdata.hero),
        // 自动上阵
        HeroFun.setHeroPos(call),
        TaskFun.generateAllTask(call),
    ]);
}

/**新玩家首次登陆后执行 */
function firstLoginSendEmail(call: ApiCall<ReqLogin, ResLogin>) {
    let conf = G.gc.yuyuemail

    if (G.time > conf.overtime || G.time < conf.opentime) return

    setTimeout(() => {
        EmailFun.addEmail({
            uid: call.conn.uid,
            type: 'system',
            title: '',
            content: '',
            prize: conf.prize,
            lngTitle: conf.title,
            lngContent: conf.content
        });
    }, 10000)
}

/**玩家登陆后执行 */
async function LoginFun(call: ApiCall<ReqLogin, ResLogin>) {

    //检测玩家头像、头像框、聊天框、模型是否过期
    let modelChange = false;
    const model: ResLogin['gud']['model'] = {};
    const change: Partial<ResLogin['gud']> = {};
    if (call.conn.gud.head.time > 0 && call.conn.gud.head.time < G.time) {
        change.head = defaultUserAppend.head;
    }
    if (call.conn.gud.headFrame.time > 0 && call.conn.gud.headFrame.time < G.time) {
        change.headFrame = defaultUserAppend.headFrame;
    }
    if (call.conn.gud.chatFrame.time > 0 && call.conn.gud.chatFrame.time < G.time) {
        change.chatFrame = defaultUserAppend.chatFrame;
    }
    Object.entries(call.conn.gud.model).forEach(v => {
        if (v[1].time > 0 && v[1].time < G.time) {
            modelChange = true;
            model[v[0]] = defaultUserAppend.model[v[0]];
        } else model[v[0]] = Object.assign({}, v[1]);
    });
    if (modelChange) change.model = model;
    Object.keys(change).length > 0 && PlayerFun.addAttr(call, change);

    //上线重算战力
    call.conn.refreshPower();
    // ### globalDebug GHManage
    if (call.conn.gud.ghLevel == 1 && await GHManage.getGH(call.conn.gud.ghId)) {
        GHManage.hzsx(call.conn.gud);
    }

    if (!await G.redis.hGet('player:uids', call.conn.gud.uid)) {
        G.redis.hSet('player:uids', call.conn.gud.uid, 1)
    }
    FriendManage.addNew(call.conn.gud.uid);

    // 更新主线任务
    TaskFun.updateMainTask(call)

    // 推送当前主线任务信息，引导用
    let _taskInfo = await TaskFun.getFinishByTtype(call, [2], {finish: 0});
    G.server.sendMsgByUid(call.uid, 'msg_s2c/TaskChange', _taskInfo?.[2]?.[0])

    // 存入当前玩家多语言信息
    G.redis.rawSet(`user:lng:${call.uid}`, call.req.lng, {EX: 259200})

    // 临时修补
    // fixHeroShiwu(call)
}

// async function fixHeroShiwu(call: ApiCall) {
//     let list = await G.mongodb.collection("hero").find({$and: [{"uid": call.uid}, {"shiwu": {$exists: true}}]}).toArray()
//
//     list.map(i => {
//         let list = R.values(i.shiwu).filter(i => !i._id)
//         if (list.length == 0) return ''
//
//         let shiwuChange = {}
//         if (i.shiwu?.['1']?._id) {
//             shiwuChange['1'] = i.shiwu['1']
//         }
//         if (i.shiwu?.['2']?._id) {
//             shiwuChange['2'] = i.shiwu['2']
//         }
//
//         G.mongodb.collection("hero").findOneAndUpdate({_id: i._id}, {$set: {shiwu: shiwuChange}})
//     })
// }

// 修复玩家退出公会 但是gud中残留公会id
async function fixUnionData(gud: ResLogin["gud"]) {
    if (gud.ghId) {
        let ghdata = await G.mongodb.collection("gonghui").findOne(
            {_id: G.mongodb.conversionId(gud.ghId), 'players.uid': gud.uid}
        )
        if (!ghdata) {
            gud.ghId = "";
            gud.ghName = "";
            gud.ghLevel = 0;
            PlayerFun.changeAttr(gud.uid, {ghId: '', ghLevel: 0})
        }
    }
}