<?php

namespace app\utils;

/**
 * 雪花算法 - 分布式唯一ID生成器
 * Class Snowflake
 * @package app\utils
 */
class Snowflake
{
    private const EPOCH = 1704067200000; // 2024-01-01 00:00:00 UTC
    private const WORKER_ID_BITS = 5;
    private const DATACENTER_ID_BITS = 5;
    private const SEQUENCE_BITS = 12;

    private $workerId;
    private $datacenterId;
    private $sequence = 0;
    private $lastTimestamp = -1;

    private static $instance = null;

    public function __construct($workerId = 1, $datacenterId = 1)
    {
        $this->workerId = $workerId & 0x1F;
        $this->datacenterId = $datacenterId & 0x1F;
    }

    /**
     * 获取单例实例
     */
    public static function getInstance()
    {
        if (self::$instance === null) {
            self::$instance = new self(1, 1);
        }
        return self::$instance;
    }

    /**
     * 生成下一个ID
     */
    public function nextId()
    {
        $timestamp = $this->currentTimeMillis();

        if ($timestamp === $this->lastTimestamp) {
            $this->sequence = ($this->sequence + 1) & 0xFFF;
            if ($this->sequence === 0) {
                $timestamp = $this->waitNextMillis($this->lastTimestamp);
            }
        } else {
            $this->sequence = 0;
        }

        $this->lastTimestamp = $timestamp;

        return (($timestamp - self::EPOCH) << 22)
            | ($this->datacenterId << 17)
            | ($this->workerId << 12)
            | $this->sequence;
    }

    /**
     * 生成字符串ID
     */
    public function nextIdString()
    {
        return (string)$this->nextId();
    }

    private function currentTimeMillis()
    {
        return (int)(microtime(true) * 1000);
    }

    private function waitNextMillis($lastTimestamp)
    {
        $timestamp = $this->currentTimeMillis();
        while ($timestamp <= $lastTimestamp) {
            $timestamp = $this->currentTimeMillis();
        }
        return $timestamp;
    }
}
