package com.test.bijihoudaun.util;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class SnowflakeIdGenerator {

    // 起始时间戳（2023-01-01 00:00:00）
    private static final long EPOCH = 1672531200000L;

    // 机器ID位数
    private static final long WORKER_ID_BITS = 5L;
    // 数据中心ID位数
    private static final long DATACENTER_ID_BITS = 5L;
    // 最大机器ID (0-31)
    private static final long MAX_WORKER_ID = ~(-1L << WORKER_ID_BITS);
    // 最大数据中心ID (0-31)
    private static final long MAX_DATACENTER_ID = ~(-1L << DATACENTER_ID_BITS);
    // 序列号位数
    private static final long SEQUENCE_BITS = 12L;

    // 机器ID左移位数
    private static final long WORKER_ID_SHIFT = SEQUENCE_BITS;
    // 数据中心ID左移位数
    private static final long DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS;
    // 时间戳左移位数
    private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS;
    // 序列号掩码（4095）
    private static final long SEQUENCE_MASK = ~(-1L << SEQUENCE_BITS);

    private final long workerId;
    private final long datacenterId;

    private long sequence = 0L;
    private long lastTimestamp = -1L;

    /**
     * 构造函数
     *
     * @param workerId     机器ID
     * @param datacenterId 数据中心ID
     */
    public SnowflakeIdGenerator(
            @Value("${worker.id:0}") long workerId,
            @Value("${datacenter.id:0}") long datacenterId) {

        // 校验机器ID范围
        if (workerId > MAX_WORKER_ID || workerId < 0) {
            throw new IllegalArgumentException(
                    String.format("Worker ID 必须在 0 到 %d 之间", MAX_WORKER_ID));
        }

        // 校验数据中心ID范围
        if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0) {
            throw new IllegalArgumentException(
                    String.format("Datacenter ID 必须在 0 到 %d 之间", MAX_DATACENTER_ID));
        }

        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    /**
     * 生成雪花ID
     *
     * @return 雪花ID
     */
    public synchronized long nextId() {
        long timestamp = currentTime();

        // 处理时钟回拨
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                    String.format("时钟回拨拒绝生成ID。当前时间: %d, 最后时间: %d",
                            timestamp, lastTimestamp));
        }

        // 同一毫秒内生成ID
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & SEQUENCE_MASK;
            // 当前毫秒序列号用完
            if (sequence == 0) {
                timestamp = nextMillis(lastTimestamp);
            }
        }
        // 新毫秒重置序列号
        else {
            sequence = 0L;
        }

        lastTimestamp = timestamp;

        // 组合ID各部分
        return ((timestamp - EPOCH) << TIMESTAMP_SHIFT)
                | (datacenterId << DATACENTER_ID_SHIFT)
                | (workerId << WORKER_ID_SHIFT)
                | sequence;
    }

    /**
     * 生成雪花ID，转化成字符串类型
     *
     * @return 雪花ID-字符串类型
     */
    public synchronized String nextIdStr() {
        return Long.toString(nextId());
    }

    // 等待下一毫秒
    private long nextMillis(long lastTimestamp) {
        long timestamp = currentTime();
        while (timestamp <= lastTimestamp) {
            timestamp = currentTime();
        }
        return timestamp;
    }

    // 获取当前毫秒时间戳
    private long currentTime() {
        return System.currentTimeMillis();
    }
}