type seconds = float
type milliseconds = float
type nanoseconds = float

type timeTuple = (seconds, nanoseconds)

type timeRef = timeTuple

type timeElapsed = timeTuple

@val @scope("process") external makeTimer: unit => timeRef = "hrtime"

@val @scope("process") external timeSince: timeRef => timeElapsed = "hrtime"

let nanoToMilli = (nano: nanoseconds): milliseconds => nano /. 1_000_000.
let secToMilli = (sec: seconds): milliseconds => sec *. 1_000.

let nanoToTimeTuple = (nano: nanoseconds): timeTuple => {
  let factor = 1_000_000_000.
  let seconds = Math.floor(nano /. factor)
  let nanos = Float.mod(nano, factor)
  (seconds, nanos)
}

let timeElapsedToNewRef = (elapsed: timeElapsed, ref: timeRef): timeRef => {
  let (elapsedSeconds, elapsedNano) = elapsed
  let (refSeconds, refNano) = ref

  let (nanoExtraSeconds, remainderNanos) = nanoToTimeTuple(elapsedNano +. refNano)
  (elapsedSeconds +. refSeconds +. nanoExtraSeconds, remainderNanos)
}

let toMillis = ((sec, nano): timeElapsed): milliseconds => {
  sec->secToMilli +. nano->nanoToMilli
}

let toSeconds = ((sec, nano): timeElapsed): seconds => {
  sec +. nano /. 1_000_000_000.
}

let toSecondsFloat = (elapsed: timeElapsed): float => {
  elapsed->toSeconds->(Utils.magic: seconds => float)
}

let toInt = float => float->Int.fromFloat
let intFromMillis = toInt
let intFromNanos = toInt
let intFromSeconds = toInt
let floatFromMillis: milliseconds => float = v => v->(Utils.magic: milliseconds => float)
let floatFromSeconds: seconds => float = v => v->(Utils.magic: seconds => float)

let millisBetween = (~from: timeRef, ~to: timeRef): int => {
  to->toMillis->intFromMillis - from->toMillis->intFromMillis
}

let secondsBetween = (~from: timeRef, ~to: timeRef): float => {
  to->toSecondsFloat -. from->toSecondsFloat
}
