import {
    Collection,
    CreateIndexesOptions,
    Db,
    FindCursor,
    IndexDescription,
    IndexSpecification,
    MongoClient,
    MongoClientOptions,
    ObjectId,
    OptionalId
} from 'mongodb';
import {CollectionActionLog, actionLogType} from './module/collection_actionLog';
import {CollectionEvent, eventType} from './module/collection_event';
import {CollectionPlayerInfo, playerInfoType} from './module/collection_palyerInfo';
import {CollectionRecord, recordType} from './module/collection_record';
import {MongodbCollections} from './module/mongodb';
import {HuoDongFun} from './public/huodongfun';
import {zbsGroup} from './api_s2c/hbzb/zbs/fun';
import {clusterRunOnce} from './clusterUtils';
import { PublicShared } from './shared/public/public';
import { addGameLog } from './gameLog';

const indexs: Partial<{ [k in keyof MongodbCollections]: IndexDescription[] }> = {
    item: [
        {
            key: {uid: 1, itemId: 1}, unique: true
        },
        {
            key: {uid: 1}
        }
    ],
    user: [
        {
            key: {bindUid: 1, sid: 1}, unique: true,
        },
        {
            key: {name: 1}, unique: true,
        },
        {
            key: {uid: 1}, unique: true,
        },
        {
            key: {mapId: 1},
        }
    ],
    hero: [
        {
            key: {uid: 1, _id: 1}, unique: true
        },
        {
            key: {uid: 1}
        }
    ],
    equip: [
        {
            key: {uid: 1, _id: 1}, unique: true
        },
        {
            key: {uid: 1}
        }
    ],
    shiwu: [
        {
            key: {uid: 1, _id: 1}, unique: true
        },
        {
            key: {uid: 1}
        }
    ],
    email: [
        {
            key: {uid: 1, _id: 1}, unique: true
        },
        {
            key: {ttl: 1}, expireAfterSeconds: 15 * 24 * 3600
        },
        {
            key: {uid: 1}
        }
    ],
    xstask: [
        {
            key: {uid: 1, _id: 1}, unique: true
        },
        {
            key: {uid: 1}
        }
    ],
    playerInfo: [
        {
            key: {uid: 1, type: 1}, unique: true
        },
        {
            key: {uid: 1}
        },
        {
            key: {type: 1}
        }
    ],
    pata: [
        {
            key: {uid: 1, sid: 1}, unique: true
        },
        {
            key: {uid: 1}
        }
    ],
    gbtx: [
        {
            key: {uid: 1}, unique: true
        }
    ],
    tanxian: [
        {
            key: {uid: 1}, unique: true
        }
    ],
    cardlog: [
        {
            key: {uid: 1, cardnum: 1}, unique: true
        }
    ],
    dxlt: [
        {
            key: {uid: 1}, unique: true
        }
    ],
    jjc: [
        {
            key: {uid: 1}, unique: true
        }
    ],
    playattr: [
        {
            key: {uid: 1, ctype: 1}, unique: true
        }
    ],
    gonghui: [
        {
            key: {name: 1}, unique: true
        }
    ],
    gonghuiUser: [
        {
            key: {uid: 1}, unique: true
        }
    ],
    gonghuiFb: [
        {
            key: {ghId: 1, fbId: 1}, unique: true
        }
    ],
    task: [
        {
            key: {uid: 1, taskid: 1}, unique: true
        },
        {
            key: {uid: 1}
        },
        {
            key: {stype: 1}
        },
        {
            key: {pval: 1}
        },
        {
            key: {nval: 1}
        }
    ],
    actionLog: [
        {
            key: {uid: 1, type: 1}, unique: true
        }
    ],
    syncBtns: [
        {
            key: {uid: 1}, unique: true
        }
    ],
    payLog: [
        {
            key: {uid: 1}, unique: true
        }
    ],
    event: [
        {
            key: {uid: 1, type: 1}, unique: true
        },
        {
            key: {uid: 1}
        }
    ],
    friend: [
        {
            key: {uid: 1}, unique: true
        }
    ],
    chat: [
        {
            key: {type: 1}, unique: true
        }
    ],
    chatLog: [
        {
            key: {uid: 1}
        },
        {
            key: {type: 1}
        }
    ],
    slzdGh: [
        {
            key: {ghid: 1}, unique: true
        }
    ],
    slzdUser: [
        {
            key: {uid: 1}, unique: true
        }
    ],
    ganhai: [
        {
            key: {uid: 1}, unique: true
        }
    ],
    dayPay: [
        {
            key: {uid: 1, time: 1}, unique: true
        },
        {
            key: {uid: 1}
        }
    ],
    rankList: [
        {
            key: {type: 1, idKey: 1}, unique: true
        }
    ],
    wanted: [
        {
            key: {uid: 1}, unique: true
        }
    ],
    peijian: [
        {
            key: {uid: 1, _id: 1}, unique: true
        }
    ],
    mingdao: [
        {
            key: {uid: 1}, unique: true
        }
    ],
    wjjl: [
        {
            key: {uid: 1}, unique: true
        }
    ],
    zhanling: [
        {
            key: {uid: 1, type: 1}, unique: true
        },
        {
            key: {uid: 1}
        }
    ],
    hdinfo: [
        {
            key: {hdid: 1}, unique: true
        },
        {
            key: {htype: 1}
        },
        {
            key: {ttype: 1}
        },
        {
            key: {stime: 1}
        },
        {
            key: {etime: 1}
        }
    ],
    giftLog: [
        {
            key: {popup_id: 1}, unique: true
        },
        {
            key: {game_user_id: 1}
        }
    ],
    loginLog: [
        {
            key: {uid: 1, _id: 1}, unique: true
        },
        {
            key: {uid: 1}
        }
    ],
    payLogNew: [
        {
            key: {uid: 1, key: 1}
        },
        {
            key: {uid: 1}
        }
    ],
    kuangdong: [
        {
            key: {uid: 1}
        }
    ],
    apiCount: [
        {
            key: {api: 1}, unique: true
        }
    ],
    weixiuchang: [
        {
            key: {uid: 1}, unique: true
        }
    ],
    player_behavior: [
        {
            key: {uid: 1, type: 1}, unique: true
        },
        {
            key: {uid: 1}
        }
    ],
    rmbuse: [
        {
            key: {uid: 1}
        }
    ],
    fightLog: [
        {
            key: {uid: 1, type: 1}
        },
        {
            key: {uid: 1}
        },
        {
            key: {ttl: 1}, expireAfterSeconds: 7 * 24 * 3600
        }
    ],
    shop: [
        {
            key: {uid: 1, shopId: 1}, unique: true
        },
        {
            key: {uid: 1}
        }
    ]
};

