/// A duration representing a span of time in milliseconds.
///
/// The inner `Int64` value holds the number of milliseconds. Negative values
/// represent durations in the past direction.
///
/// Supports arithmetic via `Add`, `Sub`, and `Neg` trait implementations,
/// as well as scalar multiplication via `Duration::scale`.

///|
pub(all) struct Duration(Int64) derive(Eq, Debug, Compare)

/// Returns the duration value in milliseconds.

///|
pub fn Duration::to_ms(self : Duration) -> Int64 {
  self.0
}

/// A point in time represented as milliseconds elapsed since the epoch.
///
/// Unlike `Duration` which represents a span of time, `EpochTime` represents
/// a specific instant. The epoch is Unix epoch (1970-01-01T00:00:00Z) by default,
/// but can be shifted via the `epoch` parameter in parse functions.

///|
pub(all) struct EpochTime(Int64) derive(Eq, Debug, Compare)

/// Returns the epoch time value in milliseconds.

///|
pub fn EpochTime::to_ms(self : EpochTime) -> Int64 {
  self.0
}

/// A parsed time specification, either absolute or relative.
///
/// Both variants carry `(EpochTime, Duration)`. The difference lies in
/// re-serialization behavior via `to_cli_string`:
///
/// - `Absolute` serializes to an ISO 8601 datetime string (e.g. `2025-03-15T00:56:14Z`).
///   The result is reproducible regardless of when or by whom it is executed.
/// - `Relative` serializes to a signed duration string (e.g. `-8m`, `+1h30m`).
///   The resolved time changes with each execution.
///
/// The `Duration` field preserves the original offset information for re-serialization.
/// For datetime-only inputs, the `Duration` is `Duration(0L)`.

///|
pub(all) enum TimeSpec {
  Absolute(EpochTime, Duration)
  Relative(EpochTime, Duration)
} derive(Eq, Debug)

/// Returns the resolved epoch time.

///|
pub fn TimeSpec::epoch(self : TimeSpec) -> EpochTime {
  match self {
    Absolute(e, _) | Relative(e, _) => e
  }
}

/// Returns the duration offset used to compute the epoch time.

///|
pub fn TimeSpec::duration(self : TimeSpec) -> Duration {
  match self {
    Absolute(_, d) | Relative(_, d) => d
  }
}

/// Returns whether this is an absolute time specification.

///|
pub fn TimeSpec::is_absolute(self : TimeSpec) -> Bool {
  match self {
    Absolute(_, _) => true
    Relative(_, _) => false
  }
}

/// Returns the resolved epoch time in milliseconds.

///|
pub fn TimeSpec::to_epoch_ms(self : TimeSpec) -> Int64 {
  self.epoch().0
}

/// A time range consisting of optional `since` and `until` bounds.
///
/// Either or both fields may be `None` to represent an open-ended range.
/// When both are present and one is `Absolute` while the other is `Relative`,
/// the absolute side serves as an anchor for resolving the relative side.

///|
pub(all) struct TimeRange {
  since : TimeSpec?
  until : TimeSpec?
} derive(Eq, Debug)

/// Controls how unsigned (no explicit `+` or `-`) duration inputs are interpreted.
///
/// Passed as the `default_sign` parameter to parse functions. The caller
/// chooses the variant based on application context:
///
/// - `Minus` — unsigned means negative (e.g. `--since 5m` means "5 minutes ago").
/// - `Plus` — unsigned means positive (e.g. `set_timer("3m")` means "3 minutes from now").
/// - `Reject` — unsigned is an error; an explicit sign is required to avoid ambiguity.

///|
pub(all) enum Sign {
  Minus
  Plus
  Reject
} derive(Eq, Debug)

/// A timezone offset from UTC.
///
/// - `Utc` — zero offset (serialized as `Z`).
/// - `Local` — resolved at runtime to the system's local timezone offset.
/// - `Hour(n)` — offset in whole hours (e.g. `Hour(9)` for `+09:00`).
/// - `Min(n)` — offset in minutes for non-whole-hour zones (e.g. `Min(330)` for `+05:30`).

///|
pub(all) enum TzOffset {
  Utc
  Local
  Hour(Int)
  Min(Int)
} derive(Eq, Debug)

/// Error type raised when parsing fails.
///
/// Contains a human-readable message describing the parse failure.

///|
pub(all) suberror ParseError {
  ParseError(String)
} derive(Eq, Debug)