Documentation
#![cfg(feature = "jiff")]

use xsd::{PrimitiveValue, TimezoneOffset, Value, primitive::Time};

#[test]
fn civil_time_conversion_rejects_every_explicit_offset() {
    for nanosecond in [0, 1, 999_999_999] {
        let civil = jiff::civil::Time::new(23, 59, 59, nanosecond).unwrap();
        let time = Time::from(civil);
        assert_eq!(jiff::civil::Time::try_from(time).unwrap(), civil);
        assert_eq!(Value::from(civil), Value::from(time));
        assert_eq!(PrimitiveValue::from(civil), PrimitiveValue::from(time));
        for minutes in -840..=840 {
            let offset = TimezoneOffset::from_minutes(minutes).unwrap();
            let time = time.with_timezone(Some(offset));
            assert_eq!(
                jiff::civil::Time::try_from(time),
                Err(xsd::TimezoneLossError)
            );
            assert_eq!(time.civil(), civil);
            assert_eq!(time.timezone(), Some(offset));
        }
    }
}

#[test]
fn time_parser_retains_offsets_including_end_of_day() {
    for clock in [
        "00:00:00",
        "12:34:56.000000001",
        "23:59:59.999999999",
        "24:00:00",
        "24:00:00.000000000000000",
    ] {
        for suffix in ["", "Z", "+00:00", "-00:00", "+14:00", "-14:00", "+05:45"] {
            let input = format!("{clock}{suffix}");
            let value = xsd::parse(&input, xsd::TIME).unwrap();
            let Value::Primitive(PrimitiveValue::Time(time)) = value.clone() else {
                panic!("wrong datatype");
            };
            let offset = if suffix.is_empty() {
                None
            } else {
                Some(suffix.parse().unwrap())
            };
            assert_eq!(time.timezone(), offset);
            if clock.starts_with("24") {
                assert_eq!(time.civil(), jiff::civil::Time::new(0, 0, 0, 0).unwrap());
            }
            assert_eq!(xsd::parse_time(value.to_string()).unwrap(), value);
            assert_eq!(xsd::parse_time(input).unwrap(), value);
        }
    }
}

#[test]
fn time_parser_rejects_invalid_timezone_suffixes() {
    for clock in ["00:00:00", "12:34:56.125", "24:00:00.0"] {
        for suffix in [
            "z",
            "+14:01",
            "-14:01",
            "+01:60",
            "+02",
            "+0200",
            "+02:00:00",
            "Z ",
            "ZZ",
            "Z+01:00",
            "[UTC]",
            "+🦀",
            "-01:00",
        ] {
            let input = format!("{clock}{suffix}");
            assert!(xsd::parse_time(&input).is_err(), "{input}");
        }
    }
    for clock in [
        "24:00:01",
        "24:01:00",
        "24:00:00.001",
        "24:00:00.",
        "24:00:00,0",
        "23:59:60",
        "12:34:56.0000000000",
    ] {
        assert!(xsd::parse_time(format!("{clock}Z")).is_err(), "{clock}");
    }
}

#[test]
fn time_retains_optional_timezone_and_nanoseconds() {
    for nanosecond in [0, 1, 125_000_000, 999_999_999] {
        let civil = Time::new(23, 59, 59, nanosecond).unwrap();
        assert_eq!(civil.timezone(), None);
        assert_eq!(civil.subsec_nanosecond(), nanosecond);
        for minutes in [-840, -330, 0, 345, 840] {
            let offset = TimezoneOffset::from_minutes(minutes).unwrap();
            let time = civil.with_timezone(Some(offset));
            assert_eq!(time.civil(), civil.civil());
            assert_eq!(time.timezone(), Some(offset));
            assert_ne!(time, civil);
            assert_eq!(time.with_timezone(None), civil);
            let expected = format!("{civil}{offset}");
            assert_eq!(time.to_string(), expected);
            assert_eq!(PrimitiveValue::from(time).to_string(), expected);
            assert_eq!(Value::from(time).to_string(), expected);
        }
    }
    for (h, m, s, ns) in [
        (24, 0, 0, 0),
        (0, 60, 0, 0),
        (0, 0, 60, 0),
        (0, 0, 0, -1),
        (0, 0, 0, 1_000_000_000),
    ] {
        assert!(Time::new(h, m, s, ns).is_err());
    }
}

#[cfg(feature = "serde")]
#[test]
fn time_struct_encoding_validates_fields_and_offsets() {
    let time = Time::new(12, 34, 56, 1)
        .unwrap()
        .with_timezone(Some(TimezoneOffset::UTC));
    let json = serde_json::json!({"civil": "12:34:56.000000001", "timezone": 0});
    assert_eq!(serde_json::to_value(time).unwrap(), json);
    assert_eq!(serde_json::from_value::<Time>(json).unwrap(), time);
    assert!(
        serde_json::from_value::<Time>(serde_json::json!({"civil": "12:34:56", "timezone": 841}))
            .is_err()
    );
    for civil in [
        "12:34:56+02:00",
        "12:34:56[UTC]",
        "23:59:60",
        "2026-01-02T12:34:56",
    ] {
        assert!(
            serde_json::from_value::<Time>(serde_json::json!({"civil": civil, "timezone": null}))
                .is_err(),
            "{civil}"
        );
    }
}