package uid

import (
	"context"
	"fmt"
	"math"
	"os"
	"strconv"
	"sync/atomic"
	"time"

	"github.com/redis/go-redis/v9"
)

const (
	Order = 1
)

const (
	BitsFull              = 64            //
	BitsPre               = 1             // 固定
	BitsTime              = 41            // 毫秒时间戳 可以最多支持69年
	BitsServer            = 5             // 服务器最多支持32台
	BitsWorker            = 5             // 最多支持32种业务
	BitsSequence          = 12            // 一毫秒内支持4096个请求
	OffsetTimestamp int64 = 1561910400000 // 时间戳起点时间毫秒 2019-07-01 00:00:00
)

type SignGenerator struct {
	ServerID    int
	WorkerID    int
	RedisClient *redis.Client
}

func NewSignGenerator(redisClient *redis.Client) *SignGenerator {
	return &SignGenerator{
		RedisClient: redisClient,
	}
}

func (sg *SignGenerator) GetNumber() (uint64, error) {
	if sg.ServerID < 0 || sg.ServerID > 31 {
		return 0, fmt.Errorf("serverID is lost")
	}

	if sg.WorkerID < 0 || sg.WorkerID > 31 {
		return 0, fmt.Errorf("workerID is lost")
	}

	// redis报错等时最大5次retry就退出生成
	maxRetryCount := 0
	for {
		if maxRetryCount > 5 {
			return 0, fmt.Errorf("more than 5 times retry")
		}
		id := uint64(math.Pow(2, BitsFull-BitsPre)) << BitsPre

		nowTime := time.Now().UnixNano() / int64(time.Millisecond)
		startTime := OffsetTimestamp
		diffTime := nowTime - startTime
		shift := BitsFull - BitsPre - BitsTime
		id |= uint64(diffTime << shift)

		shift -= BitsServer
		id |= uint64(sg.ServerID) << shift

		shift -= BitsWorker
		id |= uint64(sg.WorkerID) << shift

		// 自增值
		sequenceNumber := sg.getSequence(strconv.FormatUint(id, 10), 0)

		if sequenceNumber > int64(math.Pow(2, BitsSequence)-1) {
			time.Sleep(1 * time.Millisecond)
			maxRetryCount++
		} else {
			id |= uint64(sequenceNumber)
			return id, nil
		}
	}
}

func (sg *SignGenerator) GetServerID() int {
	return sg.ServerID
}

func (sg *SignGenerator) SetServerID(serverID int) *SignGenerator {
	sg.ServerID = serverID
	return sg
}

func (sg *SignGenerator) GetWorkerID() int {
	return sg.WorkerID
}

func (sg *SignGenerator) SetWorkerID(workerID int) *SignGenerator {
	sg.WorkerID = workerID
	return sg
}

func (sg *SignGenerator) getSequence(id string, level int) int64 {
	// 嵌套大于50层时，返回最大序号
	if level > 50 {
		return int64(math.Pow(2, BitsSequence)) + 1
	}
	lua := `
		local sequenceKey = KEYS[1]
		local sequenceNumber = redis.call("incr", sequenceKey)
		redis.call("pexpire", sequenceKey, 100)
		return sequenceNumber
	`
	sequence, err := sg.RedisClient.Eval(context.Background(), lua, []string{id}, 1).Int64()
	if err != nil {
		fmt.Println("SignGenerator err", sequence, err, level)
		return sg.getSequence(id, level+1)
	}
	return sequence
}

func (sg *SignGenerator) ReverseNumber(number uint64) map[string]interface{} {
	uuidItem := make(map[string]interface{})
	shift := BitsFull - BitsPre - BitsTime
	uuidItem["diffTime"] = (number >> shift) & (uint64(math.Pow(2, BitsTime)) - 1)

	shift -= BitsServer
	uuidItem["serverId"] = (number >> shift) & (uint64(math.Pow(2, BitsServer)) - 1)

	shift -= BitsWorker
	uuidItem["workerId"] = (number >> shift) & (uint64(math.Pow(2, BitsWorker)) - 1)

	shift -= BitsSequence
	uuidItem["sequenceNumber"] = (number >> shift) & (uint64(math.Pow(2, BitsSequence)) - 1)

	timestamp := int64(uuidItem["diffTime"].(uint64)/1000) + (OffsetTimestamp / 1000)
	uuidItem["generateTime"] = time.Unix(timestamp, 0).Format("2006-01-02 15:04:05")

	return uuidItem
}

var num int64

func GenerateNo() string {
	t := time.Now()
	s := t.Format("20060102150405")
	m := t.UnixNano()/1e6 - t.UnixNano()/1e9*1e3
	ms := sup(m, 3)
	p := os.Getpid() % 1000
	ps := sup(int64(p), 3)
	i := atomic.AddInt64(&num, 1)
	r := i % 10000
	rs := sup(r, 4)
	n := fmt.Sprintf("%s%s%s%s", s, ms, ps, rs)
	return n
}

func sup(i int64, n int) string {
	m := fmt.Sprintf("%d", i)
	for len(m) < n {
		m = fmt.Sprintf("0%s", m)
	}
	return m
}
