//! Date utilities for Veza services
//! 
//! This module provides utility functions for date and time manipulation,
//! formatting, and parsing.

use chrono::{DateTime, Utc, Local, TimeZone, NaiveDateTime, NaiveDate};
use crate::error::{CommonError, CommonResult};

/// Format a timestamp (seconds since epoch) to a string
/// 
/// # Examples
/// ```
/// use veza_common::utils::date::format_timestamp;
/// 
/// let timestamp = 1609459200; // 2021-01-01 00:00:00 UTC
/// let formatted = format_timestamp(timestamp);
/// assert!(formatted.contains("2021"));
/// ```
pub fn format_timestamp(ts: i64) -> String {
    match Utc.timestamp_opt(ts, 0) {
        chrono::LocalResult::Single(dt) => dt.format("%Y-%m-%d %H:%M:%S UTC").to_string(),
        _ => "Invalid timestamp".to_string(),
    }
}

/// Format a timestamp to ISO 8601 format
/// 
/// # Examples
/// ```
/// use veza_common::utils::date::format_timestamp_iso;
/// 
/// let timestamp = 1609459200; // 2021-01-01 00:00:00 UTC
/// let formatted = format_timestamp_iso(timestamp);
/// assert!(formatted.contains("2021-01-01"));
/// ```
pub fn format_timestamp_iso(ts: i64) -> String {
    match Utc.timestamp_opt(ts, 0) {
        chrono::LocalResult::Single(dt) => dt.to_rfc3339(),
        _ => "Invalid timestamp".to_string(),
    }
}

/// Format a DateTime to a string with custom format
/// 
/// # Examples
/// ```
/// use veza_common::utils::date::format_datetime;
/// use chrono::{DateTime, Utc};
/// 
/// let dt = Utc::now();
/// let formatted = format_datetime(&dt, "%Y-%m-%d %H:%M:%S");
/// assert!(formatted.len() > 0);
/// ```
pub fn format_datetime(dt: &DateTime<Utc>, format: &str) -> String {
    dt.format(format).to_string()
}

/// Format a DateTime to ISO 8601 format
/// 
/// # Examples
/// ```
/// use veza_common::utils::date::format_datetime_iso;
/// use chrono::{DateTime, Utc};
/// 
/// let dt = Utc::now();
/// let formatted = format_datetime_iso(&dt);
/// assert!(formatted.contains("T"));
/// ```
pub fn format_datetime_iso(dt: &DateTime<Utc>) -> String {
    dt.to_rfc3339()
}

/// Format a DateTime to a relative time string (e.g., "2 hours ago", "3 days ago")
/// 
/// # Examples
/// ```
/// use veza_common::utils::date::format_relative_time;
/// use chrono::{DateTime, Utc, Duration};
/// 
/// let now = Utc::now();
/// let past = now - Duration::hours(2);
/// let relative = format_relative_time(&past, &now);
/// assert!(relative.contains("2"));
/// ```
pub fn format_relative_time(dt: &DateTime<Utc>, reference: &DateTime<Utc>) -> String {
    let diff = reference.signed_duration_since(*dt);
    
    if diff.num_seconds() < 60 {
        "just now".to_string()
    } else if diff.num_minutes() < 60 {
        let mins = diff.num_minutes();
        format!("{} minute{} ago", mins, if mins == 1 { "" } else { "s" })
    } else if diff.num_hours() < 24 {
        let hours = diff.num_hours();
        format!("{} hour{} ago", hours, if hours == 1 { "" } else { "s" })
    } else if diff.num_days() < 30 {
        let days = diff.num_days();
        format!("{} day{} ago", days, if days == 1 { "" } else { "s" })
    } else if diff.num_days() < 365 {
        let months = diff.num_days() / 30;
        format!("{} month{} ago", months, if months == 1 { "" } else { "s" })
    } else {
        let years = diff.num_days() / 365;
        format!("{} year{} ago", years, if years == 1 { "" } else { "s" })
    }
}

/// Parse a date string to a DateTime<Utc>
/// 
/// Supports multiple formats including ISO 8601, RFC 3339, and common formats.
/// 
/// # Examples
/// ```
/// use veza_common::utils::date::parse_date;
/// 
/// let date_str = "2021-01-01T00:00:00Z";
/// let dt = parse_date(date_str).unwrap();
/// assert_eq!(dt.year(), 2021);
/// ```
pub fn parse_date(s: &str) -> CommonResult<DateTime<Utc>> {
    // Try ISO 8601 / RFC 3339 first
    if let Ok(dt) = DateTime::parse_from_rfc3339(s) {
        return Ok(dt.with_timezone(&Utc));
    }
    
    // Try common formats
    let formats = [
        "%Y-%m-%d %H:%M:%S",
        "%Y-%m-%dT%H:%M:%S",
        "%Y-%m-%dT%H:%M:%S%.f",
        "%Y-%m-%d",
        "%d/%m/%Y",
        "%m/%d/%Y",
    ];
    
    for format in &formats {
        if let Ok(naive_dt) = NaiveDateTime::parse_from_str(s, format) {
            return Ok(naive_dt.and_utc());
        }
    }
    
    // Try parsing as date only
    if let Ok(naive_date) = NaiveDate::parse_from_str(s, "%Y-%m-%d") {
        if let Some(dt) = naive_date.and_hms_opt(0, 0, 0) {
            return Ok(dt.and_utc());
        }
    }
    
    Err(CommonError::ValidationError(format!("Failed to parse date: {}", s)))
}

