//! 项目和构建时间追踪模块
//!
//! 提供跨平台的时间基准点定义和优雅的时间差显示功能。
//! 支持项目启动时间（按天计算）和构建时间（按分钟计算）的追踪。

use std::fmt;

pub use super::build::BUILD_EPOCH;

/// Unix 系统的时间基准点（2024-12-23 01:30:48 UTC）
#[cfg(unix)]
pub const EPOCH: std::time::SystemTime = unsafe {
    #[allow(dead_code)]
    struct UnixSystemTime {
        tv_sec: i64,
        tv_nsec: u32,
    }

    ::core::intrinsics::transmute_unchecked(UnixSystemTime {
        tv_sec: 1734915448,
        tv_nsec: 0,
    })
};

/// Windows 系统的时间基准点（2024-12-23 01:30:48 UTC）
#[cfg(windows)]
pub const EPOCH: std::time::SystemTime = unsafe {
    #[allow(dead_code)]
    struct WindowsFileTime {
        dw_low_date_time: u32,
        dw_high_date_time: u32,
    }

    const INTERVALS_PER_SEC: u64 = 10_000_000;
    const INTERVALS_TO_UNIX_EPOCH: u64 = 11_644_473_600 * INTERVALS_PER_SEC;
    const TARGET_INTERVALS: u64 = INTERVALS_TO_UNIX_EPOCH + 1734915448 * INTERVALS_PER_SEC;

    ::core::intrinsics::transmute_unchecked(WindowsFileTime {
        dw_low_date_time: TARGET_INTERVALS as u32,
        dw_high_date_time: (TARGET_INTERVALS >> 32) as u32,
    })
};

/// 打印项目启动以来的时间
///
/// 以年、月、日的形式显示项目运行时长
pub fn print_project_age() {
    let age = ProjectAge::since_epoch();
    println!("Project started {age} ago");
}

/// 打印程序构建以来的时间
///
/// 以分钟级精度显示构建时长，包含友好的时间描述
pub fn print_build_age() {
    let age = BuildAge::since_build();
    println!("Program built {age} ago");
}

/// 项目年龄表示，以天为最小单位
///
/// 用于表示较长时间跨度，适合项目生命周期追踪
#[derive(Debug, Clone, Copy)]
struct ProjectAge {
    years: u64,
    months: u64,
    days: u64,
}

impl ProjectAge {
    /// 计算自项目 EPOCH 以来的时间
    ///
    /// # Panics
    ///
    /// 如果系统时间早于项目 EPOCH 时间则会 panic
    #[inline]
    pub fn since_epoch() -> Self {
        let duration = std::time::SystemTime::now()
            .duration_since(EPOCH)
            .expect("system time before program epoch");

        Self::from_days(duration.as_secs() / 86400)
    }

    /// 从总天数创建 ProjectAge 实例
    ///
    /// 使用简化的时间计算：
    /// - 1年 = 365天
    /// - 1月 = 30天
    /// - 包含基本的闰年调整
    #[inline]
    fn from_days(total_days: u64) -> Self {
        if total_days == 0 {
            return Self {
                years: 0,
                months: 0,
                days: 0,
            };
        }

        let years = total_days / 365;
        let remaining_days = total_days % 365;

        // 简单的闰年调整
        let leap_adjustment = years / 4;
        let adjusted_days = remaining_days.saturating_sub(leap_adjustment);

        let months = adjusted_days / 30;
        let days = adjusted_days % 30;

        Self {
            years,
            months,
            days,
        }
    }
}

