import { rankType, ResOpen } from '../../shared/protocols/rank/PtlOpen';
import { rankInfo } from '../../shared/protocols/type';
import { Queue } from '../../shared/public/queue';
import { RankClslCross } from './rank_clsl';
//import { RankHbzbJfsCross, RankHbzbJfsLocal, RankHbzbZbsCross } from './rank_hbzb_jfs';
import { RankPower } from './rank_power';
import { RankQjzzd } from './rank_qjzzd';
import { RankSlzd1, RankSlzd2, RankSlzd3, RankSlzd4, RankSlzd5, RankSlzd6 } from './rank_slzd';
import { RankTanXian } from './rank_tanxian';
import { RankTujian } from './rank_tujian';
import { RankWzryCross } from './rank_wzry';
import { RankXszm } from './rank_xszm';
import { RankZccg } from './rank_zccg';
import * as util from 'util'
import { RankKfjs_1 } from "./rank_kfjs";
import { clusterRunOnce } from '../../clusterUtils';
import { number } from 'mathjs';



export abstract class Rank {
    static list: Partial<{
        qjzzd: RankQjzzd;
        zhanli: RankPower;
        tanxian: RankTanXian;
        slzd1: RankSlzd1;
        slzd2: RankSlzd2;
        slzd3: RankSlzd3;
        slzd4: RankSlzd4;
        slzd5: RankSlzd5;
        slzd6: RankSlzd6;
        xszm: RankXszm;
        clslCross: RankClslCross;
        zccg: RankZccg;
        tujian: RankTujian;
        wzryCross: RankWzryCross;
    }> & k_v<Rank> = {};

    queue = new Queue();
    findKey = 'uid';
    countMaxNum = 50;
    utimeTTL = 60;

    abstract getType(): rankType;

    // 获取rank数据, 注：可能重写，具体根据type类型判断
    async getRankList(uid: string, { min, max }): Promise<{ rankList: rankInfo[]; myRank: rankInfo; }> {
        let conn = G?.server?.uid_connections[uid];
        let data = await this.getRankData(uid);
        let rankList = await this.getRankListRange(min, max);
        let rank = await this.getRankSortByOne(uid);
        let score = await this.getRankScore(uid)
        return {
            rankList: rankList,
            myRank: {
                rank,
                player: data?.player || conn?.gud || {},
                valArr: [score]
            }
        };
    }

    /**
     * 根据RankInfo数据，获取用于排名的积分值得
     * 注：可能重写，具体根据type类型判断
     * @param info
     * @returns 积分值
     */
    getValArr(info: rankInfo): number | string {
        return info?.valArr[0] || 0
    }

    // 注：可能重写，具体根据type类型判断
    compare(other: rankInfo, cur: rankInfo): boolean {
        return cur.valArr[0] > other.valArr[0];
    }

    // rankList排序，注：可能重写，具体根据type类型判断
    compareSort(a: rankInfo, b: rankInfo): number {
        return b.valArr[0] - a.valArr[0];
    }

    /**
     * 页面转换，根据page和offset转换出min和max
     * @param page 页面
     * @param offset 每页数量
     * @returns
     */
    static pageToMin(page: number, offset: number) {
        let res = {
            min: page * offset,
            max: page * offset + offset
        }
        return res
    }

    /**
     * 返回排行榜DB连接对象
     */
    get db() {
        return G.mongodb.collection('rankList');
    }

    /**
     * 返回排行榜类型
     */
    get type() {
        return this.getType();
    }

    constructor(param?: any) {
        // 将param属性赋值给this
        param && Object.assign(this, param);
        Rank.list[this.getType() as string] = this;

        // 初始化排行榜
        this.cotr();
    }

    /**
     * 根据type，获取redis Data的key
     */
    get getRedisKey() {
        return util.format('rank:%s:data', this.getType())
    }

    /**
     * 根据type，获取redis Sort的key
     */
    get getRedisKeySort() {
        return util.format('rank:%s:sort', this.getType())
    }

    // 初始化
    async cotr() {
        clusterRunOnce(async () => {
            // redis已存在则不初始化
            //if(await this.getRankLen() > 0) return

            //将db里的数据，写入到 rank:xxx:sort里
            //写入的单条数据为： {uid:score}

            // 首先清理redis中sort数据 在从数据库中初始化
            await G.ioredis.del(this.getRedisKeySort);

            this.db.find({ type: this.type }, {
                projection: {
                    "idKey": 1,
                    "type": 1,
                    "data.valArr": 1,
                    "utime": 1
                }
            }).toArray().then(listArr => {
                // 写入初始化数据
                listArr = listArr || [];
                listArr.forEach(item => {
                    if (item.idKey && item.data) this.setRankData(item.idKey, item.data)
                })
            });
        })

        this.getRankListRange();
    }

