//! `time::` — UTC time. The canonical "time value" is milliseconds
//! since the Unix epoch as `i64`. ISO 8601 strings are for parsing and
//! display only. UTC only — no timezone support in v1.1.0 (would pull
//! in chrono-tz, deferred until a real use case demands it).

use chrono::{DateTime, SecondsFormat, Utc};
use rhai::{Engine as RhaiEngine, EvalAltResult, Module};

pub fn register(engine: &mut RhaiEngine) {
    let mut module = Module::new();
    register_now(&mut module);
    register_now_ms(&mut module);
    register_parse(&mut module);
    register_format(&mut module);
    register_add_seconds(&mut module);
    register_diff_seconds(&mut module);
    engine.register_static_module("time", module.into());
}

fn register_now(module: &mut Module) {
    module.set_native_fn("now", || -> Result<String, Box<EvalAltResult>> {
        Ok(Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true))
    });
}

fn register_now_ms(module: &mut Module) {
    module.set_native_fn("now_ms", || -> Result<i64, Box<EvalAltResult>> {
        Ok(Utc::now().timestamp_millis())
    });
}

fn register_parse(module: &mut Module) {
    module.set_native_fn("parse", |iso: &str| -> Result<i64, Box<EvalAltResult>> {
        DateTime::parse_from_rfc3339(iso)
            .map(|dt| dt.timestamp_millis())
            .map_err(|e| format!("time::parse: invalid ISO 8601 / RFC 3339: {e}").into())
    });
}

fn register_format(module: &mut Module) {
    module.set_native_fn("format", |ms: i64| -> Result<String, Box<EvalAltResult>> {
        DateTime::<Utc>::from_timestamp_millis(ms)
            .map(|dt| dt.to_rfc3339_opts(SecondsFormat::Millis, true))
            .ok_or_else(|| format!("time::format: ms ({ms}) out of representable range").into())
    });
}

fn register_add_seconds(module: &mut Module) {
    module.set_native_fn(
        "add_seconds",
        |ms: i64, secs: i64| -> Result<i64, Box<EvalAltResult>> {
            secs.checked_mul(1000)
                .and_then(|delta| ms.checked_add(delta))
                .ok_or_else(|| format!("time::add_seconds: overflow (ms={ms}, secs={secs})").into())
        },
    );
}

fn register_diff_seconds(module: &mut Module) {
    module.set_native_fn(
        "diff_seconds",
        |a_ms: i64, b_ms: i64| -> Result<i64, Box<EvalAltResult>> {
            b_ms.checked_sub(a_ms)
                .map(|d| d / 1000)
                .ok_or_else(|| format!("time::diff_seconds: overflow (a={a_ms}, b={b_ms})").into())
        },
    );
}