impl fmt::Display for ProjectAge {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match (self.years, self.months, self.days) {
            (0, 0, 0) => write!(f, "less than 1 day"),
            (0, 0, d) => write!(f, "{d} day{}", if d == 1 { "" } else { "s" }),
            (0, m, 0) => write!(f, "{m} month{}", if m == 1 { "" } else { "s" }),
            (0, m, d) => write!(
                f,
                "{m} month{} and {d} day{}",
                if m == 1 { "" } else { "s" },
                if d == 1 { "" } else { "s" }
            ),
            (y, 0, 0) => write!(f, "{y} year{}", if y == 1 { "" } else { "s" }),
            (y, 0, d) => write!(
                f,
                "{y} year{} and {d} day{}",
                if y == 1 { "" } else { "s" },
                if d == 1 { "" } else { "s" }
            ),
            (y, m, 0) => write!(
                f,
                "{y} year{} and {m} month{}",
                if y == 1 { "" } else { "s" },
                if m == 1 { "" } else { "s" }
            ),
            (y, m, d) => write!(
                f,
                "{y} year{}, {m} month{}, and {d} day{}",
                if y == 1 { "" } else { "s" },
                if m == 1 { "" } else { "s" },
                if d == 1 { "" } else { "s" }
            ),
        }
    }
}

/// 构建年龄表示，以分钟为最小单位
///
/// 用于表示较短时间跨度，适合构建时间追踪，提供友好的时间描述
#[derive(Debug, Clone, Copy)]
struct BuildAge {
    minutes: u64,
}

impl BuildAge {
    /// 计算自构建 EPOCH 以来的时间
    ///
    /// # Panics
    ///
    /// 如果系统时间早于构建 EPOCH 时间则会 panic
    #[inline]
    pub fn since_build() -> Self {
        let duration = std::time::SystemTime::now()
            .duration_since(BUILD_EPOCH)
            .expect("system time before build epoch");

        Self {
            minutes: duration.as_secs() / 60,
        }
    }
}

impl fmt::Display for BuildAge {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self.minutes {
            0 => write!(f, "just now"),
            1 => write!(f, "1 minute"),
            m if m < 60 => write!(f, "{m} minutes"),
            m if m < 120 => {
                let mins = m % 60;
                if mins == 0 {
                    write!(f, "1 hour")
                } else {
                    write!(
                        f,
                        "1 hour and {mins} minute{}",
                        if mins == 1 { "" } else { "s" }
                    )
                }
            }
            m if m < 1440 => {
                let hours = m / 60;
                let mins = m % 60;
                if mins == 0 {
                    write!(f, "{hours} hour{}", if hours == 1 { "" } else { "s" })
                } else {
                    write!(
                        f,
                        "{hours} hour{} and {mins} minute{}",
                        if hours == 1 { "" } else { "s" },
                        if mins == 1 { "" } else { "s" }
                    )
                }
            }
            m => {
                let days = m / 1440;
                let remaining_hours = (m % 1440) / 60;
                if remaining_hours == 0 {
                    write!(f, "{days} day{}", if days == 1 { "" } else { "s" })
                } else {
                    write!(
                        f,
                        "{days} day{} and {remaining_hours} hour{}",
                        if days == 1 { "" } else { "s" },
                        if remaining_hours == 1 { "" } else { "s" }
                    )
                }
            }
        }
    }
}

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

    #[test]
    fn test_project_age_display() {
        let age = ProjectAge {
            years: 0,
            months: 0,
            days: 0,
        };
        assert_eq!(format!("{}", age), "less than 1 day");

        let age = ProjectAge {
            years: 1,
            months: 2,
            days: 3,
        };
        assert_eq!(format!("{}", age), "1 year, 2 months, and 3 days");

        let age = ProjectAge {
            years: 2,
            months: 1,
            days: 1,
        };
        assert_eq!(format!("{}", age), "2 years, 1 month, and 1 day");
    }

    #[test]
    fn test_build_age_display() {
        let age = BuildAge { minutes: 0 };
        assert_eq!(format!("{}", age), "just now");

        let age = BuildAge { minutes: 1 };
        assert_eq!(format!("{}", age), "1 minute");

        let age = BuildAge { minutes: 65 };
        assert_eq!(format!("{}", age), "1 hour and 5 minutes");

        let age = BuildAge { minutes: 120 };
        assert_eq!(format!("{}", age), "2 hours");
    }
}
