//! Time helpers and the `MonthYear` type used for card expiries.

use serde::{Deserialize, Serialize};

/// Current Unix timestamp in seconds.
pub fn now_unix() -> i64 {
    chrono::Utc::now().timestamp()
}

/// Month + year (1-12 / e.g. 2026). Used for card expiries.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub struct MonthYear {
    pub month: u8,
    pub year: u16,
}

impl MonthYear {
    pub fn new(month: u8, year: u16) -> Result<Self, &'static str> {
        if !(1..=12).contains(&month) {
            return Err("month must be 1..=12");
        }
        if year < 2000 || year > 2099 {
            return Err("year must be 2000..=2099");
        }
        Ok(Self { month, year })
    }
}

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

    #[test]
    fn now_unix_is_positive_and_recent() {
        let t = now_unix();
        assert!(t > 1_700_000_000); // after late 2023
        assert!(t < 4_000_000_000); // before 2096
    }

    #[test]
    fn month_year_constructor_rejects_bad_month() {
        assert!(MonthYear::new(0, 2026).is_err());
        assert!(MonthYear::new(13, 2026).is_err());
        assert!(MonthYear::new(1, 2026).is_ok());
        assert!(MonthYear::new(12, 2026).is_ok());
    }

    #[test]
    fn month_year_constructor_rejects_bad_year() {
        assert!(MonthYear::new(1, 1999).is_err());
        assert!(MonthYear::new(1, 2100).is_err());
        assert!(MonthYear::new(1, 2000).is_ok());
        assert!(MonthYear::new(1, 2099).is_ok());
    }

    #[test]
    fn month_year_round_trips_through_json() {
        let my = MonthYear::new(7, 2030).unwrap();
        let json = serde_json::to_string(&my).unwrap();
        let parsed: MonthYear = serde_json::from_str(&json).unwrap();
        assert_eq!(parsed, my);
    }
}
