#include "snowflake.h"
#include <stdexcept>

namespace zsy
{
    /**
     * @brief 构造函数，初始化 Snowflake 实例。
     *
     * @param workerId 工作机器 ID，取值范围为 0 到 kMaxWorkerId。
     * @param datacenterId 数据中心 ID，取值范围为 0 到 kMaxDatacenterId。
     * @param epoch 时间戳的起始点，用于计算相对时间戳。
     *
     * @throws std::invalid_argument 当 workerId 或 datacenterId 超出允许范围时抛出。
     */
    Snowflake::Snowflake(uint64_t workerId, uint64_t datacenterId, TimePoint epoch)
        : workerId(workerId), datacenterId(datacenterId), epoch(epoch)
    {
        // 检查 workerId 是否超出最大允许值
        if (workerId > MaxWorkerId)
        {
            // 若超出范围，抛出异常提示 workerId 必须在 0 到 kMaxWorkerId 之间
            throw std::invalid_argument("机器 Id 必须为 0 ~ " + std::to_string(MaxWorkerId));
        }
        // 检查 datacenterId 是否超出最大允许值
        if (datacenterId > MaxDatacenterId)
        {
            // 若超出范围，抛出异常提示 datacenterId 必须在 0 到 kMaxDatacenterId 之间
            throw std::invalid_argument("数据中心 Id 必须为 0 ~ " + std::to_string(MaxDatacenterId));
        }
    }

    /**
     * @brief 生成下一个唯一 ID。
     *
     * 该方法使用雪花算法生成一个 64 位的唯一 ID，包含时间戳、数据中心 ID、工作机器 ID 和序列号。
     * 使用互斥锁保证线程安全，处理时钟回拨问题。
     *
     * @return uint64_t 生成的唯一 ID。
     * @throws std::runtime_error 当发生时钟回拨时抛出。
     */
    uint64_t Snowflake::nextId()
    {
        // 使用互斥锁保证线程安全，确保同一时间只有一个线程能生成 ID
        std::lock_guard<std::mutex> lock(idMutex);

        // 获取当前时间戳（相对于 epoch 的毫秒数）
        uint64_t currentTime = currentTimestamp();

        // 处理时钟回拨问题
        if (currentTime < lastTimestamp)
        {
            // 若当前时间戳小于上一次生成 ID 的时间戳，说明发生了时钟回拨
            // 抛出异常并提示时钟回拨的毫秒数
            // 计算时间差（毫秒）
            uint64_t diff = lastTimestamp - currentTime;
            // 转换为更友好的时间格式
            uint64_t days = diff / (1000 * 60 * 60 * 24);
            uint64_t hours = (diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60);
            uint64_t minutes = (diff % (1000 * 60 * 60)) / (1000 * 60);
            uint64_t seconds = (diff % (1000 * 60)) / 1000;
            uint64_t milliseconds = diff % 1000;

            throw std::runtime_error(
                "检测到时钟回拨！拒绝生成ID。回拨时间: " +
                std::to_string(days) + "天" +
                std::to_string(hours) + "小时" +
                std::to_string(minutes) + "分钟" +
                std::to_string(seconds) + "秒" +
                std::to_string(milliseconds) + "毫秒"
            );
        }

        // 若当前时间戳与上一次生成 ID 的时间戳相同
        if (currentTime == lastTimestamp)
        {
            // 序列号加 1 并通过按位与操作确保序列号在允许范围内
            sequence = ++sequence & MaxSequence;
            if (sequence == 0)
            {
                currentTime = waitNextMillis(lastTimestamp);
            }
        } else
        {
            sequence = 0;
        }

        lastTimestamp = currentTime;

        return (currentTime << TimestampShift) |
               (datacenterId << DatacenterIdShift) |
               (workerId << WorkerIdShift) |
               sequence;
    }

    std::string Snowflake::nextIdStr()
    {
        return std::to_string(nextId());
    }

    uint64_t Snowflake::getTimestamp() const
    {
        return currentTimestamp();
    }

    Snowflake::TimePoint Snowflake::extractTimePoint(uint64_t id, TimePoint epoch)
    {
        uint64_t timestamp = (id >> TimestampShift);
        return epoch + std::chrono::milliseconds{timestamp};
    }

    uint64_t Snowflake::currentTimestamp() const
    {
        auto now = std::chrono::system_clock::now();
        auto ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now);
        return std::chrono::duration_cast<std::chrono::milliseconds>(ms - epoch).count();
    }

    uint64_t Snowflake::waitNextMillis(uint64_t lastTimestamp) const
    {
        uint64_t timestamp;
        do
        {
            timestamp = currentTimestamp();
        } while (timestamp <= lastTimestamp);

        return timestamp;
    }
} // namespace utils
