// Package score ç®€å�•çš„ç§¯åˆ†ç³»ç»Ÿ
package score

import (
	"math"
	"math/rand"
	"os"
	"strconv"
	"sync"
	"time"

	coins "github.com/MoYoez/Lucy-QOnebot/box/coins"
	"github.com/bytedance/sonic"

	"github.com/FloatTech/floatbox/web"
	"github.com/FloatTech/zbputils/control"
	"github.com/tidwall/gjson"

	fcext "github.com/FloatTech/floatbox/ctxext"
	"github.com/FloatTech/zbputils/ctxext"
	zero "github.com/wdvxdr1123/ZeroBot"
	"github.com/wdvxdr1123/ZeroBot/extension/rate"
	"github.com/wdvxdr1123/ZeroBot/message"
)

type PartyGameOutsider map[string]partygame

type partygame struct {
	Name  string `json:"name"`
	Desc  string `json:"desc"`
	Coins string `json:"coins"`
}

var (
	RobTimeManager      = rate.NewManager[int64](time.Minute*70, 163)
	checkLimit          = rate.NewManager[int64](time.Minute*1, 5) // time setup
	catchLimit          = rate.NewManager[int64](time.Hour*1, 9)   // time setup
	processLimit        = rate.NewManager[int64](time.Hour*1, 5)   // time setup
	payLimit            = rate.NewManager[int64](time.Hour*1, 10)  // time setup
	wagerData           map[string]int
	newPartyGame        PartyGameOutsider
	partyGameRollLength int
)