const crossIndexs: Partial<{ [k in keyof MongodbCollections]: IndexDescription[] }> = {
    email: [
        {
            key: {sid: 1}
        },
        {
            key: {uid: 1}
        }
    ],
    fightLog: [
        {
            key: {uid: 1, type: 1}
        },
        {
            key: {uid: 1}
        },
        {
            key: {ttl: 1}, expireAfterSeconds: 7 * 24 * 3600
        }
    ],
    hbzb_user_cross: [
        {
            key: {uid: 1}, unique: true
        },
        {
            key: {jifen: 1}
        },
        {
            key: {rank: 1}
        },
        {
            key: {zbsgroup: 1}
        }
    ],
    hbzb_user_zbs: [
        {
            key: {uid: 1}, unique: true
        }
    ],
    kbzzApplyUser: [
        {
            key: {uid: 1}, unique: true
        }
    ],
    kbzzGroupUser: [
        {
            key: {uid: 1}, unique: true
        }
    ],
    clslCrossUser: [
        {
            key: { uid: 1 },
        },
        {
            key: { ttltime: 1 }, expireAfterSeconds: 7 * 24 * 3600
        }
    ],
    clslCrossGroup: [
        {
            key: { week: 1 },
        }
    ],
    rankList: [
        {
            key: {type: 1, idKey: 1}, unique: true
        },
        {
            key: {valArr: -1}
        },
    ],
    wzry_fight: [
        {
            key: {jifen: 1}
        },
        {
            key: {uid: 1}
        },
        {
            key: {zkey: 1}
        },
    ],
    wzry_user_cross: [
        {
            key: {jifen: 1}
        },
        {
            key: {uid: 1}
        },
        {
            key: {zkey: 1}
        },
    ],
    huodong_user: [
        {
            key: {uid: 1}, unique: true,
        }
    ],
};

