import { RedisService } from '../../utils/RedisService';
import { logger } from '../../utils/logger';

/**
 * [BIZ_KER_116] 基于 Redis 的全局 ID 生成器 (SnowflakeIDService)
 * @description 核心逻辑：结合 Redis 原子递增与时间戳生成的 64 位唯一 ID。
 * 结构：[41位时间戳] + [10位节点ID] + [12位自增序列]。
 * 保证在分布式环境下的绝对唯一性与趋势递增。
 */
export class SnowflakeIDService {
  private static readonly NODE_ID_KEY = 'snowflake:node_id';
  private static nodeId: number = 0;
  private static lastTimestamp: number = -1;
  private static sequence: number = 0;

  private static readonly EPOCH = 1609459200000; // 2021-01-01 00:00:00
  private static readonly NODE_ID_BITS = 10;
  private static readonly SEQUENCE_BITS = 12;

  private static readonly MAX_NODE_ID = -1 ^ (-1 << SnowflakeIDService.NODE_ID_BITS);
  private static readonly MAX_SEQUENCE = -1 ^ (-1 << SnowflakeIDService.SEQUENCE_BITS);

  private static readonly NODE_ID_SHIFT = SnowflakeIDService.SEQUENCE_BITS;
  private static readonly TIMESTAMP_SHIFT = SnowflakeIDService.SEQUENCE_BITS + SnowflakeIDService.NODE_ID_BITS;

  /**
   * 初始化节点 ID (从 Redis 动态领取)
   */
  static async init() {
    const redis = RedisService.getClient();
    try {
      // 模拟节点 ID 注册逻辑 (实际应结合集群节点列表)
      const id = await redis.incr(this.NODE_ID_KEY);
      this.nodeId = id % (this.MAX_NODE_ID + 1);
      logger.info(`[SnowflakeID] Node ID registered: ${this.nodeId}`);
    } catch (err: any) {
      logger.error(`[SnowflakeID] Init failed: ${err.message}`);
      this.nodeId = Math.floor(Math.random() * (this.MAX_NODE_ID + 1));
    }
  }

  /**
   * 生成下一个全局唯一 ID
   */
  static async nextId(): Promise<string> {
    let timestamp = Date.now();

    if (timestamp < this.lastTimestamp) {
      throw new Error('Clock moved backwards. Refusing to generate id');
    }

    if (timestamp === this.lastTimestamp) {
      this.sequence = (this.sequence + 1) & this.MAX_SEQUENCE;
      if (this.sequence === 0) {
        // 毫秒内序列溢出，等待下一毫秒
        timestamp = this.waitNextMillis(this.lastTimestamp);
      }
    } else {
      this.sequence = 0;
    }

    this.lastTimestamp = timestamp;

    const id = BigInt(timestamp - this.EPOCH) << BigInt(this.TIMESTAMP_SHIFT) |
               BigInt(this.nodeId) << BigInt(this.NODE_ID_SHIFT) |
               BigInt(this.sequence);

    return id.toString();
  }

  private static waitNextMillis(lastTimestamp: number): number {
    let timestamp = Date.now();
    while (timestamp <= lastTimestamp) {
      timestamp = Date.now();
    }
    return timestamp;
  }
}