/// Parse a timestamp (seconds since epoch) to a DateTime<Utc>
/// 
/// # Examples
/// ```
/// use veza_common::utils::date::parse_timestamp;
/// 
/// let timestamp = 1609459200; // 2021-01-01 00:00:00 UTC
/// let dt = parse_timestamp(timestamp).unwrap();
/// assert_eq!(dt.year(), 2021);
/// ```
pub fn parse_timestamp(ts: i64) -> CommonResult<DateTime<Utc>> {
    Utc.timestamp_opt(ts, 0)
        .single()
        .ok_or_else(|| CommonError::ValidationError(format!("Invalid timestamp: {}", ts)))
}

/// Get current timestamp (seconds since epoch)
/// 
/// # Examples
/// ```
/// use veza_common::utils::date::current_timestamp;
/// 
/// let ts = current_timestamp();
/// assert!(ts > 0);
/// ```
pub fn current_timestamp() -> i64 {
    Utc::now().timestamp()
}

/// Get current DateTime<Utc>
/// 
/// # Examples
/// ```
/// use veza_common::utils::date::now;
/// 
/// let dt = now();
/// assert!(dt.year() >= 2020);
/// ```
pub fn now() -> DateTime<Utc> {
    Utc::now()
}

/// Convert a DateTime<Utc> to local time
/// 
/// # Examples
/// ```
/// use veza_common::utils::date::to_local;
/// use chrono::{DateTime, Utc};
/// 
/// let utc = Utc::now();
/// let local = to_local(&utc);
/// // Local time will be different from UTC
/// ```
pub fn to_local(dt: &DateTime<Utc>) -> DateTime<Local> {
    dt.with_timezone(&Local)
}

/// Check if a date is in the past
/// 
/// # Examples
/// ```
/// use veza_common::utils::date::is_past;
/// use chrono::{DateTime, Utc, Duration};
/// 
/// let past = Utc::now() - Duration::days(1);
/// assert!(is_past(&past));
/// ```
pub fn is_past(dt: &DateTime<Utc>) -> bool {
    *dt < Utc::now()
}

/// Check if a date is in the future
/// 
/// # Examples
/// ```
/// use veza_common::utils::date::is_future;
/// use chrono::{DateTime, Utc, Duration};
/// 
/// let future = Utc::now() + Duration::days(1);
/// assert!(is_future(&future));
/// ```
pub fn is_future(dt: &DateTime<Utc>) -> bool {
    *dt > Utc::now()
}

/// Get the difference in seconds between two dates
/// 
/// # Examples
/// ```
/// use veza_common::utils::date::seconds_between;
/// use chrono::{DateTime, Utc, Duration};
/// 
/// let dt1 = Utc::now();
/// let dt2 = dt1 + Duration::seconds(60);
/// assert_eq!(seconds_between(&dt1, &dt2), 60);
/// ```
pub fn seconds_between(dt1: &DateTime<Utc>, dt2: &DateTime<Utc>) -> i64 {
    (dt2.timestamp() - dt1.timestamp()).abs()
}

/// Get the difference in days between two dates
/// 
/// # Examples
/// ```
/// use veza_common::utils::date::days_between;
/// use chrono::{DateTime, Utc, Duration};
/// 
/// let dt1 = Utc::now();
/// let dt2 = dt1 + Duration::days(7);
/// assert_eq!(days_between(&dt1, &dt2), 7);
/// ```
pub fn days_between(dt1: &DateTime<Utc>, dt2: &DateTime<Utc>) -> i64 {
    dt2.signed_duration_since(*dt1).num_days().abs()
}

#[cfg(test)]
mod tests {
    use super::*;
    use chrono::{Duration, TimeZone, Datelike, Timelike};

    #[test]
    fn test_format_timestamp() {
        let timestamp = 1609459200; // 2021-01-01 00:00:00 UTC
        let formatted = format_timestamp(timestamp);
        assert!(formatted.contains("2021"));
        assert!(formatted.contains("00:00:00"));
    }

    #[test]
    fn test_format_timestamp_iso() {
        let timestamp = 1609459200; // 2021-01-01 00:00:00 UTC
        let formatted = format_timestamp_iso(timestamp);
        assert!(formatted.contains("2021-01-01"));
        assert!(formatted.contains("T"));
    }

