package com.gree.mall.manager.config;

import com.gree.mall.manager.constant.Constant;
import com.gree.mall.manager.utils.RedisUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.redis.util.RedisLockRegistry;

import java.time.Instant;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

@Configuration
public class NumericIdGeneratorConfig {

    @Value("${machine.generator.short.id}")
    private int machineId;

    @Autowired
    private RedisLockRegistry redisLockRegistry;

    @Autowired
    private RedisUtil redisUtil;

    public static final long INITIAL_EPOCH = 1704067200000L; // 默认纪元2024-01-01

    @Bean
    public RedisEpochIdGenerator redisEpochIdGenerator() {
        return new RedisEpochIdGenerator(machineId);
    }

    public class RedisEpochIdGenerator {
        private final int machineId;
        private long currentEpoch;
        private long sequence = 0L;
        private long lastTimestamp = -1L;

        // 位分配（总39位）
        private static final int TIMESTAMP_BITS = 26; // 26位时间戳（约2.7年）
        private static final int MACHINE_BITS = 3;    // 3位机器ID（0-7）
        private static final int SEQUENCE_BITS = 10;  // 10位序列号

        private final int maxMachineId = (1 << MACHINE_BITS) - 1;
        private final long maxSequence = (1L << SEQUENCE_BITS) - 1;

        public RedisEpochIdGenerator(int machineId) {
            if (machineId > maxMachineId) {
                throw new IllegalArgumentException("机器ID超过最大值 " + maxMachineId);
            }
            this.machineId = machineId;
            initializeEpoch();
        }

        private void initializeEpoch() {
            Lock obtain = redisLockRegistry.obtain(Constant.RedisPrefix.EPOCH_LOCK_KEY);
            try {
                if (obtain.tryLock(10, TimeUnit.SECONDS)) {
                    Object objStr = redisUtil.get(Constant.RedisPrefix.EPOCH_KEY);
                    String epochStr = Objects.nonNull(objStr) ? objStr.toString() : null;
                    if (epochStr == null) {
                        // Redis数据丢失，重新初始化
                        long newEpoch = calculateNewEpoch();
                        redisUtil.set(Constant.RedisPrefix.EPOCH_KEY, String.valueOf(newEpoch));
                        currentEpoch = newEpoch;
                    } else {
                        currentEpoch = Long.parseLong(epochStr);
                    }
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                throw new RuntimeException("初始化纪元锁中断", e);
            } finally {
                if (Objects.nonNull(obtain)) {
                    obtain.unlock();
                }
            }
        }

        private long calculateNewEpoch() {
            // 如果Redis中找不到纪元，使用当前时间或默认初始纪元
            Object objStr = redisUtil.get(Constant.RedisPrefix.EPOCH_KEY);
            String backupEpoch = Objects.nonNull(objStr) ? objStr.toString() : null;
            return backupEpoch != null ? Long.parseLong(backupEpoch) :
                    Math.max(Instant.now().toEpochMilli(), INITIAL_EPOCH);
        }

        public synchronized long nextId() {
            long timestamp = Instant.now().toEpochMilli();
            checkAndUpdateEpoch(timestamp);

            if (timestamp < lastTimestamp) {
                throw new RuntimeException("时钟回拨 " + (lastTimestamp - timestamp) + "ms");
            }

            if (timestamp == lastTimestamp) {
                sequence = (sequence + 1) & maxSequence;
                if (sequence == 0) {
                    timestamp = tilNextMillis(lastTimestamp);
                }
            } else {
                sequence = 0;
            }

            lastTimestamp = timestamp;
            long adjustedTimestamp = timestamp - currentEpoch;

            return (adjustedTimestamp << (MACHINE_BITS + SEQUENCE_BITS))
                    | (machineId << SEQUENCE_BITS)
                    | sequence;
        }

        private void checkAndUpdateEpoch(long timestamp) {
            long timeSinceEpoch = timestamp - currentEpoch;
            if (timeSinceEpoch >= (1L << TIMESTAMP_BITS)) {
                Lock obtain = redisLockRegistry.obtain(Constant.RedisPrefix.EPOCH_LOCK_KEY);
                try {
                    if (obtain.tryLock(10, TimeUnit.SECONDS)) {
                        // 双重检查锁
                        Object objStr = redisUtil.get(Constant.RedisPrefix.EPOCH_KEY);
                        String strEpoch = Objects.nonNull(objStr) ? objStr.toString() : null;
                        System.out.println("锁epoch: " + strEpoch);
                        long currentEpochNow = Long.parseLong(strEpoch);
                        if (timestamp - currentEpochNow >= (1L << TIMESTAMP_BITS)) {
                            long newEpoch = timestamp;
                            redisUtil.set(Constant.RedisPrefix.EPOCH_KEY, String.valueOf(newEpoch));
                            currentEpoch = newEpoch;
                        }
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("纪元更新锁中断", e);
                } finally {
                    if (Objects.nonNull(obtain)) {
                        obtain.unlock();
                    }
                }
            }
        }

        private long tilNextMillis(long lastTimestamp) {
            long timestamp = Instant.now().toEpochMilli();
            while (timestamp <= lastTimestamp) {
                timestamp = Instant.now().toEpochMilli();
            }
            return timestamp;
        }
    }
}
