package com.trade.common.utils.uuid;

import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

/**
 * @description: 生成带有时间的唯一标识
 * @Author: wanglang
 * @Date: 2024/5/27 20:00
 * @Version: 1.0
 */
public class SnowflakeIdGenerator {

    private static final long EPOCH = LocalDate.of(2024, 1, 1).atStartOfDay(ZoneOffset.UTC).toInstant().toEpochMilli();
    // 用于工作节点 ID 的位数
    private static final int WORKER_ID_BITS = 5;
    // 用于序列号的位数
    private static final int SEQUENCE_BITS = 12;
    // 用于生成唯一 ID 的工作节点 ID
    private final long workerId;
    // 序列号计数器
    private long sequence = 0L;
    // 上一个生成 ID 的时间戳
    private long lastTimestamp = -1L;

    /**
     * SnowflakeIdGenerator 类的构造函数。
     *
     * @param workerId 工作节点 ID（每个实例应保证唯一）
     * @throws IllegalArgumentException 如果工作节点 ID 超出范围
     */
    public SnowflakeIdGenerator(long workerId){
        if (workerId < 0 || workerId >= (1L << WORKER_ID_BITS)) {
            throw new IllegalArgumentException("工作节点 ID 必须介于 0 和 " + ((1L << WORKER_ID_BITS) - 1) + " 之间");
        }
        this.workerId = workerId;
    }

    /**
     * 生成唯一标识符，结合时间戳、工作节点 ID 和序列号。
     *
     * @return 表示唯一标识符的字符串
     * @throws RuntimeException 如果系统时钟回拨
     */
    public synchronized String nextUniqueId() {
        long currentTimestamp = System.currentTimeMillis();
        // 确保当前时间戳大于或等于上一个时间戳
        if (currentTimestamp < lastTimestamp) {
            throw new RuntimeException("时钟倒退。拒绝生成 ID");
        }
        // 如果在同一毫秒内生成，递增序列号
        if (currentTimestamp == lastTimestamp) {
            sequence = (sequence + 1) & ((1L << SEQUENCE_BITS) - 1);
            if (sequence == 0) {
                currentTimestamp = waitNextMillis(currentTimestamp);
            }
        } else {
            sequence = 0L;
        }
        lastTimestamp = currentTimestamp;
        // 使用时间戳、工作节点 ID 和序列号生成唯一 ID
        long uniqueId = ((currentTimestamp - EPOCH) << (WORKER_ID_BITS + SEQUENCE_BITS)) | (workerId << SEQUENCE_BITS)
                | sequence;
        // 将时间戳部分格式化为 yyyyMMdd
        String timestamp = Instant.ofEpochMilli(currentTimestamp).atZone(ZoneOffset.UTC).toLocalDate()
                .format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        // 将唯一 ID 部分格式化为 10 位数字
        String uniqueIdStr = String.format("%010d", uniqueId);
        // 拼接时间戳和唯一 ID（截取为 10 位）
        return timestamp + uniqueIdStr.substring(uniqueIdStr.length() - 10);
    }

    /**
     * 等待下一个毫秒，确保生成新的时间戳。
     *
     * @param currentTimestamp 当前时间戳
     * @return 下一个有效时间戳
     */
    private long waitNextMillis(long currentTimestamp) {
        while (currentTimestamp <= lastTimestamp) {
            currentTimestamp = System.currentTimeMillis();
        }
        return currentTimestamp;
    }

    public static void main(String[] args) {
        SnowflakeIdGenerator generator = new SnowflakeIdGenerator(1);
        for (int i = 0; i < 10; i++) {
            String uniqueId = generator.nextUniqueId();
            System.out.println(uniqueId);
        }
    }
}
