///|
/// Snowflake ID generator for MoonBit.
///
/// The generated IDs are 64-bit integers with the following layout:
/// ```
/// 0 | 41 bits timestamp | 10 bits node | 12 bits sequence
/// ```

// ─── Defaults ─────────────────────────────────────────────────────────

///|
/// Default epoch: January 1, 2026 00:00:00 UTC (in milliseconds).
///
/// The 41-bit timestamp field provides ≈69 years of ID space.
///
///   2026-01-01 → ≈2095  ← IDs remain unique until roughly here
///
/// You can pick any epoch via `Node::new(node_id, your_epoch)` — for example:
///
/// | Epoch | Value (ms) | Expires | Use case |
/// |-------|-----------|---------|---------|
/// | 2026-01-01 | 1767225600000 | 2095-09 | default |
/// | 2025-01-01 | 1735689600000 | 2094-09 | alternative |
/// | 2010-11-04 (Twitter) | 1288834974657 | 2080-07 | interop |
pub let default_epoch : Int64 = 1767225600000L

///|
/// Number of bits allocated for the node ID.
pub let node_bits : Int = 10

///|
/// Number of bits allocated for the sequence number.
pub let step_bits : Int = 12

// ─── Snowflake type ───────────────────────────────────────────────────

///|
/// A snowflake ID — a 63-bit positive integer with embedded timestamp,
/// node ID, and sequence number.
pub struct Snowflake {
  val : Int64
} derive(Debug)

///|
/// Create a `Snowflake` from a raw `Int64`.
pub fn Snowflake::new(val : Int64) -> Self {
  { val, }
}

///|
/// Convert to a raw `Int64`.
pub fn Snowflake::to_int64(self : Self) -> Int64 {
  self.val
}

// ─── Eq / Compare for Snowflake ───────────────────────────────────────

///|
pub impl Eq for Snowflake with fn equal(self, other) {
  self.val == other.val
}

///|
pub impl Compare for Snowflake with fn compare(self, other) {
  self.val.compare(other.val)
}

///|
pub impl Hash for Snowflake with fn hash_combine(self, hasher) {
  self.val.hash_combine(hasher)
}

///|
pub impl Show for Snowflake with fn to_string(self) {
  self.val.to_string()
}

///|
/// Serialise as a JSON number string (e.g. `"67817059108864"`).
pub impl ToJson for Snowflake with fn to_json(self) {
  self.val.to_string().to_json()
}

///|
/// Deserialise from a JSON number string.
pub impl FromJson for Snowflake with fn from_json(json, path) {
  guard json is String(str) else {
    raise @json.JsonDecodeError::JsonDecodeError(
      (path, "snowflake: expected string"),
    )
  }
  let v = @string.parse_int64(str) catch {
    _ =>
      raise @json.JsonDecodeError::JsonDecodeError(
        (path, "snowflake: invalid number"),
      )
  }
  { val: v }
}

// ─── Snowflake encoding (instance methods) ───────────────────────────

///|
/// Return a binary (base‑2) string.
pub fn Snowflake::to_base2(self : Self) -> String {
  self.val.to_string(radix=2)
}

///|
/// Return a [z‑base‑32] string.
pub fn Snowflake::to_base32(self : Self) -> String {
  encode_base(self.val, 32L, base32_alphabet)
}

///|
/// Return a base‑36 string.
pub fn Snowflake::to_base36(self : Self) -> String {
  self.val.to_string(radix=36)
}

///|
/// Return a [Bitcoin Base‑58] string.
pub fn Snowflake::to_base58(self : Self) -> String {
  encode_base(self.val, 58L, base58_alphabet)
}

///|
/// Return a base‑64 string.
pub fn Snowflake::to_base64(self : Self) -> String {
  @base64.encode(@utf8.encode(self.val.to_string().view()).view())
}

///|
/// Return the decimal string as UTF‑8 bytes.
pub fn Snowflake::to_bytes(self : Self) -> Bytes {
  @utf8.encode(self.val.to_string().view())
}

///|
/// Return an 8‑byte big‑endian encoding.
pub fn Snowflake::to_int_bytes(self : Self) -> Bytes {
  self.val.reinterpret_as_uint64().to_be_bytes()
}

// ─── Snowflake field extraction (default layout) ─────────────────────

