/// String conversion utilities for UUIDs

///|
/// Convert a byte to a two-character hexadecimal string
fn byte_to_hex(b : Byte) -> String {
  let high = (b.to_int() >> 4) & 0xF
  let low = b.to_int() & 0xF
  let high_char = if high < 10 {
    '0'.to_int() + high
  } else {
    'a'.to_int() + high - 10
  }
  let low_char = if low < 10 {
    '0'.to_int() + low
  } else {
    'a'.to_int() + low - 10
  }
  Int::unsafe_to_char(high_char).to_string() +
  Int::unsafe_to_char(low_char).to_string()
}

///|
/// Convert a hexadecimal character to its numeric value
fn hex_char_to_int(c : Char) -> Int {
  let code = c.to_int()
  if code >= '0'.to_int() && code <= '9'.to_int() {
    code - '0'.to_int()
  } else if code >= 'a'.to_int() && code <= 'f'.to_int() {
    code - 'a'.to_int() + 10
  } else if code >= 'A'.to_int() && code <= 'F'.to_int() {
    code - 'A'.to_int() + 10
  } else {
    abort("Invalid hexadecimal character: " + c.to_string())
  }
}

///|
/// Convert two hexadecimal characters to a byte
/// Note: Currently unused but kept for future string parsing improvements
fn _hex_to_byte(h : Char, l : Char) -> Byte {
  let high = hex_char_to_int(h)
  let low = hex_char_to_int(l)
  (high << 4).lor(low).to_byte()
}

///|
/// Convert UUID to string representation with hyphens
/// Format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
pub fn Uuid::to_string(self : Uuid) -> String {
  let bytes = self.0
  byte_to_hex(bytes[0]) +
  byte_to_hex(bytes[1]) +
  byte_to_hex(bytes[2]) +
  byte_to_hex(bytes[3]) +
  "-" +
  byte_to_hex(bytes[4]) +
  byte_to_hex(bytes[5]) +
  "-" +
  byte_to_hex(bytes[6]) +
  byte_to_hex(bytes[7]) +
  "-" +
  byte_to_hex(bytes[8]) +
  byte_to_hex(bytes[9]) +
  "-" +
  byte_to_hex(bytes[10]) +
  byte_to_hex(bytes[11]) +
  byte_to_hex(bytes[12]) +
  byte_to_hex(bytes[13]) +
  byte_to_hex(bytes[14]) +
  byte_to_hex(bytes[15])
}

///|
/// Convert UUID to string representation without hyphens
pub fn Uuid::to_string_simple(self : Uuid) -> String {
  let bytes = self.0
  let mut result = ""
  for i = 0; i < 16; i = i + 1 {
    result = result + byte_to_hex(bytes[i])
  }
  result
}

///|
/// Parse UUID from string representation
/// Accepts both hyphenated (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) and non-hyphenated formats
pub fn from_string(s : String) -> Uuid? {
  // Must be exactly 32 hex characters (without hyphens) or 36 (with hyphens)
  let len = s.length()
  if len != 32 && len != 36 {
    return None
  }

  // Extract hex characters (removing hyphens if present)
  let mut hex_chars = ""
  for i = 0; i < len; i = i + 1 {
    let c = Int::unsafe_to_char(s.code_unit_at(i).to_int())
    if c == '-' {
      // Skip hyphens
    } else if (c >= '0' && c <= '9') ||
      (c >= 'a' && c <= 'f') ||
      (c >= 'A' && c <= 'F') {
      hex_chars = hex_chars + c.to_string()
    } else {
      // Invalid character
      return None
    }
  }

  // Must have exactly 32 hex characters after removing hyphens
  if hex_chars.length() != 32 {
    return None
  }

  // Convert each pair of hex characters to bytes
  let bytes : FixedArray[Byte] = FixedArray::make(16, b'\x00')
  for i = 0; i < 16; i = i + 1 {
    let high_char = Int::unsafe_to_char(hex_chars.code_unit_at(i * 2).to_int())
    let low_char = Int::unsafe_to_char(
      hex_chars.code_unit_at(i * 2 + 1).to_int(),
    )

    // Check if characters are valid hex
    let high = try_hex_char_to_int(high_char)
    let low = try_hex_char_to_int(low_char)
    match (high, low) {
      (Some(h), Some(l)) => bytes[i] = (h << 4).lor(l).to_byte()
      _ => return None
    }
  }
  Some(Uuid::new(bytes))
}

///|
/// Try to convert a hexadecimal character to its numeric value
/// Returns None if the character is not a valid hex character
fn try_hex_char_to_int(c : Char) -> Int? {
  let code = c.to_int()
  if code >= '0'.to_int() && code <= '9'.to_int() {
    Some(code - '0'.to_int())
  } else if code >= 'a'.to_int() && code <= 'f'.to_int() {
    Some(code - 'a'.to_int() + 10)
  } else if code >= 'A'.to_int() && code <= 'F'.to_int() {
    Some(code - 'A'.to_int() + 10)
  } else {
    None
  }
}

///|
/// Parse UUID from string, panics on invalid input
pub fn from_string_exn(s : String) -> Uuid {
  match from_string(s) {
    Some(uuid) => uuid
    None => abort("Invalid UUID string: " + s)
  }
}

///|
/// Convert UUID to URN string representation
/// Format: urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
pub fn Uuid::to_urn(self : Uuid) -> String {
  "urn:uuid:" + self.to_string()
}

///|
/// Parse UUID from URN string representation
pub fn from_urn(s : String) -> Uuid? {
  if s.length() > 9 {
    let uuid_part = s[9:].to_string() catch { _ => return None }
    from_string(uuid_part)
  } else {
    None
  }
}

///|
/// Parse UUID from URN string, panics on invalid input
pub fn from_urn_exn(s : String) -> Uuid {
  match from_urn(s) {
    Some(uuid) => uuid
    None => abort("Invalid UUID URN: " + s)
  }
}