import { ResLogin, playerAppend, playerInfo } from "../shared/protocols/user/PtlLogin";

//进程内数据玩家字典
const GUD : { [key: string]: {
    gud: ResLogin['gud'],
    version: {
        pid : number,
        ver?: number
    }
} } = {};


export async function clearGud(uid) {
    //玩家断线，清空uid数据
    if(GUD[uid]){
        console.log("玩家断线，清空GUD数据", uid);
        delete GUD[uid];
        G.ioredis.del(`gudVersion:${uid}_pid`);
        G.ioredis.del(`gudVersion:${uid}_ver`);
    }

    //清空Redis缓存
    G.ioredis.del(`tanxian:${uid}`);
    G.ioredis.del(`dxlt:${uid}`);
};

/**
 * 获取玩家的gud数据
 * ！！！警告：因为数据会进程内缓存，请将返回值当做引用类型看待
 * @param uid 玩家的uid
 * @returns 
 */
export async function getGud(uid:string) : Promise<ResLogin['gud']>{
    let gud;
    if(GUD[uid]){
        //如果本进程里存在，则判断版本号
        let redisVersion = await G.ioredis.mget([`gudVersion:${uid}_pid`, `gudVersion:${uid}_ver`]);

        if(redisVersion[0]==null || redisVersion[1]==null || redisVersion[0].toString() != (GUD[uid].version.pid).toString()){
            //最新的数据，已经不是本进程生成的了，则说明本进程持久了一份脏数据
            delete GUD[uid];
        }else{
            if(redisVersion[1].toString() != (GUD[uid].version.ver).toString()){
                //虽然数据是本进程生成的，但是版本号不一致，说明其他进程里更新过数据了
                gud = await getGudFromDB(uid);

                GUD[uid].gud = gud;
                GUD[uid].version.ver = parseInt(redisVersion[1]);
            }else{
                //本进程的数据是最新的，直接返回
                gud = GUD[uid].gud;
                //console.log("getGud==>", uid);
            }
        }
    }

    if(!gud){
        //本进程里没有，则直接从db获取
        gud = await getGudFromDB(uid);
    }

    return gud;
}

/**
 * 更新玩家的数据，注意，这里只负责更新进程内的缓存数据及维护数据版本
 * 实际DB的操作需要调用者自己做
 * @param uid 
 */
export async function setGud(uid:string, updateKV:{[key:string]:any}) : Promise<ResLogin['gud']>{
    let nver = await G.ioredis.incr(`gudVersion:${uid}_ver`);
    //如果数据不在本进程，只更新版本即可
    if(!GUD[uid])return;

    GUD[uid].version.ver = nver;
    Object.assign(GUD[uid].gud, updateKV);

    return GUD[uid].gud;
}

/**
 * 在本进程初始化玩家的数据
 * 该过程会将玩家数据从db里加热到内存
 * 仅允许在玩家登录的时候执行该方法
 * 并且玩家离线时，需要销毁对应的数据
 * 以避免多个进程持有同一个玩家的数据导致数据统一性错误
 * @param uid 
 * @param _gud 在登录时，前面的逻辑里已经查询过一次db了，没必要再查一次浪费性能，这种情况下则直接传入_gud
 */
export async function initGud(uid:string, _gud?:ResLogin['gud']) : Promise<ResLogin['gud']>{
    let gud = _gud || await getGudFromDB(uid);
    if(gud){
        console.log('initGud==>', uid);
        //设置gud版本号
        await G.ioredis.set(`gudVersion:${uid}_pid`, process.pid );
        let ver = await G.ioredis.incr(`gudVersion:${uid}_ver`);

        GUD[uid] = {
            gud : gud,
            version : {
                pid : process.pid,
                ver : ver
            }
        };

    }
    
    return GUD?.[uid]?.gud;
}

/**
 * 从db从获取玩家的gud数据
 * 业务逻辑中，请统一使用getGud方法，不然可能会造成数据一致性错误
 * @param uid 
 */
export async function getGudFromDB(uid:string) : Promise<ResLogin['gud']>{
    //console.warn("getGudFromDB", uid);
    let gud = await G.mongodb.collection('user').findOne({uid: uid});
    return gud;
}