import { localrun } from "pinus/lib/master/starter";
import { setLock, releaseLock, getLock } from './redlockCacheService';
var Redlock = require('redlock');
var _redlockCache;

export function initRedlock (redisClient) {
    _redlockCache = new RedlockService(redisClient);
}

export class RedlockService {
    _redisClient: any;
    ttl: number;
    redlock: any;
    redlockNoRetry: any; // 用于检查锁是否存在，一锁上立刻会解锁

    constructor(redisClient) {
        this._redisClient = redisClient;
        this._redisClient.on("error", function (err) { 
            console.error("Redis:Error:" + err); 
        }); 
        this.ttl = 1000;
        this.redlock = new Redlock(
            [this._redisClient],
            {
                // the expected clock drift; for more details
                // see http://redis.io/topics/distlock
                driftFactor: 0.01, // time in ms
        
                // the max number of times Redlock will attempt
                // to lock a resource before erroring
                retryCount:  10,
        
                // the time in ms between attempts
                retryDelay:  200, // time in ms
        
                // the max time in ms randomly added to retries
                // to improve performance under high contention
                // see https://www.awsarchitectureblog.com/2015/03/backoff.html
                retryJitter:  200 // time in ms
            }
        );
    
        this.redlock.on('clientError', function(err) {
            console.error('A redis error has occurred:', err);
        });

        this.redlockNoRetry = new Redlock(
            [this._redisClient],
            {
                driftFactor: 0.01, // time in ms
                retryCount:  0,
                retryDelay:  200, // time in ms
                retryJitter:  200 // time in ms
            }
        );
    
        this.redlockNoRetry.on('clientError', function(err) {
            console.error('A redis error has occurred:', err);
        });
    }
    
}

export async function lockData(serverId: number, dataName: string, id: string ) {
    let key = 'serverId_'+serverId+'_'+dataName+'_'+id;
    let lockKey = 'locks:' + key;
    console.log(' lockKey = '+ lockKey);
    return await lock(lockKey);
}

export async function lockLeagueData(dataName: string, leagueCode: string) {
    let key = dataName+'_'+ leagueCode;
    let lockKey = 'locks:' + key;
    console.log(' lockKey = '+ lockKey);
    return await lock(lockKey);
}

export async function lockDataWithNoParam(dataName: string) {
    let key = `${dataName}`;
    return await lock(key);
}

async function lock(lockKey: string) {
    return new Promise((resolve) => {
        _redlockCache.redlock.lock(lockKey, _redlockCache.ttl, function(err, lock) {
            if(err) {
                return resolve({ err })
            }
            setLock(lockKey, lock);
            return resolve({err: null, releaseCallback: releaseCallback.bind(null, lockKey)});
        });
    })
}

function releaseCallback(lockKey: string) {
    releaseLock(lockKey);
}

/**
 * 主要用于检查是否锁定，锁上后立刻会解锁，所以entryCount为0
 * 
 * @param serverId 区服
 * @param dataName 锁名
 * @param id 锁id
 */
export async function lockDataNoRetry(serverId: number, dataName: string, id: string ) {
    let key = 'serverId_'+serverId+'_'+dataName+'_'+id;
    let lockKey = 'locks:' + key;
    console.log(' lockKey = '+ lockKey);
    return await lockNoRetry(lockKey);
}

function lockNoRetry(lockKey: string) {
    return _redlockCache.redlockNoRetry.lock(lockKey, _redlockCache.ttl).then(function(lock) {
        setLock(lockKey, lock);
        return {err: null, releaseCallback: releaseCallback.bind(null, lockKey)};
    }).catch(function(err) {
        console.error(err);
        return { err };
    });
}