func init() {
	wagerData = make(map[string]int)
	wagerData["data"] = rand.Intn(2000)
	sdb := coins.Initialize("./data/score/score.db")
	loadFiles := fcext.DoOnceOnSuccess(func(ctx *zero.Ctx) bool {
		data, err := os.ReadFile(engine.DataFolder() + "loads.json")
		if err != nil {
			ctx.SendChain(message.Text("ERROR:", err))
			return false
		}
		err = sonic.Unmarshal(data, &newPartyGame)
		if err != nil {
			panic(err)
		}
		partyGameRollLength = len(newPartyGame)
		return true
	})

	// å€Ÿé‰´äº†å…¶ä»–botçš„åŠŸèƒ½ ç¼–å†™è€Œæˆ�
	engine.OnFullMatch("æŸ æª¬ç‰‡æ€»æ•°", zero.OnlyGroup).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
		uid := ctx.Event.UserID
		si := coins.GetSignInByUID(sdb, uid)
		ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("æ‚¨çš„æŸ æª¬ç‰‡æ•°é‡�ä¸€å…±æ˜¯: ", si.Coins))
	})
	engine.OnRegex(`^æŸ¥è¯¢(\[CQ:at,qq=(\d+)\]\s?|(\d+))çš„æŸ æª¬ç‰‡`, zero.OnlyGroup).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
		TargetInt, _ := strconv.ParseInt(ctx.State["regex_matched"].([]string)[2], 10, 64)
		siTargetUser := coins.GetSignInByUID(sdb, TargetInt)
		getTargetName := ctx.CardOrNickName(TargetInt)
		ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("è¿™ä½� ( ", getTargetName, " ) çš„æŸ æª¬ç‰‡ä¸º", siTargetUser.Coins, "ä¸ª"))
	})
	engine.OnFullMatch("æŠ½å¥–", loadFiles, zero.OnlyGroup).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
		if !checkLimit.Load(ctx.Event.UserID).Acquire() {
			ctx.SendChain(message.Text("å¤ªè´ªå¿ƒäº†å“¦~è¿‡ä¼šè¯•è¯•å�§"))
			return
		}
		getProtectStatus := CheckUserIsEnabledProtectMode(ctx.Event.UserID, sdb)
		if getProtectStatus {
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("å·²ç»�å�¯åŠ¨ä¿�æŠ¤æ¨¡å¼�ï¼Œä¸�å…�è®¸å�‚ä¸�ä»»ä½•æŠ½å¥–æ€§è´¨ç±»äº’åŠ¨"))
			return
		}
		var mutex sync.RWMutex // æ·»åŠ è¯»å†™é”�ä»¥ä¿�è¯�ç¨³å®šæ€§
		mutex.Lock()
		uid := ctx.Event.UserID
		si := coins.GetSignInByUID(sdb, uid) // è�·å�–ç”¨æˆ·ç›®å‰�çŠ¶å†µä¿¡æ�¯
		userCurrentCoins := si.Coins         // loading coins status
		if userCurrentCoins < 0 {
			_ = coins.InsertUserCoins(sdb, uid, 0)
			ctx.SendChain(message.Reply(uid), message.Text("æœ¬æ¬¡å�‚ä¸�çš„æŸ æª¬ç‰‡ä¸�å¤Ÿå“¦~è¯·å¤šå¤šæ‰“å�¡w"))
			return
		} // fix unexpected bug during the code error
		checkEnoughCoins := coins.CheckUserCoins(userCurrentCoins)
		if !checkEnoughCoins {
			ctx.SendChain(message.Reply(uid), message.Text("æœ¬æ¬¡å�‚ä¸�çš„æŸ æª¬ç‰‡ä¸�å¤Ÿå“¦~è¯·å¤šå¤šæ‰“å�¡w"))
			return
		}
		referpg := newPartyGame[strconv.Itoa(rand.Intn(partyGameRollLength))]
		getName := referpg.Name
		getCoinsStr := referpg.Coins
		getDesc := referpg.Desc
		getCoinsInt, _ := strconv.Atoi(getCoinsStr)
		addNewCoins := si.Coins + getCoinsInt - 60
		coins.InsertUserCoins(sdb, uid, addNewCoins)
		msgid := ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("å—¯å“¼~æ�¥ç�©æŠ½å¥–äº†å“¦w çœ‹çœ‹èƒ½æŠ½åˆ°ä»€ä¹ˆå‘¢w"))
		ctx.SendChain(message.Reply(msgid), message.Text("å‘¼å‘¼~è®©å’±çœ‹çœ‹ä½ æŠ½åˆ°äº†ä»€ä¹ˆä¸œè¥¿ww\n"), message.Text("ä½ æŠ½åˆ°çš„æ˜¯~ ", getName, "\n", "è�·å¾—äº†æŸ æª¬ç‰‡ ", getCoinsInt, "\n", getDesc, "\nç›®å‰�çš„æŸ æª¬ç‰‡æ€»æ•°ä¸ºï¼š", addNewCoins))
		defer mutex.Unlock()
	})
	// ä¸€æ¬¡æœ€å¤šéª— 400 æŸ æª¬ç‰‡,å¤±è´¥æ¦‚ç�‡è¾ƒå¤§,å¤±è´¥ä¼šè¢«å��å��æŸ æª¬ç‰‡
	engine.OnRegex(`^æŠ¢(\[CQ:at,qq=(\d+)\]\s?|(\d+))çš„æŸ æª¬ç‰‡`, zero.OnlyGroup).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
		if !catchLimit.Load(ctx.Event.UserID).Acquire() {
			ctx.SendChain(message.Text("å¤ªè´ªå¿ƒäº†å“¦~ä¸€å°�æ—¶å��å†�æ�¥è¯•è¯•å�§"))
			return
		}
		getProtectStatus := CheckUserIsEnabledProtectMode(ctx.Event.UserID, sdb)
		if getProtectStatus {
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("å·²ç»�å�¯åŠ¨ä¿�æŠ¤æ¨¡å¼�ï¼Œä¸�å…�è®¸å�‚ä¸�ä»»ä½•æŠ½å¥–æ€§è´¨ç±»äº’åŠ¨"))
			return
		}
		TargetInt, _ := strconv.ParseInt(ctx.State["regex_matched"].([]string)[2], 10, 64)
		if TargetInt == ctx.Event.UserID {
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("å“ˆ? å¹²å˜›éª—è‡ªå·±çš„?å��è›‹å“¦"))
			return
		}
		getProtectTargetStatus := CheckUserIsEnabledProtectMode(TargetInt, sdb)
		if getProtectTargetStatus {
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("å¯¹æ–¹å·²ç»�å�¯åŠ¨ä¿�æŠ¤æ¨¡å¼�ï¼Œä¸�å…�è®¸æ­¤å¤„ç�†æ“�ä½œ"))
			return
		}
		uid := ctx.Event.UserID
		siEventUser := coins.GetSignInByUID(sdb, uid)        // è�·å�–ä¸»ç”¨æˆ·ç›®å‰�çŠ¶å†µä¿¡æ�¯
		siTargetUser := coins.GetSignInByUID(sdb, TargetInt) // è�·å¾—è¢«æŠ¢ç”¨æˆ·ç›®å‰�æƒ…å†µä¿¡æ�¯
		switch {
		case siEventUser.Coins < 400:
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("è²Œä¼¼æ²¡æœ‰è¶³å¤Ÿçš„æŸ æª¬ç‰‡å�»å‡†å¤‡å“¦~è¯·å¤šå¤šæ‰“å�¡w"))
			return
		case siTargetUser.Coins < 400:
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("å¤ªå��äº†~è¯•å›¾çš„å¯¹è±¡è²Œä¼¼æ²¡æœ‰è¶³å¤Ÿå¤šçš„æŸ æª¬ç‰‡~"))
			return
		}
		eventUserName := ctx.CardOrNickName(uid)
		eventTargetName := ctx.CardOrNickName(TargetInt)
		// token chance.
		// add more possibility to get the chance (0-200)
		getTicket := RobOrCatchLimitManager(ctx) // full is 1 , least 3. level 1,2,3,
		// however, the total is still 0-400.
		fullChanceToken := rand.Intn(10)
		var modifyCoins int
		if fullChanceToken > 7 { // use it to reduce the chance to lower coins.
			modifyCoins = rand.Intn(200) + 200
		} else {
			modifyCoins = rand.Intn(200)
		}
		getRandomNum := rand.Intn(10)
		PossibilityNum := 6 / getTicket
		setIsTrue := getRandomNum/PossibilityNum != 0
		var remindTicket string
		if getTicket == 3 {
			remindTicket = "ç›®å‰�å·²ç»�è¾¾åˆ°ç–²å€¦çŠ¶æ€�ï¼Œæˆ�åŠŸç�‡ä¸‹è°ƒåˆ°15%ï¼Œæˆ–è®¸è€ƒè™‘ä¸€ä¸‹ä¸�è¦�å�šä¸€ä¸ªå��äººå‘¢ï½� ^^ "
		}
		if setIsTrue {
			_ = coins.InsertUserCoins(sdb, siEventUser.UID, siEventUser.Coins-modifyCoins)
			_ = coins.InsertUserCoins(sdb, siTargetUser.UID, siTargetUser.Coins+modifyCoins)
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("è¯•ç�€å�»æ‹¿èµ° ", eventTargetName, " çš„æŸ æª¬ç‰‡æ—¶,è¢«å�‘ç�°äº†.\næ‰€ä»¥ ", eventUserName, " å¤±å�»äº† ", modifyCoins, " ä¸ªæŸ æª¬ç‰‡\n\nå�Œæ—¶ ", eventTargetName, " å¾—åˆ°äº† ", modifyCoins, " ä¸ªæŸ æª¬ç‰‡\n", remindTicket))
			return
		}
		_ = coins.InsertUserCoins(sdb, siEventUser.UID, siEventUser.Coins+modifyCoins)
		_ = coins.InsertUserCoins(sdb, siTargetUser.UID, siTargetUser.Coins-modifyCoins)
		ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("è¯•ç�€å�»æ‹¿èµ° ", eventTargetName, " çš„æŸ æª¬ç‰‡æ—¶,æˆ�åŠŸäº†.\næ‰€ä»¥ ", eventUserName, " å¾—åˆ°äº† ", modifyCoins, " ä¸ªæŸ æª¬ç‰‡\n\nå�Œæ—¶ ", eventTargetName, " å¤±å�»äº† ", modifyCoins, " ä¸ªæŸ æª¬ç‰‡\n", remindTicket))
	})
	engine.OnRegex(`^éª—\s?\[CQ:at,qq=(\d+)\]\s(\d+)ä¸ªæŸ æª¬ç‰‡$`, zero.OnlyGroup).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
		if !catchLimit.Load(ctx.Event.UserID).Acquire() {
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("å¤ªè´ªå¿ƒäº†å“¦~ä¸€å°�æ—¶å��å†�æ�¥è¯•è¯•å�§"))
			return
		}
		getProtectStatus := CheckUserIsEnabledProtectMode(ctx.Event.UserID, sdb)
		if getProtectStatus {
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("å·²ç»�å�¯åŠ¨ä¿�æŠ¤æ¨¡å¼�ï¼Œä¸�å…�è®¸å�‚ä¸�ä»»ä½•æŠ½å¥–æ€§è´¨ç±»äº’åŠ¨"))
			return
		}
		TargetInt, _ := strconv.ParseInt(ctx.State["regex_matched"].([]string)[1], 10, 64)
		getProtectTargetStatus := CheckUserIsEnabledProtectMode(TargetInt, sdb)
		if getProtectTargetStatus {
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("å¯¹æ–¹å·²ç»�å�¯åŠ¨ä¿�æŠ¤æ¨¡å¼�ï¼Œä¸�å…�è®¸æ­¤å¤„ç�†æ“�ä½œ"))
			return
		}
		modifyCoins, _ := strconv.Atoi(ctx.State["regex_matched"].([]string)[2])
		if TargetInt == ctx.Event.UserID {
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("å“ˆ? å¹²å˜›éª—è‡ªå·±çš„?å��è›‹å“¦"))
			return
		}
		switch {
		case modifyCoins <= 100:
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("è²Œä¼¼ä½ æ˜¯æƒ³å€’è´´åˆ«äººæ�¥ç�€å˜›?å�¯ä»¥è¯•è¯•å¤šéª—ä¸€ç‚¹å“¦ï¼Œæ—¢ç„¶éƒ½éª—äº†é‚£å°±å¤šç‚¹å�§x"))
			return
		case modifyCoins > 2000:
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("ä¸�è¦�å¤ªè´ªå¿ƒäº†å•¦ï¼�å¤ªå��äº† "))
			return
		}
		uid := ctx.Event.UserID
		siEventUser := coins.GetSignInByUID(sdb, uid)        // è�·å�–ä¸»ç”¨æˆ·ç›®å‰�çŠ¶å†µä¿¡æ�¯
		siTargetUser := coins.GetSignInByUID(sdb, TargetInt) // è�·å¾—è¢«æŠ¢ç”¨æˆ·ç›®å‰�æƒ…å†µä¿¡æ�¯
		switch {
		case siTargetUser.Coins < modifyCoins:
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("å¤ªå��äº†~è¯•å›¾çš„å¯¹è±¡è²Œä¼¼æ²¡æœ‰è¶³å¤Ÿå¤šçš„æŸ æª¬ç‰‡~"))
			return
		case siEventUser.Coins < modifyCoins:
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("è²Œä¼¼ä½ éœ€è¦�æœ‰é‚£ä¹ˆå¤šæ•°é‡�çš„æŸ æª¬ç‰‡å“¦w"))
			return
		}
		eventUserName := ctx.CardOrNickName(uid)
		eventTargetName := ctx.CardOrNickName(TargetInt)
		// get random numbers.
		getTargetChanceToDealRaw := math.Round(float64(modifyCoins / 20)) // the total is 0-100ï¼Œhowever I don't allow getting chance 0. lmao. max is 100 if modify is 2000
		getTicket := RobOrCatchLimitManager(ctx)
		var remindTicket string
		if getTicket == 3 {
			remindTicket = "ç›®å‰�å·²ç»�è¾¾åˆ°ç–²å€¦çŠ¶æ€�ï¼Œæˆ�åŠŸç�‡ä¸‹è°ƒæœ¬èº«æ¦‚ç�‡çš„15%ï¼Œæˆ–è®¸è€ƒè™‘ä¸€ä¸‹ä¸�è¦�å�šä¸€ä¸ªå��äººå‘¢ï½� ^^ "
		}
		getTargetChanceToDealPossibilityKey := rand.Intn(102 / getTicket)
		if getTargetChanceToDealPossibilityKey < int(getTargetChanceToDealRaw) { // failed
			doubledModifyNum := modifyCoins * 2
			if doubledModifyNum > siEventUser.Coins {
				doubledModifyNum = siEventUser.Coins
				_ = coins.InsertUserCoins(sdb, siEventUser.UID, siEventUser.Coins-doubledModifyNum)
				_ = coins.InsertUserCoins(sdb, siTargetUser.UID, siTargetUser.Coins+doubledModifyNum)
				ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("è¯•ç�€å�»éª—èµ° ", eventTargetName, " çš„æŸ æª¬ç‰‡æ—¶,è¢« ", eventTargetName, " å�‘ç�°äº†.\næœ¬è¯¥å¤±å�» ", modifyCoins*2, "\nä½†å› ä¸º ", eventUserName, " çš„æŸ æª¬ç‰‡è¿‡å°‘ï¼Œæ‰€ä»¥ ", eventUserName, " å¤±å�»äº† ", doubledModifyNum, " ä¸ªæŸ æª¬ç‰‡\n\nå�Œæ—¶ ", eventTargetName, " å¾—åˆ°äº† ", doubledModifyNum, " ä¸ªæŸ æª¬ç‰‡\n", remindTicket))
				return
			}
			_ = coins.InsertUserCoins(sdb, siEventUser.UID, siEventUser.Coins-doubledModifyNum)
			_ = coins.InsertUserCoins(sdb, siTargetUser.UID, siTargetUser.Coins+doubledModifyNum)
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("è¯•ç�€å�»éª—èµ° ", eventTargetName, " çš„æŸ æª¬ç‰‡æ—¶,è¢« ", eventTargetName, " å�‘ç�°äº†.\næ‰€ä»¥ ", eventUserName, " å¤±å�»äº† ", doubledModifyNum, " ä¸ªæŸ æª¬ç‰‡\n\nå�Œæ—¶ ", eventTargetName, " å¾—åˆ°äº† ", doubledModifyNum, " ä¸ªæŸ æª¬ç‰‡\n", remindTicket))
			return
		}
		_ = coins.InsertUserCoins(sdb, siEventUser.UID, siEventUser.Coins+modifyCoins)
		_ = coins.InsertUserCoins(sdb, siTargetUser.UID, siTargetUser.Coins-modifyCoins)
		ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("è¯•ç�€å�»æ‹¿èµ° ", eventTargetName, " çš„æŸ æª¬ç‰‡æ—¶,æˆ�åŠŸäº†.\næ‰€ä»¥ ", eventUserName, " å¾—åˆ°äº† ", modifyCoins, " ä¸ªæŸ æª¬ç‰‡\n\nå�Œæ—¶ ", eventTargetName, " å¤±å�»äº† ", modifyCoins, " ä¸ªæŸ æª¬ç‰‡\n", remindTicket))
	})
	engine.OnRegex(`^ç»™\s?\[CQ:at,qq=(\d+)\]\sè½¬(\d+)ä¸ªæŸ æª¬ç‰‡$`, zero.OnlyGroup).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
		if !processLimit.Load(ctx.Event.UserID).Acquire() {
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("è¯·ç­‰ä¸€ä¼šå†�è½¬è´¦å“¦w"))
			return
		}
		getProtectStatus := CheckUserIsEnabledProtectMode(ctx.Event.UserID, sdb)
		if getProtectStatus {
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("å·²ç»�å�¯åŠ¨ä¿�æŠ¤æ¨¡å¼�ï¼Œä¸�å…�è®¸å�‚ä¸�ä»»ä½•æŠ½å¥–æ€§è´¨ç±»äº’åŠ¨"))
			return
		}
		TargetInt, _ := strconv.ParseInt(ctx.State["regex_matched"].([]string)[1], 10, 64)
		getProtectTargetStatus := CheckUserIsEnabledProtectMode(TargetInt, sdb)
		if getProtectTargetStatus {
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("å¯¹æ–¹å·²ç»�å�¯åŠ¨ä¿�æŠ¤æ¨¡å¼�ï¼Œä¸�å…�è®¸æ­¤å¤„ç�†æ“�ä½œ"))
			return
		}
		modifyCoins, _ := strconv.Atoi(ctx.State["regex_matched"].([]string)[2])
		if modifyCoins < 1 {
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("ç„¶è€Œä½ ä¸�èƒ½è½¬è´¦ä½�äº�0ä¸ªæŸ æª¬ç‰‡å“¦wï½� æ•²"))
			return
		}
		if TargetInt == ctx.Event.UserID {
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("ä¸�å�¯ä»¥ç»™è‡ªå·±è½¬è´¦å“¦wï¼ˆæ•²ï¼‰"))
			return
		}
		uid := ctx.Event.UserID
		siEventUser := coins.GetSignInByUID(sdb, uid)        // è�·å�–ä¸»ç”¨æˆ·ç›®å‰�çŠ¶å†µä¿¡æ�¯
		siTargetUser := coins.GetSignInByUID(sdb, TargetInt) // è�·å¾—è¢«è½¬è´¦ç”¨æˆ·ç›®å‰�æƒ…å†µä¿¡æ�¯
		if modifyCoins > siEventUser.Coins {
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("è²Œä¼¼ä½ çš„æŸ æª¬ç‰‡æ•°é‡�ä¸�å¤Ÿå“¦~"))
			return
		}
		ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("è½¬è´¦æˆ�åŠŸäº†å“¦~\n", ctx.CardOrNickName(siEventUser.UID), " å�˜åŒ–ä¸º ", siEventUser.Coins, " - ", modifyCoins, "= ", siEventUser.Coins-modifyCoins, "\n", ctx.CardOrNickName(siTargetUser.UID), " å�˜åŒ–ä¸º: ", siTargetUser.Coins, " + ", modifyCoins, "= ", siTargetUser.Coins+modifyCoins))
		_ = coins.InsertUserCoins(sdb, siEventUser.UID, siEventUser.Coins-modifyCoins)
		_ = coins.InsertUserCoins(sdb, siTargetUser.UID, siTargetUser.Coins+modifyCoins)
	})
	engine.OnRegex(`^HandleCoins\s?\[CQ:at,qq=(\d+)\]\s(\d+)$`, zero.SuperUserPermission).SetBlock(true).Handle(func(ctx *zero.Ctx) {
		TargetInt, _ := strconv.ParseInt(ctx.State["regex_matched"].([]string)[1], 10, 64)
		modifyCoins, _ := strconv.Atoi(ctx.State["regex_matched"].([]string)[2])
		siTargetUser := coins.GetSignInByUID(sdb, TargetInt) // get user info
		unModifyCoins := siTargetUser.Coins
		_ = coins.InsertUserCoins(sdb, TargetInt, unModifyCoins+modifyCoins)
		ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("Handle Coins Successfully.\n"))
	})
	engine.OnRegex(`^(ä¸¢å¼ƒ|æ‰”æ�‰)(\d+)ä¸ªæŸ æª¬ç‰‡$`).SetBlock(true).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
		modifyCoins, _ := strconv.Atoi(ctx.State["regex_matched"].([]string)[2])
		handleUser := coins.GetSignInByUID(sdb, ctx.Event.UserID)
		currentUserCoins := handleUser.Coins
		if currentUserCoins-modifyCoins < 0 {
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("è²Œä¼¼ä½ çš„æŸ æª¬ç‰‡ä¸�å¤Ÿå¤„ç�†å‘¢("))
			return
		}
		hadModifyCoins := currentUserCoins - modifyCoins
		_ = coins.InsertUserCoins(sdb, handleUser.UID, hadModifyCoins)
		ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("å·²ç»�å¸®ä½ æ‰”æ�‰äº†å“¦"))
	})
	engine.OnFullMatch("å…‘æ�¢æ¶©å›¾", zero.OnlyGroup).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
		if !payLimit.Load(ctx.Event.UserID).Acquire() {
			// ä½  ç¾¤ ç�° çŠ¶
			ctx.SendChain(message.Text("å��æ¬¸ï¼�ä¸ºä»€ä¹ˆä¸€ä¸ªç¾¤æœ‰è¿™ä¹ˆå¤šäººçœ‹æ¶©å›¾å•Šï¼ˆæ™•"))
			return
		}
		modified, _ := control.Lookup("nsfw")
		status := modified.IsEnabledIn(ctx.Event.GroupID)
		if status {
			var mutex sync.RWMutex // æ·»åŠ è¯»å†™é”�ä»¥ä¿�è¯�ç¨³å®šæ€§
			mutex.Lock()
			uid := ctx.Event.UserID
			si := coins.GetSignInByUID(sdb, uid) // è�·å�–ç”¨æˆ·ç›®å‰�çŠ¶å†µä¿¡æ�¯
			userCurrentCoins := si.Coins         // loading coins status
			if userCurrentCoins < 400 {
				ctx.SendChain(message.Reply(uid), message.Text("æœ¬æ¬¡å�‚ä¸�çš„æŸ æª¬ç‰‡ä¸�å¤Ÿå“¦~è¯·å¤šå¤šæ‰“å�¡wï¼Œä¸€æ¬¡å…‘æ�¢æœ€å°‘éœ€è¦�400"))
				return
			}
			img, err := web.GetData("https://api.lolicon.app/setu/v2?r18=1&num=1")
			if err != nil {
				ctx.SendChain(message.Text("ERROR:", err))
				return
			}
			picURL := gjson.Get(string(img), "data.0.urls.original").String()
			messageID := ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text(picURL)).ID()
			if messageID != 0 { // ä¿�è¯�æˆ�åŠŸå��æ‰�æ‰£é™¤
				_ = coins.InsertUserCoins(sdb, si.UID, userCurrentCoins-400)
			}
		} else {
			ctx.SendChain(message.Text("æœ¬ç¾¤å¹¶æ²¡æœ‰å¼€å�¯nsfwå“¦ï¼Œä¸�å…�è®¸ä½¿ç”¨æ­¤åŠŸèƒ½å“¦x"))
			return
		}
	})
	// I thought I just write a piece of shit. ğŸ’©
	engine.OnRegex(`^[!ï¼�]coin\swager\s?(\d*)`).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
		// å¾—åˆ°æœ¬èº«å¥–æ± å¤§å°�ï¼Œå¦‚æ�œæ²¡æœ‰æˆ–è€…è¢«getçš„æƒ…å†µä¸‹è�·èƒœ
		// this method should deal when we have less starter.
		rawNumber := ctx.State["regex_matched"].([]string)[1]
		if rawNumber == "" {
			rawNumber = "50"
		}
		getProtectStatus := CheckUserIsEnabledProtectMode(ctx.Event.UserID, sdb)
		if getProtectStatus {
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("å·²ç»�å�¯åŠ¨ä¿�æŠ¤æ¨¡å¼�ï¼Œä¸�å…�è®¸å�‚ä¸�ä»»ä½•æŠ½å¥–æ€§è´¨ç±»äº’åŠ¨"))
			return
		}
		modifyCoins, _ := strconv.Atoi(rawNumber)
		if modifyCoins > 1000 {
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("ä¸€æ¬¡æ€§æœ€å¤§æŠ•å…¥ä¸º1k"))
			return
		}
		handleUser := coins.GetSignInByUID(sdb, ctx.Event.UserID)
		currentUserCoins := handleUser.Coins
		if currentUserCoins-modifyCoins < 0 {
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("è²Œä¼¼ä½ çš„æŸ æª¬ç‰‡ä¸�å¤Ÿå¤„ç�†å‘¢("))
			return
		}
		// first of all , check the user status
		handlerWagerUser := coins.GetWagerUserStatus(sdb, ctx.Event.UserID)
		if handlerWagerUser.UserExistedStoppedTime > time.Now().Add(-time.Hour*12).Unix() {
			// then not pass | in the freeze time.
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("ç›®å‰�åœ¨å†·å�´æ—¶é—´ï¼Œè·�ç¦»ä¸‹ä¸ªå�¯ç”¨æ—¶é—´ä¸º: ", time.Unix(handlerWagerUser.UserExistedStoppedTime, 0).Add(time.Hour*12).Format(time.DateTime)))
			return
		}
		// passed,delete this one and continue || before max is 3500.
		checkUserWagerCoins := handlerWagerUser.InputCountNumber
		if int64(modifyCoins)+checkUserWagerCoins > 3500 {
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("è¾¾åˆ°å†·å�´æœ€å¤§å€¼ï¼Œæ‚¨ç›®å‰�å�¯æŠ•å…¥ï¼š"+strconv.Itoa(int(3500-checkUserWagerCoins))))
			return
		}
		// get wager
		getWager := coins.GetWagerStatus(sdb)
		if getWager.Expected == 0 {
			// it shows that no condition happened.
			// if not maxzine
			// in the wager mode. || start to load
			getGenOne := fcext.RandSenderPerDayN(time.Now().Unix(), 16500)
			getRandNumber := getGenOne + fcext.RandSenderPerDayN(time.Now().Unix()+ctx.Event.UserID, 5000) + 3000
			_ = coins.WagerCoinsInsert(sdb, modifyCoins+wagerData["data"], 0, getRandNumber)
			if int64(modifyCoins)+checkUserWagerCoins == 3500 {
				_ = coins.UpdateWagerUserStatus(sdb, ctx.Event.UserID, time.Now().Unix(), 0)
			} else {
				_ = coins.UpdateWagerUserStatus(sdb, ctx.Event.UserID, 0, int64(modifyCoins)+checkUserWagerCoins)
			}
			if getRandNumber <= modifyCoins {
				// winner, he | she is so lucky.^^
				// Lucy will cost 10 percent Coins.
				willRunCoins := math.Round(float64(modifyCoins+getWager.Wagercount) * 0.9)
				_ = coins.InsertUserCoins(sdb, ctx.Event.UserID, handleUser.Coins+int(willRunCoins)-modifyCoins)
				_ = coins.WagerCoinsInsert(sdb, 0, int(ctx.Event.UserID), 0)
				wagerData["data"] = int(math.Round(float64(modifyCoins+getWager.Wagercount)*0.1)) - 200
				ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("wï¼�æ�­å–œå“¦ï¼Œå¥–æ± ä¸­å¥–äº†wwï¼Œä¸€å…±è�·å¾— ", willRunCoins, " ä¸ªæŸ æª¬ç‰‡ï¼Œå½“å‰�æœ‰ ", handleUser.Coins+int(willRunCoins)-modifyCoins, " ä¸ªæŸ æª¬ç‰‡ (è�·èƒœè€…å¾—åˆ°å¥–æ±  x0.9çš„æŸ æª¬ç‰‡æ€»æ•°)"))
				return
			}
			// not winner
			_ = coins.InsertUserCoins(sdb, handleUser.UID, handleUser.Coins-modifyCoins)
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("æ²¡æœ‰ä¸­å¥–å“¦~ï¼Œå½“å‰�å¥–æ± ä¸ºï¼š"+strconv.Itoa(modifyCoins)))
			return
		}
		// not init,start to add.
		getExpected := getWager.Expected
		if int64(modifyCoins)+checkUserWagerCoins == 3500 {
			_ = coins.UpdateWagerUserStatus(sdb, ctx.Event.UserID, time.Now().Unix(), 0)
		} else {
			_ = coins.UpdateWagerUserStatus(sdb, ctx.Event.UserID, 0, int64(modifyCoins)+checkUserWagerCoins)
		}
		if getWager.Wagercount+modifyCoins >= getExpected {
			// you are winner!
			willRunCoins := math.Round(float64(modifyCoins+getWager.Wagercount) * 0.9)
			_ = coins.InsertUserCoins(sdb, ctx.Event.UserID, handleUser.Coins+int(willRunCoins)-modifyCoins)
			_ = coins.WagerCoinsInsert(sdb, 0, int(ctx.Event.UserID), 0)
			wagerData["data"] = int(math.Round(float64(modifyCoins+getWager.Wagercount)*0.1)) - 200
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("wï¼�æ�­å–œå“¦ï¼Œå¥–æ± ä¸­å¥–äº†wwï¼Œä¸€å…±è�·å¾— ", willRunCoins, " ä¸ªæŸ æª¬ç‰‡ï¼Œå½“å‰�æœ‰ ", handleUser.Coins+int(willRunCoins)-modifyCoins, " ä¸ªæŸ æª¬ç‰‡ (è�·èƒœè€…å¾—åˆ°å¥–æ±  x0.9çš„æŸ æª¬ç‰‡æ€»æ•°)"))
			return
		} else {
			_ = coins.WagerCoinsInsert(sdb, getWager.Wagercount+modifyCoins, 0, getExpected)
			_ = coins.InsertUserCoins(sdb, ctx.Event.UserID, handleUser.Coins-modifyCoins)
			if rand.Intn(10) == 8 {
				ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("å‘�ï½�ï¼Œä¸�ä¼šè¿˜æœ‰å¤§å“¥å“¥åˆ°ç�°åœ¨ "+strconv.Itoa(getWager.Wagercount+modifyCoins)+" ä¸ªæŸ æª¬ç‰‡äº†éƒ½æ²¡ä¸­å¥–å�§ï¼Ÿæ�‚é±¼ï½�â�¤ï¼Œæ�‚é±¼ï½�â�¤"))
			} else {
				ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("æ²¡æœ‰ä¸­å¥–å“¦~ï¼Œå½“å‰�å¥–æ± ä¸º: ", getWager.Wagercount+modifyCoins))
			}
		}
	})
	engine.OnRegex(`^(ç¦�ç”¨|å�¯ç”¨)æŸ æª¬ç‰‡äº’åŠ¨`).SetBlock(true).Limit(ctxext.LimitByUser).Handle(func(ctx *zero.Ctx) {
		getCode := ctx.State["regex_matched"].([]string)[1]
		if getCode == "ç¦�ç”¨" {
			getStatus := CheckUserIsEnabledProtectMode(ctx.Event.UserID, sdb)
			if getStatus {
				ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("ä½ å·²ç»�å…³é—­äº†~"))
				return
			}
			// start to handle
			suser := coins.GetProtectModeStatus(sdb, ctx.Event.UserID)
			boolStatus := suser.Time+60*60*24 < time.Now().Unix()
			if !boolStatus {
				ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("ä»…å…�è®¸24å°�æ—¶ä¿®æ”¹ä¸€æ¬¡"))
				return
			} // not the time
			// handle it.
			_ = coins.ChangeProtectStatus(sdb, ctx.Event.UserID, 1)
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("ä¿®æ”¹å®Œæˆ�~"))
			return
		}
		getStatus := CheckUserIsEnabledProtectMode(ctx.Event.UserID, sdb)
		if !getStatus {
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("ä½ å·²ç»�å�¯ç”¨äº†~"))
			return
		}
		// start to handle
		suser := coins.GetProtectModeStatus(sdb, ctx.Event.UserID)
		boolStatus := suser.Time+60*60*24 < time.Now().Unix()
		if !boolStatus {
			ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("ä»…å…�è®¸24å°�æ—¶ä¿®æ”¹ä¸€æ¬¡"))
			return
		} // not the time
		// handle it.
		_ = coins.ChangeProtectStatus(sdb, ctx.Event.UserID, 0)
		ctx.SendChain(message.Reply(ctx.Event.MessageID), message.Text("ä¿®æ”¹å®Œæˆ�~"))
	})
}

func RobOrCatchLimitManager(ctx *zero.Ctx) (ticket int) {
	// use limitManager to reduce the chance of true.
	// 33 * 4 + 6 * 5 + null key (4 time tired.)
	/*
		first time to get full chance to win.
		second time reduce it to 50 % chance to win
		last time is null , you are tired and reduce it to 33% chance to win.
	*/
	switch {
	case RobTimeManager.Load(ctx.Event.UserID).AcquireN(33):
		return 1
	case RobTimeManager.Load(ctx.Event.UserID).AcquireN(6):
		return 2
	case RobTimeManager.Load(ctx.Event.UserID).Acquire():
		return 3
	default:
		return 3
	}
}

// CheckUserIsEnabledProtectMode 1 is enabled protect mode.
func CheckUserIsEnabledProtectMode(uid int64, sdb *coins.Scoredb) bool {
	s := coins.GetProtectModeStatus(sdb, uid)
	getCode := s.Status
	if getCode == 0 {
		return false
	} else {
		return true
	}
}
