package hashgen

import (
	"encoding/hex"
	"errors"
	"fmt"
	"sync"
	"time"

	"github.com/patrickmn/go-cache"
	"github.com/sirupsen/logrus"
	"gogs.daxia.dev/huanan/pkg.daxia.dev/encrypt"
	"gogs.daxia.dev/huanan/pkg.daxia.dev/time_utils"
)

var openNumberCache *cache.Cache
var openNumberCacheLocker = sync.Mutex{}

type HashItem []byte

const HashCount = 100000

func Init() {
	for {
		//同步今天，和明天
		timeNow := time.Now()
		today := time_utils.GetFormatTime(timeNow)
		yesterday := time_utils.GetFormatTime(timeNow.Add(time.Hour * -24))

		cacheHash(today)
		cacheHash(yesterday)

		<-time.After(15 * time.Minute)
	}
}

func cacheHash(dayStr string) {
	cache := getCacheInstance()

	key := "on_" + dayStr
	_, exists := cache.Get(key)
	if exists {
		return
	}

	openNumberModelInterface := genOpenNumberHashItem(dayStr)
	logrus.Infof("cache hash:%s-%v", dayStr, len(openNumberModelInterface))

	cache.Set(key, openNumberModelInterface, 48*time.Hour)
}

func GetOpenNumber(dayStr string, roomID int32, clientHash []byte, index int32) ([]string, string, error) {
	cache := getCacheInstance()

	key := "on_" + dayStr
	openNumberModelInterface, exists := cache.Get(key)
	if !exists {
		return nil, "", errors.New("not exists:" + key)
	}

	hashItemList := openNumberModelInterface.([]HashItem)
	serverHash := hashItemList[HashCount-index-1] //index从0开始，用最后一期，表示第一期
	openNumList, _ := GetRoomOpenNumber(int(roomID), serverHash, clientHash)

	return openNumList, hex.EncodeToString(serverHash), nil
}

func getCacheInstance() *cache.Cache {
	openNumberCacheLocker.Lock()
	defer openNumberCacheLocker.Unlock()

	if openNumberCache != nil {
		return openNumberCache
	}

	openNumberCache = cache.New(48*time.Hour, 2*48*time.Hour)
	openNumberCache.OnEvicted(func(name string, val interface{}) {
		//
	})

	return openNumberCache
}

func genOpenNumberHashItem(day string) []HashItem {
	seed := encrypt.KeyDataSHA256([]byte("D40163E6E6E35A2204059B0F1121D2DF" + day))

	hashList := make([]HashItem, HashCount)

	hashA := seed
	for i := 0; i < HashCount; i++ {
		hashItem := encrypt.KeyDataSHA256(hashA)
		hashA = hashItem

		hashList[i] = hashItem
	}

	return hashList
}

func GetRoomOpenNumber(roomID int, hashItem, clientHash []byte) ([]string, string) {
	resultStrList := make([]string, 5)

	roomHashItem := getRoomHash(hashItem, roomID)
	hashItem = encrypt.KeyDataSHA256(append(roomHashItem, clientHash...))
	cardList := calNum(hashItem)

	resultStrList[0] = fmt.Sprintf("%s,%s,%s,%s,%s", cardList[0], cardList[5], cardList[10], cardList[15], cardList[20])
	resultStrList[1] = fmt.Sprintf("%s,%s,%s,%s,%s", cardList[1], cardList[6], cardList[11], cardList[16], cardList[21])
	resultStrList[2] = fmt.Sprintf("%s,%s,%s,%s,%s", cardList[2], cardList[7], cardList[12], cardList[17], cardList[22])
	resultStrList[3] = fmt.Sprintf("%s,%s,%s,%s,%s", cardList[3], cardList[8], cardList[13], cardList[18], cardList[23])
	resultStrList[4] = fmt.Sprintf("%s,%s,%s,%s,%s", cardList[4], cardList[9], cardList[14], cardList[19], cardList[24])

	return resultStrList, hex.EncodeToString(hashItem)
}

func getRoomHash(hashItem []byte, roomID int) []byte {
	roomHash := encrypt.GetMD5HashString(fmt.Sprintf("room_hash_%d", roomID))
	roomHashHex, _ := hex.DecodeString(roomHash)

	hashItem = encrypt.KeyDataSHA256(append(hashItem, roomHashHex...))

	return hashItem
}

func calNum(hashData []byte) []string {
	resultStrList := make([]string, 25)
	filterMap := map[string]bool{}

	offset := 0
	for i := 0; i < 25; i++ {
		item := fmt.Sprintf("%d", (int(hashData[i])+offset)%52) //生成[0-51]
		_, exists := filterMap[item]
		if exists {
			offset++
			i--
			continue
		}

		resultStrList[i] = item
		filterMap[item] = true
		offset = 0
	}

	return resultStrList
}