///|
/// Extract the Unix‑ms timestamp.  Uses the default 10‑bit node / 12‑bit step layout.
pub fn Snowflake::time(self : Self) -> Int64 {
  (self.val >> (node_bits + step_bits)) + default_epoch
}

///|
/// Extract the node/worker ID.  Uses the default 10‑bit node / 12‑bit step layout.
pub fn Snowflake::node_id(self : Self) -> Int64 {
  let n_max = -1L ^ (-1L << node_bits)
  (self.val & (n_max << step_bits)) >> step_bits
}

///|
/// Extract the sequence number.  Uses the default 12‑bit step layout.
pub fn Snowflake::step(self : Self) -> Int64 {
  self.val & (-1L ^ (-1L << step_bits))
}

// ─── Snowflake parsing (static methods) ──────────────────────────────

///|
/// Parse from a decimal string.
pub fn Snowflake::from_string(s : String) -> Self? {
  parse_opt(s, 10).map(fn(v) { { val: v } })
}

///|
/// Parse from a binary string.
pub fn Snowflake::from_base2(s : String) -> Self? {
  parse_opt(s, 2).map(fn(v) { { val: v } })
}

///|
/// Parse from a [z‑base‑32] string.
pub fn Snowflake::from_base32(s : String) -> Self? {
  parse_base_idx(s, 32L, base32_decode).map(fn(v) { { val: v } })
}

///|
/// Parse from a base‑36 string.
pub fn Snowflake::from_base36(s : String) -> Self? {
  parse_opt(s, 36).map(fn(v) { { val: v } })
}

///|
/// Parse from a [Bitcoin Base‑58] string.
pub fn Snowflake::from_base58(s : String) -> Self? {
  parse_base_idx(s, 58L, base58_decode).map(fn(v) { { val: v } })
}

///|
/// Parse from a base‑64 string.
pub fn Snowflake::from_base64(s : String) -> Self? {
  let decoded = try @base64.decode(s.view()) catch {
    _ => return None
  } noraise {
    b => b
  }
  Snowflake::from_string(@utf8.decode_lossy(decoded.view()))
}

///|
/// Parse from UTF‑8 bytes containing a decimal number.
pub fn Snowflake::from_bytes(bytes : Bytes) -> Self? {
  Snowflake::from_string(@utf8.decode_lossy(bytes.view()))
}

///|
/// Parse from an 8‑byte big‑endian byte array.
/// Returns `None` if `bytes` is not exactly 8 bytes.
pub fn Snowflake::from_int_bytes(bytes : Bytes) -> Self? {
  if bytes.length() != 8 {
    return None
  }
  let mut result = 0UL
  for i = 0; i < 8; i = i + 1 {
    result = (result << 8) | bytes[i].to_uint64()
  }
  Some({ val: result.reinterpret_as_int64() })
}

// ─── Node (generator) ─────────────────────────────────────────────────

///|
/// A generator that produces unique, time‑sorted `Snowflake` IDs.
pub struct Node {
  mut step : Int64
  mut last_time : Int64
  node : Int64
  epoch : Int64
  node_max : Int64
  node_mask : Int64
  step_mask : Int64
  time_shift : Int
  node_shift : Int
}

///|
/// Create a new `Node`.
///
/// Parameters:
/// - `node`    : Worker ID (0 … node_max).
/// - `epoch`   : Custom epoch in ms since Unix epoch.
/// - `nb`      : Bits for the node field (default 10).
/// - `sb`      : Bits for the sequence field (default 12).
///
/// Returns `None` if parameters are out of range.
pub fn Node::new(
  node : Int64,
  epoch : Int64,
  nb? : Int = node_bits,
  sb? : Int = step_bits,
) -> Self? {
  if nb + sb > 22 {
    return None
  }
  let n_max = -1L ^ (-1L << nb)
  if node < 0L || node > n_max {
    return None
  }
  Some({
    step: 0L,
    last_time: 0L,
    node,
    epoch,
    node_max: n_max,
    node_mask: n_max << sb,
    step_mask: -1L ^ (-1L << sb),
    time_shift: nb + sb,
    node_shift: sb,
  })
}

///|
/// Create a `Node` with the default epoch and default bit widths.
pub fn Node::new_default(node : Int64) -> Self? {
  Node::new(node, default_epoch)
}

