///|
/// @json.JsonPath-like API
pub(all) enum JsonPointer {
  /// ObjectRoot
  Root
  /// Key Access
  Key(JsonPointer, key~ : String)
  /// Index Access
  Index(JsonPointer, index~ : Int)
} derive(Eq, Hash, Debug, FromJson, ToJson)

///|
pub fn JsonPointer::index(self : JsonPointer, index : Int) -> JsonPointer {
  Index(self, index~)
}

///|
pub fn JsonPointer::key(self : JsonPointer, key : String) -> JsonPointer {
  Key(self, key~)
}

///|
pub impl Show for JsonPointer with fn output(self, logger) {
  match self {
    Root => logger.write_string("#")
    Key(p, key~) => logger..write_object(p)..write_string("/").write_string(key)
    Index(p, index~) =>
      logger..write_object(p)..write_string("/").write_object(index)
  }
}

///|
pub fn JsonPointer::access(self : JsonPointer, json : Json) -> Json? {
  match self {
    Root => Some(json)
    Key(parent, key~) =>
      match parent.access(json) {
        Some(Object(obj)) => obj.get(key)
        _ => None
      }
    Index(parent, index~) =>
      match parent.access(json) {
        Some(Array(arr)) =>
          if index >= 0 && index < arr.length() {
            Some(arr[index])
          } else {
            None
          }
        _ => None
      }
  }
}

///|
/// Parse JSON Pointer string into JsonPointer
pub fn JsonPointer::from_string(
  pointer : String,
) -> Result[JsonPointer, String] {
  if pointer == "#" || pointer == "" {
    return Ok(Root)
  }
  // Remove leading # or #/ if present
  let normalized = if pointer.has_prefix("#/") {
    pointer[2:]
  } else if pointer.has_prefix("#") {
    pointer[1:]
  } else if pointer.has_prefix("/") {
    pointer[1:]
  } else {
    pointer
  }

  // Split by / and build path
  let parts = normalized.split("/")
  let mut path = Root
  for part in parts {
    if part == "" {
      continue
    }
    let part_str = part.to_owned()

    // Check if this is a number (array index)
    let is_digit = fn(c : Char) -> Bool { c >= '0' && c <= '9' }
    let is_number = part_str.length() > 0 && part_str.iter().all(is_digit)
    if is_number {
      let mut num = 0
      for ch in part_str.iter() {
        num = num * 10 + (ch.to_int() - '0'.to_int())
      }
      path = path.index(num)
    } else {
      let unescaped = part_str
        .replace(old="~1", new="/")
        .replace(old="~0", new="~")
      path = path.key(unescaped)
    }
  }
  path |> Ok
}

///|
test "JsonPointer::access" {
  let path = Root.key("foo").index(0).key("bar")
  assert_eq(path.to_string(), "#/foo/0/bar")
  let pointer = Root.key("foo").index(0).key("bar")
  let json : Json = { "foo": [{ "bar": 42 }] }
  let resolved = pointer.access(json).unwrap()
  assert_eq(resolved, 42)
}

///|
test "JsonPointer::from_string" {
  let path1 = JsonPointer::from_string("#/foo/0/bar").unwrap()
  let expected1 = Root.key("foo").index(0).key("bar")
  assert_eq(path1, expected1)
  let path2 = JsonPointer::from_string("/foo/bar").unwrap()
  let expected2 = Root.key("foo").key("bar")
  assert_eq(path2, expected2)
  let path3 = JsonPointer::from_string("#").unwrap()
  assert_eq(path3, Root)
  let path4 = JsonPointer::from_string("").unwrap()
  assert_eq(path4, Root)
  let path5 = JsonPointer::from_string("#/foo~1bar/0").unwrap()
  let expected5 = Root.key("foo/bar").index(0)
  assert_eq(path5, expected5)
  let path6 = JsonPointer::from_string("#/foo~0bar").unwrap()
  let expected6 = Root.key("foo~bar")
  assert_eq(path6, expected6)
}

///|
test "JsonPointer::from_json_pointer" {
  let result = JsonPointer::from_string("#/abc/items/0/def")
  result
  |> debug_inspect(
    content=(
      #|Ok(Key(Index(Key(Key(Root, key="abc"), key="items"), index=0), key="def"))
    ),
  )
  // assert(result is Err(JsonPointerDecodeError(_)))
}