use rand::Rng;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::SystemTime;
use uuid::Uuid;

// 数据中心ID和机器ID
const DATA_CENTER_ID: u16 = 0;
const MACHINE_ID: u16 = 0;

// 时间戳左移22位 (10 + 12)
const TIMESTAMP_LEFT_SHIFT: u64 = 22;
// 数据中心ID左移17位 (5 + 12)
const DATA_CENTER_ID_SHIFT: u64 = 17;
// 机器ID左移12位
const MACHINE_ID_SHIFT: u64 = 12;
// 序列号掩码，12位全为1
const SEQUENCE_MASK: u64 = 0xfff;

// 上次生成ID的时间戳
static LAST_TIMESTAMP: AtomicU64 = AtomicU64::new(0);
// 序列号
static SEQUENCE: AtomicU64 = AtomicU64::new(0);

/// ID tool, used for generating IDs, including UUID and snowflake algorithm
pub struct IdUtil;

impl IdUtil {
    /// 生成UUID
    pub fn gen_uuid() -> String {
        Uuid::new_v4().to_string()
    }

    /// 生成UUID，不带中横线，共计32位
    pub fn uuid32() -> String {
        Uuid::new_v4().to_string().replace("-", "")
    }

    /// 生成UUID，带中横线，共计36位
    pub fn uuid36() -> String {
        Uuid::new_v4().to_string()
    }

    /// 生成指定长度的随机字符串ID
    ///
    /// 该函数使用字母和数字字符集生成随机字符串，适用于生成唯一标识符
    ///
    /// # 参数
    /// * `length` - 指定生成字符串的长度
    ///
    /// # 返回值
    /// 返回一个包含随机字符的String，长度为指定的length
    ///
    /// # 示例
    /// ```
    /// let id = random_string_id(128);
    /// assert_eq!(id.len(), 128);
    /// ```
    pub fn random_string_id(length: usize) -> String {
        let str_chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        let mut rng = rand::thread_rng();
        let mut result = String::with_capacity(length);
        // 循环生成指定长度的随机字符串
        for _ in 0..length {
            let index = rng.gen_range(0..str_chars.len());
            result.push(str_chars.chars().nth(index).unwrap());
        }
        result
    }

    /// 生成一个唯一的 tik ID 字符串
    ///
    /// 该函数通过组合三个随机字符串来创建一个格式化的唯一标识符。
    /// 生成的格式为: "{2位随机字符串}_{14位随机字符串}_{16位随机字符串}__"
    ///
    /// # 返回值
    /// * `String` - 返回生成的唯一 tik ID 字符串
    ///
    /// # 示例
    /// ```
    /// let id = tik_id();
    /// // 示例输出: "ab_cdefghijklmn_opqrstuvwxyzabcdefg__"
    /// ```
    pub fn tik_id() -> String {
        format!(
            "{}_{}_{}__",
            Self::random_string_id(2),
            Self::random_string_id(14),
            Self::random_string_id(16)
        )
    }

    /// 使用雪花算法生成ID
    ///
    /// 1位0 + 41位时间戳 + 10位机器ID(5位数据中心ID + 5位机器ID) + 12位序列号 = 64位二进制数字
    pub fn snowflake_id() -> u64 {
        let mut timestamp = Self::get_current_timestamp();

        // 如果当前时间小于上一次生成ID的时间戳，说明系统时钟回退过，抛出异常
        let last_timestamp = LAST_TIMESTAMP.load(Ordering::Relaxed);
        if timestamp < last_timestamp {
            panic!(
                "Clock moved backwards. Refusing to generate id for {} milliseconds",
                last_timestamp - timestamp
            );
        }

        // 如果是同一毫秒内生成的ID，则序列号递增
        if timestamp == last_timestamp {
            let sequence = SEQUENCE.fetch_add(1, Ordering::Relaxed) & SEQUENCE_MASK;
            // 如果序列号溢出，等待下一毫秒
            if sequence == 0 {
                timestamp = Self::til_next_millis(last_timestamp);
            }
        } else {
            // 不同毫秒内，序列号重置为0
            SEQUENCE.store(0, Ordering::Relaxed);
        }

        // 记录上一次生成ID的时间戳
        LAST_TIMESTAMP.store(timestamp, Ordering::Relaxed);

        // 生成雪花ID
        (timestamp << TIMESTAMP_LEFT_SHIFT)
            | ((DATA_CENTER_ID as u64) << DATA_CENTER_ID_SHIFT)
            | ((MACHINE_ID as u64) << MACHINE_ID_SHIFT)
            | SEQUENCE.load(Ordering::Relaxed)
    }

    fn get_current_timestamp() -> u64 {
        SystemTime::now()
            .duration_since(SystemTime::UNIX_EPOCH)
            .expect("Time went backwards")
            .as_millis() as u64
    }

    fn til_next_millis(last_timestamp: u64) -> u64 {
        let mut timestamp = Self::get_current_timestamp();
        while timestamp <= last_timestamp {
            timestamp = Self::get_current_timestamp();
        }
        timestamp
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_uuid() {
        let uuid = IdUtil::uuid32();
        println!("{}", uuid);
        let uuid = IdUtil::uuid36();
        println!("{}", uuid);
    }

    #[test]
    fn test_random_string_id() {
        let id = IdUtil::random_string_id(128);
        println!("{}", id)
    }

    #[test]
    fn test_snowflake_id() {
        let id = IdUtil::snowflake_id();
        println!("{}", id);
        println!("{:b}", id);
    }
}
