///|
/// Errors are recoverable: malformed input never deliberately aborts.
pub(all) suberror ConfigError {
  InvalidPointer(String)
  InvalidIndex(String)
  MissingPath(String)
  InvalidPatch(String)
  TestFailed(String)
  InvalidMove(String)
  LimitExceeded(String)
  InvalidResolution(String)
} derive(@debug.Debug)

///|
/// RFC 6902 operations. Paths are RFC 6901 JSON Pointers.
pub(all) enum Operation {
  Add(String, Json)
  Remove(String)
  Replace(String, Json)
  Move(String, String) // source, destination
  Copy(String, String) // source, destination
  Test(String, Json)
} derive(@debug.Debug, Eq)

///|
pub extend Operation with Eq::{equal, not_equal}

///|
/// Missing values are None; an explicit JSON null is Some(Null).
pub(all) struct Conflict {
  path : String
  base : Json?
  ours : Json?
  theirs : Json?
} derive(@debug.Debug, Eq)

///|
pub extend Conflict with Eq::{equal, not_equal}

///|
/// A conflicted result is a preview. Conflicting locations retain the base.
pub(all) struct MergeResult {
  value : Json
  conflicts : Array[Conflict]
} derive(@debug.Debug, Eq)

///|
pub extend MergeResult with Eq::{equal, not_equal}

///|
/// Every conflict requires an explicit choice. Delete differs from Set(Null).
pub(all) enum Choice {
  Base
  Ours
  Theirs
  Set(Json)
  Delete
} derive(@debug.Debug)

///|
pub(all) struct Decision {
  path : String
  choice : Choice
} derive(@debug.Debug)

///|
/// Final configuration and a patch guarded by a whole-base test.
pub struct ResolvedMerge {
  value : Json
  patch : Array[Operation]
} derive(@debug.Debug)

///|
fn clone_json(value : Json) -> Json {
  match value {
    Array(a) => Json::array(a.map(clone_json))
    Object(o) => Json::object(o.map(fn(_, v) { clone_json(v) }))
    _ => value
  }
}

///|
/// Bound recursion and work before any operation traverses the document.
fn validate_tree(
  value : Json,
  depth : Int,
  count : Ref[Int],
) -> Unit raise ConfigError {
  if depth > 128 {
    raise LimitExceeded("document depth exceeds 128")
  }
  count.val = count.val + 1
  if count.val > 100000 {
    raise LimitExceeded("document exceeds 100000 nodes")
  }
  match value {
    Array(a) =>
      for v in a {
        validate_tree(v, depth + 1, count)
      }
    Object(o) =>
      for _, v in o {
        validate_tree(v, depth + 1, count)
      }
    Number(_, repr~) => {
      let _ = canonical_number(value, repr)
    }
    _ => ()
  }
}

///|
fn validate_document(value : Json) -> Unit raise ConfigError {
  validate_tree(value, 0, Ref(0))
}

///|
fn keys_union(a : Map[String, Json], b : Map[String, Json]) -> Array[String] {
  let keys = a.keys().to_array()
  for k, _ in b {
    if !a.contains(k) {
      keys.push(k)
    }
  }
  keys.sort_by((a, b) => a.to_array().lexical_compare(b.to_array()))
  keys
}

///|
fn append_path(path : String, key : String) -> String {
  path + "/" + key.replace_all(old="~", new="~0").replace_all(old="/", new="~1")
}

///|
pub extend ConfigError with @debug.Debug::{to_repr}

///|
pub extend Operation with @debug.Debug::{to_repr}

///|
pub extend Conflict with @debug.Debug::{to_repr}

///|
pub extend MergeResult with @debug.Debug::{to_repr}

///|
pub extend Choice with @debug.Debug::{to_repr}

///|
pub extend Decision with @debug.Debug::{to_repr}

///|
pub extend ResolvedMerge with @debug.Debug::{to_repr}