export async function initMongoDB() {
    //mongodb连接数说明：https://blog.csdn.net/for_cxc/article/details/116859714
    //可结合查看：node_modules\mongodb\lib\connection_string.js
    //maxPoolSize默认值：100

    /**
     * MongoDB连接池具有多个参数，用于控制连接池的行为和性能。下面是一些常用的连接池参数及其作用：
     * maxPoolSize：连接池的最大连接数。默认值为100。该参数决定了连接池中可以同时存在的最大连接数，超过这个数量后，新的连接请求会被阻塞。
     * minPoolSize：连接池的最小连接数。默认值为0。该参数决定了连接池中保持的最小连接数，当连接数少于这个数量时，连接池会自动创建新的连接实例。
     * maxIdleTimeMS：连接的最大空闲时间（毫秒）。默认值为0，表示连接不会被强制断开。该参数决定了连接在空闲一定时间后是否被断开，以释放服务器资源。
     * waitQueueTimeoutMS：连接请求的等待时间（毫秒）。默认值为0，表示无限等待。当连接池达到最大连接数时，新的连接请求会被放入等待队列中，该参数决定了连接请求在队列中的最长等待时间。
     */

    let option:MongoClientOptions = {
        //空闲5分钟后关闭
        maxIdleTimeMS: 5*60*1000
    };
    //跨服只有1个，直接采用默认配置就行
    if(G.argv.serverType != 'cross'){
        if(PublicShared.getOpenServerDay() > 3){
            option.maxPoolSize=10;
        }
    }

    console.log('connect mongodb ......');
    let client = await MongoClient.connect(G.argv.serverType == 'cross' ? G.config.crossMongodbUrl : G.config.mongodbUrl, option);
    G.mongodb = new _mongodb(client.db(G.config.dbName || ''));
    console.log('connect mongodb succ');


    if (G.argv.serverType != 'cross') {
        console.log('connect crossmongodb ......');
        //本服里，维持住跟跨服数据库的链接
        let crossClient = await MongoClient.connect(G.config.crossMongodbUrl,{
            maxPoolSize:10,
            maxIdleTimeMS: 5*60*1000
        });
        G.crossmongodb = new _mongodb(crossClient.db(G.config.corssDBName || ""));
        console.log('connect crossmongodb succ');
    }

    clusterRunOnce(() => {
        //只在集群的一个进程里进行即可
        setDbIndexes();
    });
}

/**根据配置，设置mongodb的索引 */
export async function setDbIndexes() {
    let _indexs = G.argv.serverType == 'cross' ? crossIndexs : indexs;
    for (let coll in _indexs) {
        for (let i = 0; i < _indexs[coll].length; i++) {
            let index = _indexs[coll][i];

            let option = {} as CreateIndexesOptions;
            if (index.unique != null) {
                option.unique = index.unique;
            }
            if (index.expireAfterSeconds != null) {
                option.expireAfterSeconds = index.expireAfterSeconds;
            }

            try {
                await G.mongodb.collection(coll as keyof MongodbCollections).createIndex(
                    index.key,
                    option
                );
                // console.log('创建索引成功', coll, index);
            } catch (error) {
                if (error.codeName == 'IndexKeySpecsConflict') {
                    //当相同索引存在，但是配置(如：unique expireAfterSeconds)不同时，会收到这个报错
                    //此时会把原来的索引删除后重建
                    console.log('创建索引失败，和已有的索引冲突，尝试删除后重建');
                    await G.mongodb.collection(coll as keyof MongodbCollections).dropIndex(index.key);

                    //再试一次，不行只能抛出错误了
                    try {
                        await G.mongodb.collection(coll as keyof MongodbCollections).createIndex(
                            index.key,
                            option
                        );
                        // console.log('创建索引成功', coll, index);
                    } catch (error) {
                        console.log('创建索引失败==>', coll, index, error.message);
                    }

                } else {
                    console.log('创建索引失败=>', coll, index, error.message);
                }
            }
        }
    }
}

export class _mongodb {
    constructor(private db: Db) {

    }

