package svc

import (
	"applet/app/md"
	"applet/app/utils"
	"applet/app/utils/cache"
	"errors"
	"fmt"
	"math/rand"
	"reflect"
	"time"
)

const redisMutexLockExpTime = 15

// TryGetDistributedLock 分布式锁获取
// requestId 用于标识请求客户端，可以是随机字符串，需确保唯一
func TryGetDistributedLock(lockKey, requestId string, isNegative bool) bool {
	if isNegative { // 多次尝试获取
		retry := 1
		for {
			ok, err := cache.Do("SET", lockKey, requestId, "EX", redisMutexLockExpTime, "NX")
			// 获取锁成功
			if err == nil && ok == "OK" {
				return true
			}
			// 尝试多次没获取成功
			if retry > 10 {
				return false
			}
			time.Sleep(time.Millisecond * time.Duration(rand.Intn(1000)))
			retry += 1
		}
	} else { // 只尝试一次
		ok, err := cache.Do("SET", lockKey, requestId, "EX", redisMutexLockExpTime, "NX")
		// 获取锁成功
		if err == nil && ok == "OK" {
			return true
		}

		return false
	}
}

// ReleaseDistributedLock 释放锁，通过比较requestId，用于确保客户端只释放自己的锁，使用lua脚本保证操作的原子型
func ReleaseDistributedLock(lockKey, requestId string) (bool, error) {
	luaScript := `
	if redis.call("get",KEYS[1]) == ARGV[1]
    then
		return redis.call("del",KEYS[1])
	else
		return 0
	end`

	do, err := cache.Do("eval", luaScript, 1, lockKey, requestId)
	fmt.Println(reflect.TypeOf(do))
	fmt.Println(do)

	if utils.AnyToInt64(do) == 1 {
		return true, err
	} else {
		return false, err
	}
}

func GetDistributedLockRequestId(prefix string) string {
	return prefix + utils.IntToStr(rand.Intn(100000000))
}

// HandleBalanceDistributedLock 处理余额更新时获取锁和释放锁 如果加锁成功，使用语句 ` defer cb() ` 释放锁
func HandleBalanceDistributedLock(masterId, uid, requestIdPrefix string) (cb func(), err error) {
	// 获取余额更新锁
	balanceLockKey := fmt.Sprintf(md.UserFinValidUpdateLock, masterId, uid)
	requestId := GetDistributedLockRequestId(requestIdPrefix)
	balanceLockOk := TryGetDistributedLock(balanceLockKey, requestId, true)
	if !balanceLockOk {
		return nil, errors.New("系统繁忙，请稍后再试")
	}

	cb = func() {
		_, _ = ReleaseDistributedLock(balanceLockKey, requestId)
	}

	return cb, nil
}

func HandleLimiterDistributedLock(masterId, ip, requestIdPrefix string) (cb func(), err error) {
	balanceLockKey := fmt.Sprintf(md.AppLimiterLock, masterId, ip)
	requestId := GetDistributedLockRequestId(requestIdPrefix)
	balanceLockOk := TryGetDistributedLock(balanceLockKey, requestId, true)
	if !balanceLockOk {
		return nil, errors.New("系统繁忙，请稍后再试")
	}

	cb = func() {
		_, _ = ReleaseDistributedLock(balanceLockKey, requestId)
	}

	return cb, nil
}