    /**
     * 更新玩家的积分
     * @param uid uid，在rank里通常保存于idKey这个字段
     * @param data 积分数据，主要是需要里面的data.valArr字段
     * @returns
     */
    async setRankData(uid: string, data: rankInfo) {
        let keySort = this.getRedisKeySort

        // let key = this.getRedisKey
        // G.redis.hSet(key, idKey, data)

        /**积分 */
        let score = await this.getValArr(data)
        //G.redis.zAdd(keySort, <sortEd>{value: idKey, score: valArr})
        await G.ioredis.zadd(keySort, score, uid)
        return
    }

    /**
     * 获取单个用户的数据
     * @param uid uid
     * @returns
     */
    async getRankData(uid: string) {
        let data: rankInfo;
        let res = await this.db.findOne({ "idKey": uid, "type": this.getType() })

        if (res?.data && res.data.utime + 60 > G.time) {
            data = (G.mongodb.conversionIdObj(res)).data;
        } else if (res?.data) {
            let player = await G.mongodb.collection("user").findOne({ uid: uid }, { projection: { _id: 0 } });

            data = Object.assign(res.data, { player: player });
            this.db.updateOne({ idKey: uid, type: this.getType() }, { $set: { "data.player": data.player, "data.utime": G.time } });
        }
        return data;

        // let key = this.getRedisKey
        // let data = await G.redis.hGet(key, idKey)
        // if(!data || data.utime < (G.time - this.utimeTTL)) {
        //     let type = this.getType()
        //     let res = await this.db.findOne({isKey: idKey, type})   //<--isKey？？意味着整个rank从来不会更新，res永远是空
        //     if(res) {
        //         data = G.mongodb.conversionIdObj(res)
        //         this.setRankData(idKey, data.data)
        //     }
        // }
        // if(data) return data
        // return undefined || {}
    }

    /**
     * 获取单个用户的排序分数
     * @param uid
     * @returns
     */
    async getRankScore(uid: string): Promise<number> {
        let score = await G.redis.zScore(this.getRedisKeySort, uid)
        return score || 0
    }

    /**
     * 从redis中获取单个用户的排名 *降序
     * @param uid
     * @returns
     */
    async getRankSortByOne(uid: string): Promise<number> {
        let rank = await G.redis.zRevRank(this.getRedisKeySort, uid)
        return rank === 0 || rank > 0 ? rank : -1;
    }

    // 从redis中获取单个指定排名的用户数据 *降序
    async getRankSortDataByOne(rank: number): Promise<rankInfo> {
        let rankList: rankInfo[] = await this.getRankListRange(rank, rank + 1)
        let rankInfo = rankList && rankList.length > 0 ? rankList[0] : {} as rankInfo
        return rankInfo
    }

    // 获取排名长度
    // async getRankLen() {
    //     return await G.redis.hLen(this.getRedisKey)
    // }

    /**
     * 获取指定排名范围的数据 *降序
     * @param min
     * @param max
     * @returns
     */
    async getRankListRange(min: number = 0, max: number = 50): Promise<rankInfo[]> {
        let uids = await this.getRankListIdKeyRange(min, max)
        if (uids && uids.length > 0) {
            let res = await this.db.find({ idKey: { $in: uids }, type: this.getType() }).toArray()

            if (this.type.indexOf("slzd") != -1) {
                let ghid = [];
                res = res.map(item => {
                    if (!item.data?.player?.ghid || item.data.utime + 60 < G.time) {
                        ghid.push(G.mongodb.conversionId(item.idKey));
                    }
                    return item;
                })
                if (ghid.length > 0) {
                    let ghinfo = await G.mongodb.collection("gonghui").find(
                        { _id: { $in: ghid } }, { projection: { name: 1 } }
                    ).toArray();
                    ghinfo.forEach(item => {
                        let index = res.findIndex(x => x.idKey == item._id.toHexString());
                        res[index].data.player = {
                            ghName: item.name,
                            ghId: item._id.toHexString(),
                        };
                        this.db.updateOne({ idKey: item._id.toHexString(), type: this.getType() }, { $set: { "data.player": res[index].data.player } });
                    })
                }
            } else if (this.type.indexOf('clslCross') != -1) {
               // 丛林猎手 跨服排行榜 不需要更新
            } else {
                let updateUids = [];
                res = res.map(item => {
                    // 没有player 或者 player 过期
                    if (!item.data?.player || item.data.utime + 60 < G.time) {
                        updateUids.push(item.idKey);
                    }
                    return item;
                });

                let newUserArr = await G.mongodb.collection('user').find(
                    { uid: { $in: updateUids } }, { projection: { _id: 0 } }
                ).toArray();

                newUserArr.forEach(item => {
                    // 每次遍历查找？
                    let index = res.findIndex(x => x.idKey == item.uid);

                    res[index].data.player = item;
                    this.db.updateOne({ idKey: item.uid, type: this.getType() }, { $set: { "data.player": item } });

                    // 跟新redis score
                    // this.setRankData(item.uid, res[index].data as any);
                })
            }

            // 按照redis uids 排序顺序排序
            return res.sort((a, b) => uids.indexOf(a.idKey) - uids.indexOf(b.idKey)).map(ele => ele.data);
        }
        return []
    }