    /**
     * mongodb仿sql查询
     * @param field 需要获取的字段，空数组表示所有字段，如：["uid","lv"]
     * @param from 从哪个表查询
     * @param where 查询条件，如：{"uid":"ciniao"}
     * @param order 排序条件，如：{"field":1}
     * @param limit 指定行数：10
     * @returns
     */
    fmtOption(field?: string[], order?: any, limit?: number) {
        let option = {};

        if (field?.length > 0) {
            option['projection'] = {};
            for (let i = 0; i < field.length; i++) {
                option['projection'][field[i]] = 1;
            }
        }

        if (order != null) {
            option['sort'] = order;
        }

        if (limit != null) {
            option['limit'] = limit;
        }
        return option;
    }

    async find(from: string, where: any, field?: string[], order?: any, limit?: number) {
        let option = this.fmtOption(field, order, limit);
        return this.db.collection(from).find(where, option).toArray();
    }

    async findOne(from: string, where: any, field?: string[], order?: any, limit?: number) {
        let option = this.fmtOption(field, order, limit);
        return this.db.collection(from).findOne(where, option);
    }

    collection<T extends keyof MongodbCollections>(collection: T): Collection<OptionalId<MongodbCollections[T]>>;
    collection<T extends keyof playerInfoType>(collection: 'playerInfo', type: T): Collection<OptionalId<CollectionPlayerInfo<T>>>;
    collection(collection: string) {
        return this.db.collection(collection);
    }

    cActionLog<T extends keyof actionLogType>(type: T): Collection<OptionalId<CollectionActionLog<T>>> {
        return this.db.collection('actionLog');
    }

    cEvent<T extends keyof eventType>(type: T): Collection<OptionalId<CollectionEvent<T>>> {
        return this.db.collection('event');
    }

    cPlayerInfo<T extends keyof playerInfoType>(type: T): Collection<OptionalId<CollectionPlayerInfo<T>>> {
        return this.db.collection('playerInfo');
    }

    cRecord<T extends keyof recordType>(type: T): Collection<OptionalId<CollectionRecord<T>>> {
        return this.db.collection('record');
    }

    /**_id转化 */
    conversionId(_id: string): ObjectId;
    conversionId(_id: ObjectId): string;
    conversionId(_id: string | ObjectId) {
        if (!_id) return null;
        if (_id instanceof ObjectId) return _id.toHexString();
        else return new ObjectId(_id);
    }

    /**_id数据转化 */
    conversionIdObj<T extends { _id: string; }>(obj: T): Omit<T, '_id'> & { _id: ObjectId; };
    conversionIdObj<T extends { _id: ObjectId; }>(obj: T): Omit<T, '_id'> & { _id: string; };
    conversionIdObj(obj: { _id: string; } | { _id: ObjectId; }) {
        if (!obj) return null;
        let {_id, ...ops} = obj;
        if (!_id) return ops;
        return {
            _id: this.conversionId(_id as any),
            ...ops
        } as any;
    }

    createTreeObj(...args: { key: string, val: any; }[]);
    createTreeObj(...args: { key: string, k: string, val: any; }[]);
    createTreeObj(...args: { key: string, k: string, val: any; }[]) {
        let obj = {};
        args.forEach(v => {
            if (Object.keys(v).length == 2) {
                obj[v.key] = v.val;
            } else {
                obj[`${v.key}.${v.k}`] = v.val;
            }
        });
        return obj;
    }

    // 增加查询索引（以达到检查数据库是否可用的目的）
    indexInformation(name) {
        return this.db.indexInformation(name)
    }
}

// //@ts-ignore
// FindCursor.prototype._toArray = FindCursor.prototype.toArray;
// //@ts-ignore
// FindCursor.prototype.toArray = async function(){
//     let rss = await this._toArray();
//
//     //如果返回的数据超过50条
//     if(rss.length>50){
//         addGameLog("system","findCount",null,{
//             collection: this.namespace.collection,
//             db:this.namespace.db,
//             count:rss.length
//         })
//         console.log("system,findCount",{
//             collection: this.namespace.collection,
//             db:this.namespace.db,
//             count:rss.length
//         })
//         console.trace();
//     }
//     return rss;
// }