package service

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

const (
	// MaxSequence  max sequence in one time
	MaxSequence = 1000
	// WorkerBit worker bit
	WorkerBit = 10
	// SequenceBit sequence bit
	SequenceBit = 10
)

// SnowFlake snow flake
type SnowFlake struct {
	sync.Mutex
	lastTimestamp int64
	sequence      int64
	workerID      int64
}

// NewSnowFlake new
func NewSnowFlake() *SnowFlake {
	return &SnowFlake{
		workerID: time.Now().UnixNano() % 1000,
	}
}

// Generate generate
func (s *SnowFlake) Generate() (int64, error) {
	s.Lock()
	defer s.Unlock()

	now := time.Now().UnixNano() / 1e6
	if now == s.lastTimestamp {
		s.sequence = (s.sequence + 1) % MaxSequence
		if s.sequence == 0 {
			now = s.waitNextMill(now)
		}
	} else {
		s.sequence = 0
	}

	if now < s.lastTimestamp {
		return 0, errors.New("inner time error")
	}
	s.lastTimestamp = now
	return s.generate(), nil
}

func (s *SnowFlake) generate() int64 {
	return (s.lastTimestamp << (WorkerBit + SequenceBit)) | (s.workerID << SequenceBit) | s.sequence
}

func (s *SnowFlake) waitNextMill(t int64) int64 {
	for t == s.lastTimestamp {
		time.Sleep(100 * time.Microsecond)
		t = time.Now().UnixNano() / 1e6
	}
	return t
}