    /**
     * 从redis中获取指定排名范围的uid集合 *降序
     * @param min
     * @param max
     * @returns uid集合数组
     */
    async getRankListIdKeyRange(min: number = 0, max: number = 50): Promise<string[]> {
        let uids = await G.redis.zRevRange(this.getRedisKeySort, min, max - 1)
        return uids || []
    }

    /**
     * 获取指定类型的全部rank列表,返回为积分排序后的数组
     * @returns
     */
    async getRankListAll(): Promise<rankInfo[]> {
        // let res = await G.redis.hGetAll(this.getRedisKey)
        let res = (await this.db.find({ type: this.getType() }).toArray()).map(ele => ele.data);
        if (res) {
            // 如果是用户数据，则比对utime，更新数据
            // res = await this.checkData(res)
            return res.sort(this.compareSort)
        }
        return []
    }

    /**
     * 按排名获取全部的idKey
     * @returns
     */
    async getRankListIdKeyAll(): Promise<string[]> {
        let res = this.getRankListIdKeyRange(0, -1)
        return res
    }

    // 验证数据的过期时间，更新数据
    // async checkData(rankList: rankInfo[]): Promise<rankInfo[]> {
    //     let updateUid = []
    //     rankList.forEach((value,key) => {
    //         // 仅针对用户表，公会表暂不考虑
    //         if(rankList[key].player?.uid && (rankList[key].utime || 0) < (G.time - this.utimeTTL)) {
    //             // 更新数据
    //             updateUid.push(rankList[key].player.uid)
    //         }
    //     })
    //     if(updateUid.length > 0) {
    //         let newUserArr = await G.mongodb.collection('user').find({uid:{$in: updateUid}}).toArray()
    //         // let newUserArr = await G.redis.gets('user', ...updateUid.map(uid => [uid] as [string]))
    //         // let newUserArr = await G.mongodb.collection('user').find({uid:{$in: updateUid}}).toArray()
    //         newUserArr.forEach(item => {
    //             let index = rankList.findIndex( x => x.player.uid == item.uid);
    //             rankList[index].player = item;
    //             this.setRankData(item.uid, rankList[index]);
    //         })
    //     }
    //     return rankList
    // }


    /**
     * 删除指定idk的排名数据
     * @param idKey
     * @returns
     */
    async delRankData(idKey: string) {
        // G.redis.hDel(this.getRedisKey, idKey)
        G.redis.zRem(this.getRedisKeySort, idKey);
        this.db.updateOne({ idKey: idKey, type: this.getType() }, {
            $set: { idKey: `del_${idKey}` }
        });
    }

    /**
     * 原逻辑前50(countMaxNum)名才更新数据（上榜），多余的数据会删除。
     * @param info
     * @returns
     */
    async addNew(info: rankInfo) {
        //this.queue.enqueue(async () => {
        await this.setRankData(info.player[this.findKey], info)
        await this.db.updateOne({ type: this.type, idKey: info.player[this.findKey] }, { $set: { data: info } }, { upsert: true });
        // 删除第50名以后的数据，（排名从0开始计算）
        // let idKeys:string[] = await this.getRankListIdKeyRange(50, -1)
        // idKeys.forEach(idKey => {
        //     this.db.deleteOne({ type: this.type, idKey: idKey });
        //     this.delRankData(idKey)
        // })
        //});
    }


    // 清空相关rank数据
    async clear() {
        // this.queue.enqueue(async () => {
        // G.redis.rawDel(this.getRedisKey)
        G.redis.rawDel(this.getRedisKeySort);
        this.db.deleteMany({ type: this.type });
        // });
    }
}