package com.lanyuanxiaoyao.server.configuration.database;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.time.Instant;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.annotations.IdGeneratorType;
import org.hibernate.annotations.ValueGenerationType;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * 使用雪花算法作为ID生成器
 *
 * @author lanyuanxiaoyao
 * @date 2024-11-14
 */
@Slf4j
public class SnowflakeId {
  @IdGeneratorType(IdGenerator.class)
  @Retention(RUNTIME)
  @Target({FIELD, METHOD})
  public @interface Generator {
  }

  public static final class IdGenerator implements IdentifierGenerator {
    @Override
    public Object generate(SharedSessionContractImplementor session, Object object) {
      try {
        return Snowflake.next();
      } catch (Exception e) {
        log.error("Generate snowflake id failed", e);
        throw new RuntimeException(e);
      }
    }
  }

  private static class Snowflake {
    /**
     * 起始的时间戳
     */
    private final static long START_TIMESTAMP = 1;

    /**
     * 序列号占用的位数
     */
    private final static long SEQUENCE_BIT = 11;

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

    /**
     * 时间戳值向左位移
     */
    private final static long TIMESTAMP_OFFSET = SEQUENCE_BIT;

    /**
     * 序列号
     */
    private static long sequence = 0;
    /**
     * 上一次时间戳
     */
    private static long lastTimestamp = -1;

    public static synchronized long next() {
      long currentTimestamp = nowTimestamp();
      if (currentTimestamp < lastTimestamp) {
        throw new RuntimeException("Clock have moved backwards.");
      }

      if (currentTimestamp == lastTimestamp) {
        // 相同毫秒内, 序列号自增
        sequence = (sequence + 1) & MAX_SEQUENCE_BIT;
        // 同一毫秒的序列数已经达到最大
        if (sequence == 0) {
          currentTimestamp = nextTimestamp();
        }
      } else {
        // 不同毫秒内, 序列号置为0
        sequence = 0;
      }

      lastTimestamp = currentTimestamp;
      return (currentTimestamp - START_TIMESTAMP) << TIMESTAMP_OFFSET | sequence;
    }

    private static long nextTimestamp() {
      long milli = nowTimestamp();
      while (milli <= lastTimestamp) {
        milli = nowTimestamp();
      }
      return milli;
    }

    private static long nowTimestamp() {
      return Instant.now().toEpochMilli();
    }
  }
}