    #[test]
    fn test_format_datetime() {
        let dt = Utc.with_ymd_and_hms(2021, 1, 1, 12, 30, 45).unwrap();
        let formatted = format_datetime(&dt, "%Y-%m-%d %H:%M:%S");
        assert_eq!(formatted, "2021-01-01 12:30:45");
    }

    #[test]
    fn test_format_datetime_iso() {
        let dt = Utc.with_ymd_and_hms(2021, 1, 1, 12, 30, 45).unwrap();
        let formatted = format_datetime_iso(&dt);
        assert!(formatted.contains("2021-01-01"));
        assert!(formatted.contains("T"));
        assert!(formatted.contains("12:30:45"));
    }

    #[test]
    fn test_format_relative_time() {
        let now = Utc::now();
        
        // Just now
        let just_now = now - Duration::seconds(30);
        assert!(format_relative_time(&just_now, &now).contains("just now"));
        
        // Minutes ago
        let minutes_ago = now - Duration::minutes(5);
        let relative = format_relative_time(&minutes_ago, &now);
        assert!(relative.contains("5"));
        assert!(relative.contains("minute"));
        
        // Hours ago
        let hours_ago = now - Duration::hours(2);
        let relative = format_relative_time(&hours_ago, &now);
        assert!(relative.contains("2"));
        assert!(relative.contains("hour"));
        
        // Days ago
        let days_ago = now - Duration::days(3);
        let relative = format_relative_time(&days_ago, &now);
        assert!(relative.contains("3"));
        assert!(relative.contains("day"));
    }

    #[test]
    fn test_parse_date_iso() {
        let date_str = "2021-01-01T00:00:00Z";
        let dt = parse_date(date_str).unwrap();
        assert_eq!(dt.year(), 2021);
        assert_eq!(dt.month(), 1);
        assert_eq!(dt.day(), 1);
    }

    #[test]
    fn test_parse_date_common_format() {
        let date_str = "2021-01-01 12:30:45";
        let dt = parse_date(date_str).unwrap();
        assert_eq!(dt.year(), 2021);
        assert_eq!(dt.hour(), 12);
        assert_eq!(dt.minute(), 30);
    }

    #[test]
    fn test_parse_date_date_only() {
        let date_str = "2021-01-01";
        let dt = parse_date(date_str).unwrap();
        assert_eq!(dt.year(), 2021);
        assert_eq!(dt.month(), 1);
        assert_eq!(dt.day(), 1);
        assert_eq!(dt.hour(), 0);
    }

    #[test]
    fn test_parse_date_invalid() {
        let date_str = "invalid date";
        let result = parse_date(date_str);
        assert!(result.is_err());
    }

    #[test]
    fn test_parse_timestamp() {
        let timestamp = 1609459200; // 2021-01-01 00:00:00 UTC
        let dt = parse_timestamp(timestamp).unwrap();
        assert_eq!(dt.year(), 2021);
        assert_eq!(dt.month(), 1);
        assert_eq!(dt.day(), 1);
    }

    #[test]
    fn test_current_timestamp() {
        let ts = current_timestamp();
        assert!(ts > 0);
        assert!(ts > 1609459200); // Should be after 2021-01-01
    }

    #[test]
    fn test_now() {
        let dt = now();
        assert!(dt.year() >= 2020);
    }

    #[test]
    fn test_to_local() {
        let utc = Utc.with_ymd_and_hms(2021, 1, 1, 12, 0, 0).unwrap();
        let local = to_local(&utc);
        // Local time should be different from UTC (unless in UTC timezone)
        assert_eq!(local.year(), 2021);
    }

    #[test]
    fn test_is_past() {
        let past = Utc::now() - Duration::days(1);
        assert!(is_past(&past));
        
        let future = Utc::now() + Duration::days(1);
        assert!(!is_past(&future));
    }

    #[test]
    fn test_is_future() {
        let future = Utc::now() + Duration::days(1);
        assert!(is_future(&future));
        
        let past = Utc::now() - Duration::days(1);
        assert!(!is_future(&past));
    }

    #[test]
    fn test_seconds_between() {
        let dt1 = Utc.with_ymd_and_hms(2021, 1, 1, 0, 0, 0).unwrap();
        let dt2 = dt1 + Duration::seconds(60);
        assert_eq!(seconds_between(&dt1, &dt2), 60);
        
        // Should work in reverse order too
        assert_eq!(seconds_between(&dt2, &dt1), 60);
    }

    #[test]
    fn test_days_between() {
        let dt1 = Utc.with_ymd_and_hms(2021, 1, 1, 0, 0, 0).unwrap();
        let dt2 = dt1 + Duration::days(7);
        assert_eq!(days_between(&dt1, &dt2), 7);
        
        // Should work in reverse order too
        assert_eq!(days_between(&dt2, &dt1), 7);
    }
}

