// k ordered id generator
package ids

import (
	"errors"
	"sync"
	"time"
)

const (
	// 2024-07-03T00:00:00Z
	customEpochMs int64 = 1719964800000

	seqBits    = 8
	workerBits = 16
	timeBits   = 40

	seqMask    = (1 << seqBits) - 1           // 0xFF
	workerMask = (1 << workerBits) - 1        // 0xFFFF
	timeMask   = (int64(1) << timeBits) - 1   // low 40 bits

	workerShift = seqBits
	timeShift   = workerBits + seqBits
)

// Generator is safe for concurrent use.
type Generator struct {
	workerID uint64
	mu       sync.Mutex
	lastMs   int64
	sequence uint64
}

func NewGenerator(workerID uint32) (*Generator, error) {
	if workerID > workerMask {
		return nil, errors.New("workerID too large for 16 bits")
	}
	return &Generator{workerID: uint64(workerID)}, nil
}

func (g *Generator) Next() (uint64, error) {
	nowMs := time.Now().UTC().UnixMilli()
	delta := nowMs - customEpochMs
	if delta < 0 {
		return 0, errors.New("time is before custom epoch")
	}
	if delta > timeMask {
		return 0, errors.New("timestamp overflow (40-bit ms range exceeded)")
	}

	g.mu.Lock()
	defer g.mu.Unlock()

	if delta == g.lastMs {
		g.sequence = (g.sequence + 1) & seqMask
		if g.sequence == 0 {
			// sequence wrapped; wait for next millisecond
			for delta == g.lastMs {
				nowMs = time.Now().UTC().UnixMilli()
				delta = nowMs - customEpochMs
			}
		}
	} else {
		g.sequence = 0
		g.lastMs = delta
	}

	id := (uint64(delta) << timeShift) |
		((g.workerID & workerMask) << workerShift) |
		(g.sequence & seqMask)

	return id, nil
}

// ---- Base62 ----

const base62Alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"

func Base62Encode(x uint64) string {
	if x == 0 {
		return "0"
	}
	var buf [11]byte // 62^11 > 2^64, so max 11 chars
	i := len(buf)
	for x > 0 {
		r := x % 62
		x /= 62
		i--
		buf[i] = base62Alphabet[r]
	}
	return string(buf[i:])
}

