package com.clx.performance.component;

import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.clx.performance.constant.RedisConstants;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 雪花算法生成唯一标示
 * datacenterId：根据服务的ip地址区分，当前最多支持32太机器
 * channelConfTypeId： 用于区分不同渠道的消息标示 用了6位的二进制位标示
 * 从17位~23位 可看当前消息类型
 */
@Component
public class IdGenerateSnowFlake implements InitializingBean {

    @Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    //时间 41位
    private static long lastTime = System.currentTimeMillis();

    //数据中心ID 5位(默认0-31) 通过nacos获取服务ip注册redis统计 %32
    private long datacenterId = 0;
    private long datacenterIdShift = 4;

    private long channelConfTypeId = 0;

    private long channelConfTypeIdShirt = 6;

//    机房机器ID 5位(默认0-31)
//    private long workerId = 0;
//    private long workerIdShift = 5;

    //随机数 12位(默认0~4095)
    private AtomicLong random = new AtomicLong();
    private long randomShift = 12;
    //随机数的最大值
    private long maxRandom = (long) Math.pow(2, randomShift);


//    public SnowFlake(long workerIdShift, long datacenterIdShift){
//        if (workerIdShift < 0 ||
//                datacenterIdShift < 0 ||
//                workerIdShift + datacenterIdShift > 22) {
//            throw new IllegalArgumentException("参数不匹配");
//        }
//        this.workerIdShift = workerIdShift;
//        this.datacenterIdShift = datacenterIdShift;
//        this.randomShift = 22 - datacenterIdShift - workerIdShift;
//        this.maxRandom = (long) Math.pow(2, randomShift);
//    }

    //获取雪花的ID
    private long getId(Long channelConfTypeId) {
        return lastTime << (channelConfTypeIdShirt + datacenterIdShift + randomShift) |
                channelConfTypeId << (datacenterIdShift + randomShift) |
                datacenterId << randomShift |
                random.get();
    }

    //生成一个新的ID
    public synchronized long nextId(Long channelConfTypeId) {
        long now = System.currentTimeMillis();

        //如果当前时间和上一次时间不在同一毫秒内，直接返回
        if (now > lastTime) {
            lastTime = now;
            random.set(0);
            return getId(channelConfTypeId);
        }

        //将最后的随机数，进行+1操作
        if (random.incrementAndGet() < maxRandom) {
            return getId(channelConfTypeId);
        }

        //自选等待下一毫秒
        while (now <= lastTime) {
            now = System.currentTimeMillis();
        }

        lastTime = now;
        random.set(0);
        return getId(channelConfTypeId);

    }

    public synchronized String nextIdToString(Long channelConfTypeId) {
        long now = System.currentTimeMillis();

        //如果当前时间和上一次时间不在同一毫秒内，直接返回
        if (now > lastTime) {
            lastTime = now;
            random.set(0);
            return getId(channelConfTypeId) + "";
        }

        //将最后的随机数，进行+1操作
        if (random.incrementAndGet() < maxRandom) {
            return getId(channelConfTypeId) + "";
        }

        //自选等待下一毫秒
        while (now <= lastTime) {
            now = System.currentTimeMillis();
        }

        lastTime = now;
        random.set(0);
        return getId(channelConfTypeId) + "";

    }

    /**
     * 获取channelType
     * @param n
     * @return
     */
    public Integer getChannelType(Long n){
        String binaryString = Long.toBinaryString(n);
        String substring = binaryString.substring(41, 47);
        return Integer.parseInt(substring,2);
    }

    //测试
    public static void main(String[] args) {
        IdGenerateSnowFlake msgSnowFlake = new IdGenerateSnowFlake();
        String s1 = msgSnowFlake.nextIdToString(0L);
        String s2 = msgSnowFlake.nextIdToString(1L);
        String s3 = msgSnowFlake.nextIdToString(2L);
        String s4 = msgSnowFlake.nextIdToString(3L);
        String s5 = msgSnowFlake.nextIdToString(4L);

        Integer channelType = msgSnowFlake.getChannelType(Long.valueOf(s1));
        Integer channelType1 = msgSnowFlake.getChannelType(Long.valueOf(s2));
        Integer channelType2 = msgSnowFlake.getChannelType(Long.valueOf(s3));
        Integer channelType3 = msgSnowFlake.getChannelType(Long.valueOf(s4));
        Integer channelType4 = msgSnowFlake.getChannelType(Long.valueOf(s5));

        System.out.println(channelType1);
    }

    /**
     * 根据不同的ip注册机器ID，用于保证ID全局唯一
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        Map<Object, Object> msgSnowFlake = redisTemplate.opsForHash().entries(RedisConstants.ID_SNOWFLAKE);
        String ip = nacosDiscoveryProperties.getIp();
        datacenterId = msgSnowFlake.size() % 32;
        redisTemplate.opsForHash().put(RedisConstants.ID_SNOWFLAKE, ip, String.valueOf(msgSnowFlake.size() + 1));
    }
}