///|
/// Normalizes portable lexical paths without touching the host filesystem.
/// Going above the lexical root is rejected. Drive-letter and UNC paths use
/// ASCII case folding; POSIX and relative paths remain case sensitive.
fn normalize_path(raw : String) -> String? {
  let path = raw.replace_all(old="\\", new="/")
  guard path != "" else { return None }
  let drive_absolute = path.length() >= 3 &&
    path[1] == ':' &&
    path[2] == '/' &&
    ((path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z'))
  if path.length() >= 2 && path[1] == ':' && !drive_absolute {
    return None
  }
  let unc = path.has_prefix("//")
  let absolute = !drive_absolute && !unc && path.has_prefix("/")
  let root_depth = if drive_absolute { 1 } else if unc { 2 } else { 0 }
  let parts : Array[String] = []
  for part in path.split("/") {
    let segment = part.to_owned()
    match segment {
      "" | "." => ()
      ".." => {
        if parts.length() <= root_depth {
          return None
        }
        ignore(parts.pop())
      }
      _ => parts.push(segment)
    }
  }
  if unc && parts.length() < 2 {
    None
  } else if drive_absolute && parts.length() == 1 {
    Some((parts[0] + "/").to_lower())
  } else if parts.length() == 0 {
    if absolute {
      Some("/")
    } else {
      None
    }
  } else {
    let joined = parts.iter().join("/")
    let normalized = if unc {
      Some("//" + joined)
    } else if absolute {
      Some("/" + joined)
    } else {
      Some(joined)
    }
    match normalized {
      Some(value) if drive_absolute || unc => Some(value.to_lower())
      _ => normalized
    }
  }
}

///|
fn path_is_within(path : String, roots : Array[String]) -> Bool {
  guard normalize_path(path) is Some(candidate) else { return false }
  for raw_root in roots {
    guard normalize_path(raw_root) is Some(root) else { continue }
    if root == "/" && candidate.has_prefix("/") {
      return true
    }
    let drive_root = root.length() == 3 && root[1] == ':' && root[2] == '/'
    if drive_root && candidate.has_prefix(root) {
      return true
    }
    if candidate == root || candidate.has_prefix(root + "/") {
      return true
    }
  }
  false
}

///|
fn path_is_allowed(
  path : String,
  roots : Array[String],
  protected_paths : Array[String],
) -> Bool {
  path_is_within(path, roots) && !path_is_within(path, protected_paths)
}