///|
/// Generate a new unique `Snowflake`.
///
/// To guarantee uniqueness:
/// - Keep accurate system time.
/// - Never run multiple nodes with the same node ID.
pub fn Node::generate(self : Self) -> Snowflake {
  let now = @env.now().reinterpret_as_int64()
  let mut elapsed = now - self.epoch

  // ── Clock backward jump guard ────────────────────────────────────
  // If the system clock jumps backwards, `elapsed` will be less than
  // `self.last_time`.  In that case we wait until real time catches up
  // rather than reusing a past timestamp, which would risk duplicates.
  if elapsed < self.last_time {
    while elapsed < self.last_time {
      let n = @env.now().reinterpret_as_int64()
      elapsed = n - self.epoch
    }
    // Time has caught up — treat as a normal tick.
    self.step = 0L
  } else if elapsed == self.last_time {
    self.step = (self.step + 1L) & self.step_mask
    if self.step == 0L {
      while elapsed <= self.last_time {
        let n = @env.now().reinterpret_as_int64()
        elapsed = n - self.epoch
      }
    }
  } else {
    self.step = 0L
  }

  self.last_time = elapsed
  {
    val: (elapsed << self.time_shift) |
    (self.node << self.node_shift) |
    self.step,
  }
}

///|
/// Extract the Unix‑ms timestamp from a `Snowflake` using this node's
/// epoch and bit layout.
pub fn Node::extract_time(self : Self, id : Snowflake) -> Int64 {
  (id.val >> self.time_shift) + self.epoch
}

///|
/// Extract the node/worker ID from a `Snowflake` using this node's
/// bit layout.
pub fn Node::extract_node_id(self : Self, id : Snowflake) -> Int64 {
  (id.val & self.node_mask) >> self.node_shift
}

///|
/// Extract the sequence number from a `Snowflake` using this node's
/// bit layout.
pub fn Node::extract_step(self : Self, id : Snowflake) -> Int64 {
  id.val & self.step_mask
}

// ─── Internal helpers ─────────────────────────────────────────────────

///|
/// z-base-32 alphabet.
let base32_alphabet : String = "ybndrfg8ejkmcpqxot1uwisza345h769"

///|
/// Bitcoin Base‑58 alphabet.
let base58_alphabet : String = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"

///|
/// Precomputed lookup: character codepoint → value (-1 = invalid).
let base32_decode : Array[Int] = {
  let table = Array::make(128, -1)
  for i = 0; i < base32_alphabet.char_length(); i = i + 1 {
    let c = base32_alphabet.get_char(i).unwrap()
    table[c.to_int()] = i
  }
  table
}

///|
/// Precomputed lookup: character codepoint → value (-1 = invalid).
let base58_decode : Array[Int] = {
  let table = Array::make(128, -1)
  for i = 0; i < base58_alphabet.char_length(); i = i + 1 {
    let c = base58_alphabet.get_char(i).unwrap()
    table[c.to_int()] = i
  }
  table
}

///|
/// Encode `n` in the given `base` with the supplied `alphabet`.
fn encode_base(n : Int64, base : Int64, alphabet : String) -> String {
  if n == 0L {
    alphabet.get_char(0).unwrap().to_string()
  } else {
    let mut m = n
    let mut len = 0
    let mut t = m
    while t > 0L {
      len = len + 1
      t /= base
    }
    let sb = StringBuilder()
    let buf : Array[Char] = Array::make(len, '\u{0}')
    while m > 0L {
      let idx = (m % base).to_int()
      len = len - 1
      buf[len] = alphabet.get_char(idx).unwrap()
      m /= base
    }
    for i = 0; i < buf.length(); i = i + 1 {
      sb.write_char(buf[i])
    }
    sb.to_string()
  }
}

///|
/// Parse a decimal/base2/base36 string via `@string.parse_int64`.
fn parse_opt(s : String, base : Int) -> Int64? {
  try @string.parse_int64(s, base~) catch {
    _ => None
  } noraise {
    v => Some(v)
  }
}

///|
/// Parse a custom‑alphabet string (base32 / base58) using a lookup table.
fn parse_base_idx(s : String, base : Int64, table : Array[Int]) -> Int64? {
  if s.is_empty() {
    return None
  }
  let max_safe = if base == 32L {
    288230376151711743L
  } else {
    158526706883441459L
  }
  let mut id = 0L
  for ch in s {
    let code = ch.to_int()
    if code >= 128 {
      return None
    }
    let v = table[code]
    if v == -1 {
      return None
    }
    if id > max_safe {
      return None
    }
    id = id * base + v.to_int64()
  }
  Some(id)
}