const logger = require('../utils/log')

interface CDKInfo {
    itenList : string, // 物品列表
    batch : number,  // 当前批次CDK描述
    serverList: string // 服务器ID列表
    cnt : number
}

interface CDKData {
    batch : string, // json字符串
    code : string,  // json字符串
}

let CDKCache:Map<string,number> =  new Map()

const code:string[] = ["0","c","e","u","m",
    "k","d","7","x","f",
    "9","j","w","6","8",
    "t","1","h","4","p",
    "y","3","2","b","5","n"]

const codeMap:Map<string,number> = new Map([
    ['0',0],["c",1],["e", 2],["u", 3],["m",4],
    ["k", 5],["d",6],["7", 7],["x", 8],["f", 9],
    ["9", 10],["j",11],["w",12],["6",13],["8",14],
    ["t",15],["1",16],["h",17],["4",18],["p",19],
    ["y",20],["3",21],["2",22],["b",23],["5",24],["n",25]
])

const unit = 20
const bit = unit
const bit2 = unit * unit
const maxCDKRand = 25

const redis_batch_incr = "cdk_batch"
const redis_cdk_batch = "cdk_" // cdk批次信息
const redis_cdk = "hcdk_"  // 批次对应cdk hset

function transNumber(num:number):[number,number,number]{
    let first = 0
    let second = 0
    if (num >= bit2){
        first = Math.floor(num / bit2)
    }
    
    if (num >= bit){
        let t = num - (first * bit2)
        second = Math.floor(t / bit)
    }
    let third = num - (first * bit2) - (second * bit)
    return [first,second,third]
}

function calcNumber(first:number,second:number,third:number) {
    if (first === undefined || second === undefined || third === undefined) {
        return -1
    }
    if (first > unit || second > unit || third > unit) {
        return -1
    }
    return first * bit2 + second * bit + third 
}

function generateCDK(batch:number , idx:number) {
    let ret = ""
    for (let i = 0; i <= 1;i++) {
        let r = (Math.floor(maxCDKRand * Math.random()) % 20) + 2 // 保证数字在2 - 22之间错在首字母0
        ret = ret + code[r]
    }
    let [idx_f,idx_s,idx_t] = transNumber(idx)
    ret = ret  + code[idx_t] + code[idx_s] + code[idx_f]  // 321
    let [batch_f,batch_s,batch_t] = transNumber(batch)
    // 是否需要检测是否超标
    ret = ret  + code[batch_s] + code[batch_t] + code[batch_f] // 231
    for (let i = 0; i <= 1;i++) {
        let r = Math.floor(maxCDKRand * Math.random())
        ret = ret + code[r]
    }
    return ret
}

function decodeCDK(code:string):[boolean,number,number] {
    if (code.length != 10) {
        return [false,0,0]
    }
    let idx = calcNumber(codeMap.get(code[4]),codeMap.get(code[3]),codeMap.get(code[2]))
    if (idx < 0 ) {
        return [false,0,0]
    }
    let batch = calcNumber(codeMap.get(code[7]),codeMap.get(code[5]),codeMap.get(code[6]))
    if (batch < 0) {
        return [false,0,0]
    }
    return [true,batch,idx]
}

class CDK {
    async genCDK (ctx,cnt:number,useCnt:number,serverList:string,itemList:string) {
        const redisClient = ctx.redis.client;
        let batch = await redisClient.incr(redis_batch_incr)
        let batch_info_key = redis_cdk_batch +  batch
        let data : CDKInfo = {
            itenList:itemList,
            serverList:serverList,
            batch: batch,
            cnt:useCnt
        }
        // 将批次数据存入redis
        await redisClient.set(batch_info_key,JSON.stringify(data))

        const pipeline = redisClient.pipeline();
        const cdk_hset_key = redis_cdk + batch
        // 生成cdk代码
        for (let i = 1; i <= cnt;i++) {
            let cdk = generateCDK(batch,i)
            pipeline.hset(cdk_hset_key,cdk,1)
        }
        // 执行管道代码
        return await pipeline.exec()
    }

    async getCDK(ctx,batch:number){ 
        const redisClient = ctx.redis.client;
        let curBatch = await redisClient.get(redis_batch_incr)
        if (Number(curBatch) < batch) {
            return ""
        }
        let batch_info_key = redis_cdk_batch +  batch
        let batch_info = await redisClient.get(batch_info_key)
        const cdk_hset_key = redis_cdk + batch
        let ret = await redisClient.hgetall(cdk_hset_key)
        /*let cdkList = ret.reduce((acc:any,value,index) => {
            if (index % 2 == 1) {
                acc.push(acc,value)
            }
            return acc
        })*/
        let cdkdata : CDKData = {
            batch:batch_info,
            code: ret
        }
        return JSON.stringify(cdkdata)
    }

    async getCDKItemList(ctx,code:string) {
        logger.info(`收到cdk is :${code}`)
        const redisClient = ctx.redis.client
        let [ok,batch,index] = decodeCDK(code)
        if (!ok) {
            return ""
        }
        let batch_info_key = redis_cdk_batch +  batch
        let batch_info_ret = await redisClient.get(batch_info_key)
        if (batch_info_ret == undefined || batch_info_ret == null ) {
            return ""
        }
        return batch_info_ret
    }

    async useCDK(ctx,code:string) {
        logger.info(` checkCDK 收到cdk is :${code}`)
        let [ok,batch,index] = decodeCDK(code)
        if (!ok) {
            return "invalid code"
        }
        const redisClient = ctx.redis.client;
        let cdk_hset_key = redis_cdk + batch
        // 必须保持原子性，只能用eval
        let evalStr = `
        local cdk = redis.call('hget',KEYS[1],KEYS[2])
        if not cdk then 
            return -1
        end
        redis.call('hdel',KEYS[1],KEYS[2])
        return 0
        `
        let ret = await redisClient.eval(evalStr,2,cdk_hset_key,code)
        if (ret == -1) {
            return 'cdk not found'
        }
        return "success"
    }

    async checkCDK(ctx,code:string,serverId : number) {
        logger.info(` checkCDK 收到cdk is :${code}`)
        let [ok,batch,index] = decodeCDK(code)
        if (!ok) {
            return "invalid code"
        }
        let time =  CDKCache.get(code) 
        if (time != undefined && time > Date.now()) {
            return "invalid code"
        }
        const redisClient = ctx.redis.client;
        // 先判断是否满足batch
        let batch_info_key = redis_cdk_batch +  batch
        let batch_info_ret = await redisClient.get(batch_info_key)
        if (batch_info_ret == undefined || batch_info_ret == null ) {
            return "batch not found"
        }
        let batch_info = JSON.parse(batch_info_ret)  as CDKInfo

        let jsonret = JSON.parse(batch_info.serverList)
        let serverList  = jsonret as number[]
        let isServer = false
        for(let i = 0;i < serverList.length;i++) {
            if (serverList[i] == serverId || serverList[i] == 0) {
                isServer = true
                break
            }
        }
        if(!isServer) {
            return "serverId error"
        } 
        let cdk_hset_key = redis_cdk + batch
        // 必须保持原子性，只能用eval
        let evalStr = `
        local cdk = redis.call('hget',KEYS[1],KEYS[2])
        if not cdk then 
            return -1
        end
        return 0
        `
        let ret = await redisClient.eval(evalStr,2,cdk_hset_key,code)
        if (ret == -1) {
            return 'cdk not found'
        }
        CDKCache.set(code,Date.now() + 2000)
        return "success"
    }
}

module.exports = new CDK()