package idgen

import (
	"sync"
	"time"
)

const (
	// int64EpochMs 使用固定业务纪元，缩短时间戳占用位数。
	int64EpochMs int64 = 1704067200000
	// maxInt64NodeID 预留 10 bit 给节点，支持 1024 个发号节点。
	maxInt64NodeID int64 = 1023
	// int64SequenceMask 预留 12 bit 给同毫秒序列。
	int64SequenceMask int64 = 4095
)

// Int64Generator 生成 Snowflake-like int64 ID。
// 格式为 timestamp_ms | node_id | sequence，适合用户主键这种不可变长 ID。
type Int64Generator struct {
	mu         sync.Mutex
	nodeID     int64
	lastMs     int64
	sequence   int64
	now        func() time.Time
	sleepUntil func(time.Time)
}

// NewInt64Generator 创建用户主键发号器。
// nodeID 必须由部署配置保证不同发号节点不冲突。
func NewInt64Generator(nodeID int64) *Int64Generator {
	if nodeID < 0 {
		nodeID = 0
	}
	if nodeID > maxInt64NodeID {
		nodeID = nodeID & maxInt64NodeID
	}

	return &Int64Generator{
		nodeID:     nodeID,
		now:        time.Now,
		sleepUntil: defaultSleepUntil,
	}
}

// NewInt64 返回一个全局唯一倾向的 int64 ID。
func (g *Int64Generator) NewInt64() int64 {
	g.mu.Lock()
	defer g.mu.Unlock()

	nowMs := g.now().UnixMilli()
	if nowMs < g.lastMs {
		// 时钟回拨时等待到上次发号时间，避免生成倒退 ID。
		g.sleepUntil(time.UnixMilli(g.lastMs))
		nowMs = g.now().UnixMilli()
	}

	if nowMs == g.lastMs {
		g.sequence = (g.sequence + 1) & int64SequenceMask
		if g.sequence == 0 {
			// 同毫秒序列耗尽时等到下一毫秒再继续。
			next := time.UnixMilli(g.lastMs + 1)
			g.sleepUntil(next)
			nowMs = g.now().UnixMilli()
		}
	} else {
		g.sequence = 0
	}

	g.lastMs = nowMs

	return ((nowMs - int64EpochMs) << 22) | (g.nodeID << 12) | g.sequence
}

// defaultSleepUntil 封装真实等待，测试可通过结构体替换。
func defaultSleepUntil(target time.Time) {
	if delay := time.Until(target); delay > 0 {
		time.Sleep(delay)
	}
}
