package com.emotion.util;

import lombok.extern.slf4j.Slf4j;

/**
 * 雪花算法ID生成器
 * 生成64位长整型ID，转换为字符串避免前端精度丢失问题
 * 
 * 雪花算法结构：
 * 1位符号位（固定为0） + 41位时间戳 + 10位机器ID + 12位序列号
 * 
 * @author huazhongmin
 * @since 2025-07-23
 */
@Slf4j
public class SnowflakeIdGenerator {

    /**
     * 起始时间戳 (2024-01-01 00:00:00)
     */
    private static final long START_TIMESTAMP = 1704067200000L;

    /**
     * 机器ID位数
     */
    private static final long MACHINE_ID_BITS = 10L;

    /**
     * 序列号位数
     */
    private static final long SEQUENCE_BITS = 12L;

    /**
     * 机器ID最大值
     */
    private static final long MAX_MACHINE_ID = ~(-1L << MACHINE_ID_BITS);

    /**
     * 序列号最大值
     */
    private static final long MAX_SEQUENCE = ~(-1L << SEQUENCE_BITS);

    /**
     * 机器ID左移位数
     */
    private static final long MACHINE_ID_SHIFT = SEQUENCE_BITS;

    /**
     * 时间戳左移位数
     */
    private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS;

    /**
     * 机器ID
     */
    private final long machineId;

    /**
     * 序列号
     */
    private long sequence = 0L;

    /**
     * 上次生成ID的时间戳
     */
    private long lastTimestamp = -1L;

    /**
     * 构造函数
     * 
     * @param machineId 机器ID (0-1023)
     */
    public SnowflakeIdGenerator(long machineId) {
        if (machineId > MAX_MACHINE_ID || machineId < 0) {
            throw new IllegalArgumentException(
                String.format("机器ID必须在0到%d之间", MAX_MACHINE_ID));
        }
        this.machineId = machineId;
        log.info("雪花算法ID生成器初始化完成，机器ID: {}", machineId);
    }

    /**
     * 默认构造函数，使用默认机器ID
     */
    public SnowflakeIdGenerator() {
        // 使用当前时间戳的后10位作为默认机器ID
        this(System.currentTimeMillis() % (MAX_MACHINE_ID + 1));
    }

    /**
     * 生成下一个ID
     * 
     * @return 生成的ID
     */
    public synchronized long nextId() {
        long timestamp = getCurrentTimestamp();

        // 如果当前时间小于上次ID生成的时间戳，说明系统时钟回退过，抛出异常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                String.format("系统时钟回退，拒绝生成ID。当前时间戳: %d, 上次时间戳: %d", 
                             timestamp, lastTimestamp));
        }

        // 如果是同一时间戳，则在序列号上自增
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & MAX_SEQUENCE;
            // 如果序列号溢出，则等待下一个毫秒
            if (sequence == 0) {
                timestamp = getNextTimestamp(lastTimestamp);
            }
        } else {
            // 如果是新的时间戳，则序列号重置为0
            sequence = 0L;
        }

        // 更新上次生成ID的时间戳
        lastTimestamp = timestamp;

        // 移位并通过或运算拼到一起组成64位的ID
        return ((timestamp - START_TIMESTAMP) << TIMESTAMP_SHIFT)
                | (machineId << MACHINE_ID_SHIFT)
                | sequence;
    }

    /**
     * 生成字符串格式的ID
     * 
     * @return 字符串格式的ID
     */
    public String nextIdAsString() {
        return String.valueOf(nextId());
    }

    /**
     * 获取当前时间戳
     * 
     * @return 当前时间戳
     */
    private long getCurrentTimestamp() {
        return System.currentTimeMillis();
    }

    /**
     * 获取下一个时间戳
     * 
     * @param lastTimestamp 上次时间戳
     * @return 下一个时间戳
     */
    private long getNextTimestamp(long lastTimestamp) {
        long timestamp = getCurrentTimestamp();
        while (timestamp <= lastTimestamp) {
            timestamp = getCurrentTimestamp();
        }
        return timestamp;
    }

    /**
     * 解析ID获取时间戳
     * 
     * @param id 雪花算法生成的ID
     * @return 时间戳
     */
    public long parseTimestamp(long id) {
        return (id >> TIMESTAMP_SHIFT) + START_TIMESTAMP;
    }

    /**
     * 解析ID获取机器ID
     * 
     * @param id 雪花算法生成的ID
     * @return 机器ID
     */
    public long parseMachineId(long id) {
        return (id >> MACHINE_ID_SHIFT) & MAX_MACHINE_ID;
    }

    /**
     * 解析ID获取序列号
     * 
     * @param id 雪花算法生成的ID
     * @return 序列号
     */
    public long parseSequence(long id) {
        return id & MAX_SEQUENCE;
    }

    /**
     * 获取机器ID
     * 
     * @return 机器ID
     */
    public long getMachineId() {
        return machineId;
    }

    /**
     * 批量生成ID
     * 
     * @param count 生成数量
     * @return ID数组
     */
    public long[] nextIds(int count) {
        if (count <= 0) {
            throw new IllegalArgumentException("生成数量必须大于0");
        }
        
        long[] ids = new long[count];
        for (int i = 0; i < count; i++) {
            ids[i] = nextId();
        }
        return ids;
    }

    /**
     * 批量生成字符串格式的ID
     * 
     * @param count 生成数量
     * @return 字符串ID数组
     */
    public String[] nextIdsAsString(int count) {
        if (count <= 0) {
            throw new IllegalArgumentException("生成数量必须大于0");
        }
        
        String[] ids = new String[count];
        for (int i = 0; i < count; i++) {
            ids[i] = nextIdAsString();
        }
        return ids;
    }
}
