use chrono::{DateTime, Duration, Local, NaiveDate, NaiveDateTime, ParseError, TimeZone, Utc};
use std::cmp::Ordering;
use std::time::{SystemTime, UNIX_EPOCH};

/// Date tools, including common quick generation functions such as obtaining the current day's time string, formatting, time string parsing, etc.
///
/// chrono时间关系图，见 <img src="../docs/chrono.png"></img>
pub struct DateUtil;

impl DateUtil {
    pub fn current_date() -> NaiveDate {
        // 获取当前本地时间
        let now = Local::now();
        now.date_naive()
    }

    /// 获取当前日期字符串，输出格式：%Y-%m-%d
    ///
    pub fn current_date_string() -> String {
        let now = Local::now();
        now.format("%Y-%m-%d").to_string()
    }

    pub fn current_datetime() -> NaiveDateTime {
        // 获取当前本地时间
        let now = Local::now();
        now.naive_local()
    }

    pub fn current_datetime_string() -> String {
        // 获取当前本地时间
        let now = Local::now();
        now.format("%Y-%m-%d %H:%M:%S").to_string()
    }

    /// 获取当前日期的字符串表示
    ///
    /// 该函数获取本地当前时间，并将其格式化为"年-月-日"的字符串格式。
    ///
    /// # 返回值
    /// 返回格式为"YYYY-MM-DD"的日期字符串
    pub fn today_string() -> String {
        let now = Local::now();
        now.format("%Y-%m-%d").to_string()
    }

    pub fn today_date() -> NaiveDate {
        // 获取当前本地时间
        let now = Local::now();
        now.date_naive()
    }

    pub fn parse_day_string(day_string: &str) -> Result<NaiveDate, ParseError> {
        NaiveDate::parse_from_str(day_string, "%Y-%m-%d")
    }

    /// 进行日期偏移，offset为偏移量，正数表示向后偏移，负数表示向前偏移
    ///
    /// # 返回值
    /// 返回格式为"YYYY-MM-DD"的日期字符串
    pub fn day_offset(offset: i64) -> String {
        let now: DateTime<Local> = Local::now();
        let duration = Duration::days(offset);
        let now = now + duration;
        now.format("%Y-%m-%d").to_string()
    }

    /// 获取昨天日期的字符串表示
    ///
    /// 该函数计算昨天的日期，并将其格式化为"YYYY-MM-DD"格式的字符串。
    ///
    /// # Returns
    ///
    /// 返回昨天日期的字符串表示，格式为"YYYY-MM-DD"
    ///
    /// # Example
    ///
    /// ```
    /// let yesterday = yesterday_string();
    /// println!("{}", yesterday); // 输出类似 "2023-12-06"
    /// ```
    pub fn yesterday_string() -> String {
        // 获取当前本地时间
        let now = Local::now();
        // 计算昨天的时间
        let yesterday = now - Duration::days(1);
        // 将昨天的日期格式化为字符串并返回
        yesterday.format("%Y-%m-%d").to_string()
    }

    pub fn yesterday_date() -> NaiveDate {
        // 获取当前本地时间
        let now = Local::now();
        // 获取昨天的日期
        let yesterday = now - Duration::days(1);
        // 返回昨天的日期
        yesterday.date_naive()
    }

    /// 获取明天日期的字符串表示
    ///
    /// 该函数计算明天的日期，并将其格式化为"YYYY-MM-DD"格式的字符串。
    ///
    /// # 返回值
    /// 返回表示明天日期的字符串，格式为"YYYY-MM-DD"
    pub fn tomorrow_string() -> String {
        // 获取当前本地时间
        let now = Local::now();
        // 计算明天的时间
        let tomorrow = now + Duration::days(1);
        // 将明天的日期格式化为字符串并返回
        tomorrow.format("%Y-%m-%d").to_string()
    }

    pub fn tomorrow_date() -> NaiveDate {
        // 获取当前本地时间
        let now = Local::now();
        // 获取昨天的日期
        let tomorrow = now + Duration::days(1);
        // 返回昨天的日期
        tomorrow.date_naive()
    }

    /// 比较两个时间字符串的大小
    ///
    /// 该函数将两个时间字符串按照指定的格式解析为DateTime对象，然后进行比较。
    ///
    /// # 参数
    /// * `time1` - 第一个时间字符串
    /// * `time2` - 第二个时间字符串
    /// * `fmt` - 时间格式字符串，用于解析time1和time2
    ///
    /// # 返回值
    /// * `Ok(Ordering)` - 比较结果，Ordering::Less表示time1小于time2，
    ///                    Ordering::Equal表示time1等于time2，
    ///                    Ordering::Greater表示time1大于time2
    /// * `Err(ParseError)` - 时间解析失败时返回错误信息
    ///
    /// # 错误处理
    /// 如果任一时间字符串无法按照指定格式解析，函数将返回ParseError错误
    pub fn cmp_string(time1: &str, time2: &str, fmt: &str) -> Result<Ordering, ParseError> {
        // 将两个时间字符串转换为DateTime对象，然后进行比较大小
        let naive1 = NaiveDateTime::parse_from_str(time1, fmt)?;
        let naive2 = NaiveDateTime::parse_from_str(time2, fmt)?;
        let datetime1: DateTime<Utc> = Utc.from_utc_datetime(&naive1);
        let datetime2: DateTime<Utc> = Utc.from_utc_datetime(&naive2);
        let c = datetime1.cmp(&datetime2);
        Ok(c)
    }

    /// 获取当前时间字符串，输出格式：%Y-%m-%d %H:%M:%S
    ///
    pub fn current_time_string() -> String {
        let now = Local::now();
        now.format("%Y-%m-%d %H:%M:%S").to_string()
    }

    /// # 参数
    /// - time: 格式如："2025-08-30 12:00:00"
    pub fn parse_datetime(time: &str) -> Result<NaiveDateTime, chrono::ParseError> {
        let res = NaiveDateTime::parse_from_str(time, "%Y-%m-%d %H:%M:%S")?;
        Ok(res)
    }

    pub fn parse_string(time: &str, fmt: &str) -> Result<NaiveDateTime, chrono::ParseError> {
        let res = NaiveDateTime::parse_from_str(time, fmt)?;
        Ok(res)
    }

    /// 当前时间戳（秒），返回10位长度
    pub fn current_timestamp() -> u64 {
        let time = Local::now();
        DateUtil::timestamp(time)
    }

    /// 返回10位时间戳（秒）
    pub fn timestamp(time: DateTime<Local>) -> u64 {
        let system_time: SystemTime = time.into();
        let duration = system_time.duration_since(UNIX_EPOCH).unwrap();
        let timestamp = duration.as_secs();
        timestamp
    }

    /// 时间戳加减法（秒）
    pub fn timestamp_add(time: DateTime<Local>, seconds: i64) -> u64 {
        let diff = Duration::seconds(seconds);
        let time = time + diff;
        DateUtil::timestamp(time)
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_current_time_string() {
        let timestring = DateUtil::current_time_string();
        println!("timestring = {}", timestring);
    }

    #[test]
    fn test_current_timestamp() {
        let ts = DateUtil::current_timestamp();
        println!("ts = {}", ts);
    }

    #[test]
    fn test_parse_datetime() {
        let x = DateUtil::parse_datetime("2025-08-30 12:00:00");
        println!("{:?}", x);
    }

    #[test]
    fn test_cmp_string() {
        let c = DateUtil::cmp_string(
            "2025-08-30 13:00:00",
            "2025-08-30 12:00:01",
            "%Y-%m-%d %H:%M:%S",
        );
        println!("{:?}", c);
    }
}
