///|
fn moon_sync_default_remote_root() -> String {
  "moongate-sync"
}

///|
fn moon_sync_default_profile() -> String {
  "default"
}

///|
fn moon_sync_default_status_json() -> Json {
  Json::object({})
}

///|
fn moon_sync_protocol_format() -> String {
  "moongate-webdav-sync"
}

///|
fn moon_sync_protocol_version() -> Int {
  2
}

///|
fn moon_sync_db_compat_version() -> Int {
  6
}

///|
fn moon_sync_remote_db_sql() -> String {
  "db.sql"
}

///|
fn moon_sync_remote_skills_zip() -> String {
  "skills.zip"
}

///|
fn moon_sync_remote_manifest() -> String {
  "manifest.json"
}

///|
fn moon_webdav_default_timeout_ms() -> Int {
  30000
}

///|
fn moon_webdav_transfer_timeout_ms() -> Int {
  300000
}

///|
priv struct MoonSyncManifestInfo {
  format : String
  version : Int
  db_compat_version : Int?
  device_name : String
  created_at : String
  snapshot_id : String
  artifacts : Array[String]
  artifact_meta : Map[String, MoonSyncArtifactMeta]
}

///|
priv struct MoonSyncArtifactMeta {
  sha256 : String
  size : Int
}

///|
priv struct MoonLocalSyncSnapshot {
  db_sql : Bytes
  skills_zip : Bytes
  manifest_bytes : Bytes
  manifest_hash : String
}

///|
priv struct MoonWebDavRemoteSnapshot {
  manifest : MoonSyncManifestInfo
  manifest_bytes : Bytes
}

///|
priv struct MoonS3RemoteSnapshot {
  manifest : MoonSyncManifestInfo
  manifest_bytes : Bytes
  etag : String?
}

///|
priv struct MoonWebDavTextResponse {
  code : Int
  body : String
}

///|
priv struct MoonWebDavCurlRequest {
  program : String
  args : Array[String]
}

///|
fn moon_sync_status_json(
  incoming : Map[String, Json],
  existing : Json?,
) -> Json {
  match existing {
    Some(Object(existing_obj)) =>
      match existing_obj.get("status") {
        Some(Object(status)) => return Json::object(status)
        _ => ()
      }
    _ => ()
  }
  match incoming.get("status") {
    Some(Object(status)) => Json::object(status)
    _ => moon_sync_default_status_json()
  }
}

///|
fn moon_sync_json_string(obj : Map[String, Json], key : String) -> String {
  json_string_from_map(obj, key).unwrap_or("").trim().to_owned()
}

///|
fn moon_sync_json_bool(obj : Map[String, Json], key : String) -> Bool {
  json_bool_from_map(obj, key).unwrap_or(false)
}

///|
fn moon_sync_secret_or_existing(
  obj : Map[String, Json],
  key : String,
  existing : Json?,
  preserve_empty : Bool,
) -> String {
  let incoming = moon_sync_json_string(obj, key)
  if !preserve_empty || !incoming.is_empty() {
    return incoming
  }
  match existing {
    Some(Object(existing_obj)) =>
      json_string_from_map(existing_obj, key).unwrap_or("")
    _ => incoming
  }
}

///|
fn moon_webdav_sync_settings_json(
  settings : Json,
  existing : Json?,
  preserve_empty_password : Bool,
) -> Json {
  guard settings is Object(obj) else {
    return moon_webdav_sync_empty_settings_json()
  }
  let remote_root = moon_sync_json_string(obj, "remoteRoot")
  let profile = moon_sync_json_string(obj, "profile")
  {
    "enabled": Json::boolean(moon_sync_json_bool(obj, "enabled")),
    "autoSync": Json::boolean(moon_sync_json_bool(obj, "autoSync")),
    "baseUrl": Json::string(moon_sync_json_string(obj, "baseUrl")),
    "username": Json::string(moon_sync_json_string(obj, "username")),
    "password": Json::string(
      moon_sync_secret_or_existing(
        obj, "password", existing, preserve_empty_password,
      ),
    ),
    "remoteRoot": Json::string(
      if remote_root.is_empty() {
        moon_sync_default_remote_root()
      } else {
        remote_root
      },
    ),
    "profile": Json::string(
      if profile.is_empty() {
        moon_sync_default_profile()
      } else {
        profile
      },
    ),
    "status": moon_sync_status_json(obj, existing),
  }.to_json()
}

///|
fn moon_webdav_sync_empty_settings_json() -> Json {
  {
    "enabled": Json::boolean(false),
    "autoSync": Json::boolean(false),
    "baseUrl": Json::string(""),
    "username": Json::string(""),
    "password": Json::string(""),
    "remoteRoot": Json::string(moon_sync_default_remote_root()),
    "profile": Json::string(moon_sync_default_profile()),
    "status": moon_sync_default_status_json(),
  }.to_json()
}

///|
fn moon_webdav_sync_settings_empty(settings : Json) -> Bool {
  guard settings is Object(obj) else { return true }
  moon_sync_json_string(obj, "baseUrl").is_empty() &&
  moon_sync_json_string(obj, "username").is_empty() &&
  moon_sync_json_string(obj, "password").is_empty()
}

///|
fn moon_validate_webdav_sync_settings(settings : Json) -> String? {
  guard settings is Object(obj) else { return Some("WebDAV URL is required.") }
  if moon_sync_json_string(obj, "baseUrl").is_empty() {
    return Some("WebDAV URL is required.")
  }
  if moon_sync_json_string(obj, "username").is_empty() {
    return Some("WebDAV username is required.")
  }
  None
}

///|
fn moon_s3_sync_settings_json(
  settings : Json,
  existing : Json?,
  preserve_empty_secret : Bool,
) -> Json {
  guard settings is Object(obj) else {
    return moon_s3_sync_empty_settings_json()
  }
  let remote_root = moon_sync_json_string(obj, "remoteRoot")
  let profile = moon_sync_json_string(obj, "profile")
  {
    "enabled": Json::boolean(moon_sync_json_bool(obj, "enabled")),
    "autoSync": Json::boolean(moon_sync_json_bool(obj, "autoSync")),
    "region": Json::string(moon_sync_json_string(obj, "region")),
    "bucket": Json::string(moon_sync_json_string(obj, "bucket")),
    "accessKeyId": Json::string(moon_sync_json_string(obj, "accessKeyId")),
    "secretAccessKey": Json::string(
      moon_sync_secret_or_existing(
        obj, "secretAccessKey", existing, preserve_empty_secret,
      ),
    ),
    "endpoint": Json::string(moon_sync_json_string(obj, "endpoint")),
    "remoteRoot": Json::string(
      if remote_root.is_empty() {
        moon_sync_default_remote_root()
      } else {
        remote_root
      },
    ),
    "profile": Json::string(
      if profile.is_empty() {
        moon_sync_default_profile()
      } else {
        profile
      },
    ),
    "status": moon_sync_status_json(obj, existing),
  }.to_json()
}

///|
fn moon_s3_sync_empty_settings_json() -> Json {
  {
    "enabled": Json::boolean(false),
    "autoSync": Json::boolean(false),
    "region": Json::string(""),
    "bucket": Json::string(""),
    "accessKeyId": Json::string(""),
    "secretAccessKey": Json::string(""),
    "endpoint": Json::string(""),
    "remoteRoot": Json::string(moon_sync_default_remote_root()),
    "profile": Json::string(moon_sync_default_profile()),
    "status": moon_sync_default_status_json(),
  }.to_json()
}

///|
fn moon_s3_sync_settings_empty(settings : Json) -> Bool {
  guard settings is Object(obj) else { return true }
  moon_sync_json_string(obj, "bucket").is_empty() &&
  moon_sync_json_string(obj, "region").is_empty() &&
  moon_sync_json_string(obj, "accessKeyId").is_empty() &&
  moon_sync_json_string(obj, "secretAccessKey").is_empty()
}

///|
fn moon_validate_s3_sync_settings(settings : Json) -> String? {
  guard settings is Object(obj) else { return Some("S3 bucket is required.") }
  if moon_sync_json_string(obj, "bucket").is_empty() {
    return Some("S3 bucket is required.")
  }
  if moon_sync_json_string(obj, "region").is_empty() {
    return Some("S3 region is required.")
  }
  if moon_sync_json_string(obj, "accessKeyId").is_empty() {
    return Some("S3 Access Key ID is required.")
  }
  if moon_sync_json_string(obj, "secretAccessKey").is_empty() {
    return Some("S3 Secret Access Key is required.")
  }
  None
}

///|
fn moon_app_webdav_sync_json(settings : Json) -> Json? {
  guard settings is Object(obj) else { return None }
  obj.get("webdavSync")
}

///|
fn moon_app_s3_sync_json(settings : Json) -> Json? {
  guard settings is Object(obj) else { return None }
  obj.get("s3Sync")
}

///|
fn moon_app_settings_with_sync(
  settings : Json,
  key : String,
  sync_settings : Json,
) -> Json {
  match settings {
    Object(obj) => {
      obj[key] = sync_settings
      Json::object(obj)
    }
    _ => {
      let obj : Map[String, Json] = {}
      obj[key] = sync_settings
      Json::object(obj)
    }
  }
}

///|
fn moon_sync_settings_request(path : StringView, body : Json?) -> Json? {
  let _ = path
  body
}

///|
fn moon_sync_preserve_empty_password_request(
  path : StringView,
  body : Json?,
) -> Bool {
  proxy_bool_request(path, body, "preserveEmptyPassword").unwrap_or(true)
}

///|
fn moon_sync_password_touched_request(path : StringView, body : Json?) -> Bool {
  proxy_bool_request(path, body, "passwordTouched").unwrap_or(false)
}

///|
fn moon_webdav_sync_required_error(settings : Json) -> String? {
  match moon_app_webdav_sync_json(settings) {
    None => Some("WebDAV sync is not configured.")
    Some(raw) => {
      let normalized = moon_webdav_sync_settings_json(raw, None, false)
      if moon_webdav_sync_settings_empty(normalized) {
        return Some("WebDAV sync is not configured.")
      }
      guard normalized is Object(obj) else {
        return Some("WebDAV sync is not configured.")
      }
      if !moon_sync_json_bool(obj, "enabled") {
        return Some("WebDAV sync is disabled.")
      }
      moon_validate_webdav_sync_settings(normalized)
    }
  }
}

///|
fn moon_s3_sync_required_error(settings : Json) -> String? {
  match moon_app_s3_sync_json(settings) {
    None => Some("S3 sync is not configured.")
    Some(raw) => {
      let normalized = moon_s3_sync_settings_json(raw, None, false)
      if moon_s3_sync_settings_empty(normalized) {
        return Some("S3 sync is not configured.")
      }
      guard normalized is Object(obj) else {
        return Some("S3 sync is not configured.")
      }
      if !moon_sync_json_bool(obj, "enabled") {
        return Some("S3 sync is disabled.")
      }
      moon_validate_s3_sync_settings(normalized)
    }
  }
}

///|
fn moon_sync_path_segments(path : String) -> Array[String] {
  let result : Array[String] = []
  for part in path.split("/") {
    let part = part.trim()
    if !part.is_empty() {
      result.push(part.to_owned())
    }
  }
  result
}

///|
fn moon_sync_remote_dir_segments(
  remote_root : String,
  profile : String,
) -> Array[String] {
  let segs = moon_sync_path_segments(remote_root)
  segs.push("v\{moon_sync_protocol_version()}")
  segs.push("db-v\{moon_sync_db_compat_version()}")
  for part in moon_sync_path_segments(profile) {
    segs.push(part)
  }
  segs
}

///|
fn moon_sync_remote_dir_display(
  remote_root : String,
  profile : String,
) -> String {
  "/\{moon_sync_remote_dir_segments(remote_root, profile).join("/")}"
}

///|
fn moon_sync_remote_file_path(
  remote_root : String,
  profile : String,
  file_name : String,
) -> String {
  let segs = moon_sync_remote_dir_segments(remote_root, profile)
  for part in moon_sync_path_segments(file_name) {
    segs.push(part)
  }
  segs.join("/")
}

///|
fn moon_sync_sha256_hex(bytes : Bytes) -> String {
  let digest = @crypto.sha256(bytes)
  let builder = StringBuilder::new()
  for i in 0.. Char {
  if value < 10 {
    ('0'.to_int() + value).unsafe_to_char()
  } else {
    ('a'.to_int() + value - 10).unsafe_to_char()
  }
}

///|
fn moon_sync_snapshot_id(
  artifacts : Map[String, MoonSyncArtifactMeta],
) -> String {
  let keys = artifacts.keys().collect()
  keys.sort()
  let parts : Array[String] = []
  for key in keys {
    guard artifacts.get(key) is Some(meta) else { continue }
    parts.push("\{key}:\{meta.sha256}")
  }
  moon_sync_sha256_hex(@encoding/utf8.encode(parts.join("|")))
}

///|
fn moon_sync_device_name() -> String {
  let configured = @os.getenv("MOONGATE_DEVICE_NAME") catch { _ => None }
  match configured {
    Some(value) if !value.trim().is_empty() => value.trim().to_owned()
    _ => {
      let hostname = @os.gethostname() catch { _ => "" }
      match hostname {
        "" => "Unknown Device"
        value => value.trim().to_owned()
      }
    }
  }
}

///|
fn moon_sync_artifact_meta(bytes : Bytes) -> MoonSyncArtifactMeta {
  { sha256: moon_sync_sha256_hex(bytes), size: bytes.length() }
}

///|
fn moon_sync_artifact_meta_json(meta : MoonSyncArtifactMeta) -> Json {
  {
    "sha256": Json::string(meta.sha256),
    "size": Json::number(meta.size.to_double()),
  }.to_json()
}

///|
fn moon_sync_manifest_json(
  artifacts : Map[String, MoonSyncArtifactMeta],
  snapshot_id : String,
) -> Json {
  let artifact_json : Map[String, Json] = {}
  let keys = artifacts.keys().collect()
  keys.sort()
  for key in keys {
    guard artifacts.get(key) is Some(meta) else { continue }
    artifact_json[key] = moon_sync_artifact_meta_json(meta)
  }
  {
    "format": Json::string(moon_sync_protocol_format()),
    "version": Json::number(moon_sync_protocol_version().to_double()),
    "dbCompatVersion": Json::number(moon_sync_db_compat_version().to_double()),
    "deviceName": Json::string(moon_sync_device_name()),
    "createdAt": Json::string(@os.unix_seconds().to_string()),
    "artifacts": Json::object(artifact_json),
    "snapshotId": Json::string(snapshot_id),
  }.to_json()
}

///|
fn moon_webdav_encode_path_segment(segment : String) -> String {
  let bytes = @encoding/utf8.encode(segment)
  let builder = StringBuilder::new()
  for i in 0.. Bool {
  (value >= 'A'.to_int() && value <= 'Z'.to_int()) ||
  (value >= 'a'.to_int() && value <= 'z'.to_int()) ||
  (value >= '0'.to_int() && value <= '9'.to_int()) ||
  value == '-'.to_int() ||
  value == '.'.to_int() ||
  value == '_'.to_int() ||
  value == '~'.to_int()
}

///|
fn moon_webdav_hex_digit(value : Int) -> Char {
  if value < 10 {
    ('0'.to_int() + value).unsafe_to_char()
  } else {
    ('A'.to_int() + value - 10).unsafe_to_char()
  }
}

///|
fn moon_webdav_encoded_path(segments : Array[String]) -> String {
  segments.map(moon_webdav_encode_path_segment).join("/")
}

///|
fn moon_webdav_remote_file_url(
  settings : Json,
  file_name : String,
) -> Result[String, String] {
  let remote_segments = match
    moon_webdav_remote_dir_segments_from_settings(settings) {
    Ok(segs) => segs
    Err(error) => return Err(error)
  }
  for part in moon_sync_path_segments(file_name) {
    remote_segments.push(part)
  }
  moon_webdav_remote_url(settings, remote_segments)
}

///|
fn moon_webdav_remote_dir_url(
  settings : Json,
  depth : Int,
) -> Result[String, String] {
  let remote_segments = match
    moon_webdav_remote_dir_segments_from_settings(settings) {
    Ok(segs) => segs
    Err(error) => return Err(error)
  }
  if depth <= 0 {
    return moon_webdav_remote_url(settings, [])
  }
  let limit = if depth > remote_segments.length() {
    remote_segments.length()
  } else {
    depth
  }
  let segs = remote_segments[:limit].to_owned()
  match moon_webdav_remote_url(settings, segs) {
    Ok(url) => Ok(if url.has_suffix("/") { url } else { "\{url}/" })
    Err(error) => Err(error)
  }
}

///|
fn moon_webdav_remote_dir_segments_from_settings(
  settings : Json,
) -> Result[Array[String], String] {
  guard settings is Object(obj) else {
    return Err("WebDAV sync is not configured.")
  }
  guard json_string_from_map(obj, "remoteRoot") is Some(remote_root) else {
    return Err("WebDAV remote root is required.")
  }
  guard json_string_from_map(obj, "profile") is Some(profile) else {
    return Err("WebDAV profile is required.")
  }
  Ok(moon_sync_remote_dir_segments(remote_root, profile))
}

///|
fn moon_webdav_remote_url(
  settings : Json,
  segments : Array[String],
) -> Result[String, String] {
  guard settings is Object(obj) else {
    return Err("WebDAV sync is not configured.")
  }
  guard json_string_from_map(obj, "baseUrl") is Some(base_url) else {
    return Err("WebDAV URL is required.")
  }
  let base_url = trim_end_slashes(base_url.trim().to_owned())
  if base_url.is_empty() {
    return Err("WebDAV URL is required.")
  }
  if segments.is_empty() {
    Ok(base_url)
  } else {
    Ok("\{base_url}/\{moon_webdav_encoded_path(segments)}")
  }
}

///|
fn moon_webdav_basic_auth_header(
  username : String,
  password : String,
) -> String {
  let token = @base64.encode(@encoding/utf8.encode("\{username}:\{password}"))
  "Basic \{token}"
}

///|
fn moon_s3_is_aws_endpoint(endpoint : String) -> Bool {
  endpoint.is_empty() || endpoint.contains("amazonaws.com")
}

///|
fn moon_s3_split_scheme_host(endpoint : String) -> (String, String) {
  let trimmed = trim_end_slashes(endpoint.trim().to_owned())
  if trimmed.has_prefix("http://") {
    ("http", trimmed[7:].to_owned())
  } else if trimmed.has_prefix("https://") {
    ("https", trimmed[8:].to_owned())
  } else {
    ("https", trimmed)
  }
}

///|
fn moon_s3_remote_file_key(
  settings : Json,
  file_name : String,
) -> Result[String, String] {
  guard settings is Object(obj) else {
    return Err("S3 sync is not configured.")
  }
  guard json_string_from_map(obj, "remoteRoot") is Some(remote_root) else {
    return Err("S3 remote root is required.")
  }
  guard json_string_from_map(obj, "profile") is Some(profile) else {
    return Err("S3 profile is required.")
  }
  Ok(moon_sync_remote_file_path(remote_root, profile, file_name))
}

///|
fn moon_s3_remote_dir_display(settings : Json) -> Result[String, String] {
  guard settings is Object(obj) else {
    return Err("S3 sync is not configured.")
  }
  guard json_string_from_map(obj, "remoteRoot") is Some(remote_root) else {
    return Err("S3 remote root is required.")
  }
  guard json_string_from_map(obj, "profile") is Some(profile) else {
    return Err("S3 profile is required.")
  }
  Ok(moon_sync_remote_dir_segments(remote_root, profile).join("/"))
}

///|
fn moon_s3_bucket_url(settings : Json) -> Result[String, String] {
  guard settings is Object(obj) else {
    return Err("S3 sync is not configured.")
  }
  let endpoint = json_string_from_map(obj, "endpoint").unwrap_or("")
  let bucket = json_string_from_map(obj, "bucket").unwrap_or("")
  let region = json_string_from_map(obj, "region").unwrap_or("")
  if bucket.is_empty() {
    return Err("S3 bucket is required.")
  }
  if region.is_empty() {
    return Err("S3 region is required.")
  }
  if moon_s3_is_aws_endpoint(endpoint) {
    Ok("https://\{bucket}.s3.\{region}.amazonaws.com/")
  } else {
    let (scheme, host) = moon_s3_split_scheme_host(endpoint)
    Ok("\{scheme}://\{host}/\{moon_webdav_encode_path_segment(bucket)}/")
  }
}

///|
fn moon_s3_object_url(settings : Json, key : String) -> Result[String, String] {
  guard settings is Object(obj) else {
    return Err("S3 sync is not configured.")
  }
  let endpoint = json_string_from_map(obj, "endpoint").unwrap_or("")
  let bucket = json_string_from_map(obj, "bucket").unwrap_or("")
  let region = json_string_from_map(obj, "region").unwrap_or("")
  if bucket.is_empty() {
    return Err("S3 bucket is required.")
  }
  if region.is_empty() {
    return Err("S3 region is required.")
  }
  let encoded_key = moon_webdav_encoded_path(moon_sync_path_segments(key))
  if moon_s3_is_aws_endpoint(endpoint) {
    Ok("https://\{bucket}.s3.\{region}.amazonaws.com/\{encoded_key}")
  } else {
    let (scheme, host) = moon_s3_split_scheme_host(endpoint)
    Ok(
      "\{scheme}://\{host}/\{moon_webdav_encode_path_segment(bucket)}/\{encoded_key}",
    )
  }
}

///|
fn moon_s3_curl_common_args(
  settings : Json,
  timeout_ms : Int,
) -> Result[Array[String], String] {
  guard settings is Object(obj) else {
    return Err("S3 sync is not configured.")
  }
  let region = json_string_from_map(obj, "region").unwrap_or("")
  let access_key = json_string_from_map(obj, "accessKeyId").unwrap_or("")
  let secret_key = json_string_from_map(obj, "secretAccessKey").unwrap_or("")
  if region.is_empty() {
    return Err("S3 region is required.")
  }
  if access_key.is_empty() {
    return Err("S3 Access Key ID is required.")
  }
  if secret_key.is_empty() {
    return Err("S3 Secret Access Key is required.")
  }
  Ok([
    "-sS",
    "-L",
    "--aws-sigv4",
    "aws:amz:\{region}:s3",
    "-u",
    "\{access_key}:\{secret_key}",
    "--max-time",
    (timeout_ms / 1000).to_string(),
  ])
}

///|
fn moon_s3_curl_head_request(
  settings : Json,
  url : String,
  header_path? : String,
) -> Result[MoonWebDavCurlRequest, String] {
  let args = match
    moon_s3_curl_common_args(settings, moon_webdav_default_timeout_ms()) {
    Ok(args) => args
    Err(error) => return Err(error)
  }
  args.push("-I")
  args.push("-o")
  args.push("/dev/null")
  args.push("-w")
  args.push("%{http_code}")
  if header_path is Some(path) {
    args.push("-D")
    args.push(path)
  }
  args.push(url)
  Ok({ program: "curl", args })
}

///|
fn moon_s3_curl_put_request(
  settings : Json,
  url : String,
  file_path : String,
  content_type : String,
) -> Result[MoonWebDavCurlRequest, String] {
  let args = match
    moon_s3_curl_common_args(settings, moon_webdav_transfer_timeout_ms()) {
    Ok(args) => args
    Err(error) => return Err(error)
  }
  args.push("-X")
  args.push("PUT")
  args.push("-H")
  args.push("Content-Type: \{content_type}")
  args.push("--upload-file")
  args.push(file_path)
  args.push("-o")
  args.push("/dev/null")
  args.push("-w")
  args.push("%{http_code}")
  args.push(url)
  Ok({ program: "curl", args })
}

///|
fn moon_s3_curl_get_request(
  settings : Json,
  url : String,
  output_path : String,
  header_path? : String,
) -> Result[MoonWebDavCurlRequest, String] {
  let args = match
    moon_s3_curl_common_args(settings, moon_webdav_transfer_timeout_ms()) {
    Ok(args) => args
    Err(error) => return Err(error)
  }
  args.push("-o")
  args.push(output_path)
  args.push("-w")
  args.push("%{http_code}")
  if header_path is Some(path) {
    args.push("-D")
    args.push(path)
  }
  args.push(url)
  Ok({ program: "curl", args })
}

///|
async fn moon_webdav_get_text(
  url : String,
  username : String,
  password : String,
) -> Result[MoonWebDavTextResponse, String] {
  let (origin, path) = split_origin_and_path(url)
  let client = @http.Client::Client(origin, headers={
    "Authorization": moon_webdav_basic_auth_header(username, password),
    "Accept": "application/json",
  })
  defer client.close()
  let response = client.get(if path.is_empty() { "/" } else { path }) catch {
    err => return Err("WebDAV GET failed: \{err}")
  }
  Ok({ code: response.code, body: client.read_all().text() })
}

///|
fn moon_webdav_curl_status_request(
  settings : Json,
  webdav_method : String,
  url : String,
  depth_zero? : Bool = false,
) -> Result[MoonWebDavCurlRequest, String] {
  guard settings is Object(obj) else {
    return Err("WebDAV sync is not configured.")
  }
  let args : Array[String] = [
    "-sS", "-L", "-o", "/dev/null", "-w", "%{http_code}", "--max-time", "30", "-X",
    webdav_method,
  ]
  if depth_zero {
    args.push("-H")
    args.push("Depth: 0")
  }
  let username = json_string_from_map(obj, "username").unwrap_or("")
  if !username.is_empty() {
    args.push("-u")
    args.push(
      "\{username}:\{json_string_from_map(obj, "password").unwrap_or("")}",
    )
  }
  args.push(url)
  Ok({ program: "curl", args })
}

///|
fn moon_webdav_curl_put_request(
  settings : Json,
  url : String,
  file_path : String,
  content_type : String,
) -> Result[MoonWebDavCurlRequest, String] {
  guard settings is Object(obj) else {
    return Err("WebDAV sync is not configured.")
  }
  let args : Array[String] = [
    "-sS",
    "-L",
    "-o",
    "/dev/null",
    "-w",
    "%{http_code}",
    "--max-time",
    (moon_webdav_transfer_timeout_ms() / 1000).to_string(),
    "-X",
    "PUT",
    "-H",
    "Content-Type: \{content_type}",
    "--upload-file",
    file_path,
  ]
  let username = json_string_from_map(obj, "username").unwrap_or("")
  if !username.is_empty() {
    args.push("-u")
    args.push(
      "\{username}:\{json_string_from_map(obj, "password").unwrap_or("")}",
    )
  }
  args.push(url)
  Ok({ program: "curl", args })
}

///|
fn moon_webdav_curl_get_request(
  settings : Json,
  url : String,
  output_path : String,
) -> Result[MoonWebDavCurlRequest, String] {
  guard settings is Object(obj) else {
    return Err("WebDAV sync is not configured.")
  }
  let args : Array[String] = [
    "-sS",
    "-L",
    "-o",
    output_path,
    "-w",
    "%{http_code}",
    "--max-time",
    (moon_webdav_transfer_timeout_ms() / 1000).to_string(),
  ]
  let username = json_string_from_map(obj, "username").unwrap_or("")
  if !username.is_empty() {
    args.push("-u")
    args.push(
      "\{username}:\{json_string_from_map(obj, "password").unwrap_or("")}",
    )
  }
  args.push(url)
  Ok({ program: "curl", args })
}

///|
async fn moon_webdav_run_status_request(
  request : MoonWebDavCurlRequest,
  operation : String,
) -> Result[Int, String] {
  let result = @spawn.spawn(
    request.program,
    request.args.map(fn(arg) { arg[:] }),
    timeout=moon_webdav_default_timeout_ms(),
  ) catch {
    err => return Err("WebDAV \{operation} failed: \{err}")
  }
  if result.status != 0 {
    let detail = if result.stderr.trim().is_empty() {
      result.stdout.trim()
    } else {
      result.stderr.trim()
    }
    return Err(
      if detail.is_empty() {
        "WebDAV \{operation} failed: exit code \{result.status}"
      } else {
        "WebDAV \{operation} failed: \{detail}"
      },
    )
  }
  let status = @string.parse_int(result.stdout.trim(), base=10) catch {
    _ => return Err("WebDAV \{operation} returned an invalid HTTP status.")
  }
  Ok(status)
}

///|
async fn moon_s3_run_status_request(
  request : MoonWebDavCurlRequest,
  operation : String,
) -> Result[Int, String] {
  let result = @spawn.spawn(
    request.program,
    request.args.map(fn(arg) { arg[:] }),
    timeout=moon_webdav_transfer_timeout_ms(),
  ) catch {
    err => return Err("S3 \{operation} failed: \{err}")
  }
  if result.status != 0 {
    let detail = if result.stderr.trim().is_empty() {
      result.stdout.trim()
    } else {
      result.stderr.trim()
    }
    return Err(
      if detail.is_empty() {
        "S3 \{operation} failed: exit code \{result.status}"
      } else {
        "S3 \{operation} failed: \{detail}"
      },
    )
  }
  let status = @string.parse_int(result.stdout.trim(), base=10) catch {
    _ => return Err("S3 \{operation} returned an invalid HTTP status.")
  }
  Ok(status)
}

///|
fn moon_webdav_status_ok_for_propfind(status : Int) -> Bool {
  (status >= 200 && status <= 299) || status == 207
}

///|
fn moon_webdav_status_ok_for_mkcol(status : Int) -> Bool {
  status >= 200 && status <= 299
}

///|
fn moon_webdav_status_needs_propfind_verify(status : Int) -> Bool {
  status == 405 || status == 409 || (status >= 300 && status <= 399)
}

///|
fn moon_webdav_status_ok_for_transfer(status : Int) -> Bool {
  status >= 200 && status <= 299
}

///|
fn moon_s3_status_ok(status : Int) -> Bool {
  status >= 200 && status <= 299
}

///|
async fn moon_webdav_sync_test_connection_json(
  settings : Json,
) -> Result[Json, String] {
  let base_url = match moon_webdav_remote_url(settings, []) {
    Ok(url) => url
    Err(error) => return Err(error)
  }
  let propfind = moon_webdav_curl_status_request(
    settings,
    "PROPFIND",
    base_url,
    depth_zero=true,
  )
  let propfind = match propfind {
    Ok(request) => request
    Err(error) => return Err(error)
  }
  let base_status = match moon_webdav_run_status_request(propfind, "PROPFIND") {
    Ok(status) => status
    Err(error) => return Err(error)
  }
  if !moon_webdav_status_ok_for_propfind(base_status) {
    return Err("WebDAV PROPFIND failed: HTTP \{base_status}")
  }
  let dir_segments = match
    moon_webdav_remote_dir_segments_from_settings(settings) {
    Ok(segs) => segs
    Err(error) => return Err(error)
  }
  for depth in 1..<=dir_segments.length() {
    let dir_url = match moon_webdav_remote_dir_url(settings, depth) {
      Ok(url) => url
      Err(error) => return Err(error)
    }
    let mkcol = match
      moon_webdav_curl_status_request(settings, "MKCOL", dir_url) {
      Ok(request) => request
      Err(error) => return Err(error)
    }
    let status = match moon_webdav_run_status_request(mkcol, "MKCOL") {
      Ok(status) => status
      Err(error) => return Err(error)
    }
    if moon_webdav_status_ok_for_mkcol(status) {
      continue
    }
    if moon_webdav_status_needs_propfind_verify(status) {
      let verify = moon_webdav_curl_status_request(
        settings,
        "PROPFIND",
        dir_url,
        depth_zero=true,
      )
      let verify = match verify {
        Ok(request) => request
        Err(error) => return Err(error)
      }
      let verify_status = match
        moon_webdav_run_status_request(verify, "PROPFIND") {
        Ok(status) => status
        Err(error) => return Err(error)
      }
      if moon_webdav_status_ok_for_propfind(verify_status) {
        continue
      }
    }
    return Err("WebDAV MKCOL failed: HTTP \{status}")
  }
  Ok(
    {
      "success": Json::boolean(true),
      "message": Json::string("WebDAV connection ok"),
    }.to_json(),
  )
}

///|
async fn moon_s3_sync_test_connection_json(
  settings : Json,
) -> Result[Json, String] {
  let bucket_url = match moon_s3_bucket_url(settings) {
    Ok(url) => url
    Err(error) => return Err(error)
  }
  let request = match moon_s3_curl_head_request(settings, bucket_url) {
    Ok(request) => request
    Err(error) => return Err(error)
  }
  let status = match moon_s3_run_status_request(request, "HEAD bucket") {
    Ok(status) => status
    Err(error) => return Err(error)
  }
  if moon_s3_status_ok(status) {
    Ok(
      {
        "success": Json::boolean(true),
        "message": Json::string("S3 connection ok"),
      }.to_json(),
    )
  } else {
    Err("S3 HEAD bucket failed: HTTP \{status}")
  }
}

///|
async fn ConfigService::moon_sync_build_local_snapshot(
  self : ConfigService,
  proxy : ProxyService,
  host~ : String,
  port~ : Int,
) -> MoonLocalSyncSnapshot {
  let db_sql = @encoding/utf8.encode(
    self.moon_export_snapshot_json(proxy, host~, port~).stringify(indent=2) +
    "\n",
  )
  let skills_zip = @encoding/utf8.encode(
    moon_skill_state_load().stringify(indent=2) + "\n",
  )
  let artifacts : Map[String, MoonSyncArtifactMeta] = {}
  artifacts[moon_sync_remote_db_sql()] = moon_sync_artifact_meta(db_sql)
  artifacts[moon_sync_remote_skills_zip()] = moon_sync_artifact_meta(skills_zip)
  let snapshot_id = moon_sync_snapshot_id(artifacts)
  let manifest_bytes = @encoding/utf8.encode(
    moon_sync_manifest_json(artifacts, snapshot_id).stringify(indent=2) + "\n",
  )
  {
    db_sql,
    skills_zip,
    manifest_bytes,
    manifest_hash: moon_sync_sha256_hex(manifest_bytes),
  }
}

///|
fn moon_sync_success_status_json(manifest_hash : String) -> Json {
  moon_sync_success_status_json_with_etag(manifest_hash, None)
}

///|
fn moon_sync_success_status_json_with_etag(
  manifest_hash : String,
  etag : String?,
) -> Json {
  {
    "lastSyncAt": Json::number(@os.unix_seconds().to_double()),
    "lastError": Json::null(),
    "lastErrorSource": Json::null(),
    "lastLocalManifestHash": Json::string(manifest_hash),
    "lastRemoteManifestHash": Json::string(manifest_hash),
    "lastRemoteEtag": match etag {
      Some(value) => Json::string(value)
      None => Json::null()
    },
  }.to_json()
}

///|
fn moon_sync_settings_with_status(settings : Json, status : Json) -> Json {
  guard settings is Object(obj) else { return settings }
  obj["status"] = status
  Json::object(obj)
}

///|
fn ConfigService::moon_persist_webdav_sync_success(
  self : ConfigService,
  settings : Json,
  manifest_hash : String,
) -> Unit {
  let next = moon_sync_settings_with_status(
    settings,
    moon_sync_success_status_json(manifest_hash),
  )
  self.app_settings = moon_app_settings_with_sync(
    self.app_settings,
    "webdavSync",
    next,
  )
}

///|
fn ConfigService::moon_persist_s3_sync_success(
  self : ConfigService,
  settings : Json,
  manifest_hash : String,
  etag : String?,
) -> Unit {
  let next = moon_sync_settings_with_status(
    settings,
    moon_sync_success_status_json_with_etag(manifest_hash, etag),
  )
  self.app_settings = moon_app_settings_with_sync(
    self.app_settings,
    "s3Sync",
    next,
  )
}

///|
async fn moon_webdav_put_file(
  settings : Json,
  url : String,
  file_path : String,
  content_type : String,
  artifact_name : String,
) -> Result[Unit, String] {
  let request = match
    moon_webdav_curl_put_request(settings, url, file_path, content_type) {
    Ok(request) => request
    Err(error) => return Err(error)
  }
  let status = match moon_webdav_run_status_request(request, "PUT") {
    Ok(status) => status
    Err(error) => return Err(error)
  }
  if moon_webdav_status_ok_for_transfer(status) {
    Ok(())
  } else {
    Err("WebDAV PUT \{artifact_name} failed: HTTP \{status}")
  }
}

///|
async fn moon_webdav_get_file(
  settings : Json,
  url : String,
  output_path : String,
  artifact_name : String,
) -> Result[Int, String] {
  let request = match moon_webdav_curl_get_request(settings, url, output_path) {
    Ok(request) => request
    Err(error) => return Err(error)
  }
  let status = match moon_webdav_run_status_request(request, "GET") {
    Ok(status) => status
    Err(error) => return Err(error)
  }
  if status == 404 || moon_webdav_status_ok_for_transfer(status) {
    Ok(status)
  } else {
    Err("WebDAV GET \{artifact_name} failed: HTTP \{status}")
  }
}

///|
async fn moon_s3_put_file(
  settings : Json,
  url : String,
  file_path : String,
  content_type : String,
  artifact_name : String,
) -> Result[Unit, String] {
  let request = match
    moon_s3_curl_put_request(settings, url, file_path, content_type) {
    Ok(request) => request
    Err(error) => return Err(error)
  }
  let status = match moon_s3_run_status_request(request, "PUT") {
    Ok(status) => status
    Err(error) => return Err(error)
  }
  if moon_s3_status_ok(status) {
    Ok(())
  } else {
    Err("S3 PUT \{artifact_name} failed: HTTP \{status}")
  }
}

///|
async fn moon_s3_get_file(
  settings : Json,
  url : String,
  output_path : String,
  artifact_name : String,
  header_path? : String,
) -> Result[Int, String] {
  let request = match
    moon_s3_curl_get_request(settings, url, output_path, header_path?) {
    Ok(request) => request
    Err(error) => return Err(error)
  }
  let status = match moon_s3_run_status_request(request, "GET") {
    Ok(status) => status
    Err(error) => return Err(error)
  }
  if status == 404 || moon_s3_status_ok(status) {
    Ok(status)
  } else {
    Err("S3 GET \{artifact_name} failed: HTTP \{status}")
  }
}

///|
async fn moon_s3_head_file_etag(
  settings : Json,
  url : String,
  header_path : String,
) -> Result[String?, String] {
  let request = match moon_s3_curl_head_request(settings, url, header_path~) {
    Ok(request) => request
    Err(error) => return Err(error)
  }
  let status = match moon_s3_run_status_request(request, "HEAD") {
    Ok(status) => status
    Err(error) => return Err(error)
  }
  if status == 404 {
    return Ok(None)
  }
  if !moon_s3_status_ok(status) {
    return Err("S3 HEAD failed: HTTP \{status}")
  }
  Ok(moon_s3_etag_from_headers(header_path))
}

///|
async fn moon_s3_etag_from_headers(header_path : String) -> String? {
  let content = @fs.read_file(header_path).text() catch { _ => return None }
  moon_s3_etag_from_header_text(content)
}

///|
fn moon_s3_etag_from_header_text(content : String) -> String? {
  for line_view in content.split("\n") {
    let line = line_view.to_owned()
    if line.to_lower().has_prefix("etag:") {
      let parts = line.split(":").collect()
      if parts.length() >= 2 {
        let value = parts[1].trim().to_owned()
        if !value.is_empty() {
          return Some(value)
        }
      }
    }
  }
  None
}

///|
async fn ConfigService::moon_webdav_sync_upload_json(
  self : ConfigService,
  proxy : ProxyService,
  host~ : String,
  port~ : Int,
  settings : Json,
) -> Result[Json, String] {
  match moon_webdav_sync_test_connection_json(settings) {
    Ok(_) => ()
    Err(error) => return Err(error)
  }
  let snapshot = self.moon_sync_build_local_snapshot(proxy, host~, port~)
  match
    @fsx.with_temporary_directory("moongate-webdav-upload-XXXXXX", async fn(
      tmp_dir,
    ) {
      let db_path = @pathx.join(tmp_dir, moon_sync_remote_db_sql())
      let skills_path = @pathx.join(tmp_dir, moon_sync_remote_skills_zip())
      let manifest_path = @pathx.join(tmp_dir, moon_sync_remote_manifest())
      @fsx.write_bytes_to_file(db_path, snapshot.db_sql) catch {
        error => return Err("Failed to stage db.sql: \{error}")
      }
      @fsx.write_bytes_to_file(skills_path, snapshot.skills_zip) catch {
        error => return Err("Failed to stage skills.zip: \{error}")
      }
      @fsx.write_bytes_to_file(manifest_path, snapshot.manifest_bytes) catch {
        error => return Err("Failed to stage manifest.json: \{error}")
      }
      let db_url = match
        moon_webdav_remote_file_url(settings, moon_sync_remote_db_sql()) {
        Ok(url) => url
        Err(error) => return Err(error)
      }
      match
        moon_webdav_put_file(
          settings,
          db_url,
          db_path,
          "application/sql",
          moon_sync_remote_db_sql(),
        ) {
        Ok(_) => ()
        Err(error) => return Err(error)
      }
      let skills_url = match
        moon_webdav_remote_file_url(settings, moon_sync_remote_skills_zip()) {
        Ok(url) => url
        Err(error) => return Err(error)
      }
      match
        moon_webdav_put_file(
          settings,
          skills_url,
          skills_path,
          "application/zip",
          moon_sync_remote_skills_zip(),
        ) {
        Ok(_) => ()
        Err(error) => return Err(error)
      }
      let manifest_url = match
        moon_webdav_remote_file_url(settings, moon_sync_remote_manifest()) {
        Ok(url) => url
        Err(error) => return Err(error)
      }
      match
        moon_webdav_put_file(
          settings,
          manifest_url,
          manifest_path,
          "application/json",
          moon_sync_remote_manifest(),
        ) {
        Ok(_) => ()
        Err(error) => return Err(error)
      }
      Ok(())
    }) {
    Ok(_) => ()
    Err(error) => return Err(error)
  }
  self.moon_persist_webdav_sync_success(settings, snapshot.manifest_hash)
  Ok({ "status": Json::string("uploaded") }.to_json())
}

///|
async fn ConfigService::moon_s3_sync_upload_json(
  self : ConfigService,
  proxy : ProxyService,
  host~ : String,
  port~ : Int,
  settings : Json,
) -> Result[Json, String] {
  match moon_s3_sync_test_connection_json(settings) {
    Ok(_) => ()
    Err(error) => return Err(error)
  }
  let snapshot = self.moon_sync_build_local_snapshot(proxy, host~, port~)
  let manifest_key = match
    moon_s3_remote_file_key(settings, moon_sync_remote_manifest()) {
    Ok(key) => key
    Err(error) => return Err(error)
  }
  let manifest_url = match moon_s3_object_url(settings, manifest_key) {
    Ok(url) => url
    Err(error) => return Err(error)
  }
  let mut etag : String? = None
  match
    @fsx.with_temporary_directory("moongate-s3-upload-XXXXXX", async fn(
      tmp_dir,
    ) {
      let db_path = @pathx.join(tmp_dir, moon_sync_remote_db_sql())
      let skills_path = @pathx.join(tmp_dir, moon_sync_remote_skills_zip())
      let manifest_path = @pathx.join(tmp_dir, moon_sync_remote_manifest())
      @fsx.write_bytes_to_file(db_path, snapshot.db_sql) catch {
        error => return Err("Failed to stage db.sql: \{error}")
      }
      @fsx.write_bytes_to_file(skills_path, snapshot.skills_zip) catch {
        error => return Err("Failed to stage skills.zip: \{error}")
      }
      @fsx.write_bytes_to_file(manifest_path, snapshot.manifest_bytes) catch {
        error => return Err("Failed to stage manifest.json: \{error}")
      }
      let db_key = match
        moon_s3_remote_file_key(settings, moon_sync_remote_db_sql()) {
        Ok(key) => key
        Err(error) => return Err(error)
      }
      let db_url = match moon_s3_object_url(settings, db_key) {
        Ok(url) => url
        Err(error) => return Err(error)
      }
      match
        moon_s3_put_file(
          settings,
          db_url,
          db_path,
          "application/sql",
          moon_sync_remote_db_sql(),
        ) {
        Ok(_) => ()
        Err(error) => return Err(error)
      }
      let skills_key = match
        moon_s3_remote_file_key(settings, moon_sync_remote_skills_zip()) {
        Ok(key) => key
        Err(error) => return Err(error)
      }
      let skills_url = match moon_s3_object_url(settings, skills_key) {
        Ok(url) => url
        Err(error) => return Err(error)
      }
      match
        moon_s3_put_file(
          settings,
          skills_url,
          skills_path,
          "application/zip",
          moon_sync_remote_skills_zip(),
        ) {
        Ok(_) => ()
        Err(error) => return Err(error)
      }
      match
        moon_s3_put_file(
          settings,
          manifest_url,
          manifest_path,
          "application/json",
          moon_sync_remote_manifest(),
        ) {
        Ok(_) => ()
        Err(error) => return Err(error)
      }
      let header_path = @pathx.join(tmp_dir, "manifest.headers")
      etag = match moon_s3_head_file_etag(settings, manifest_url, header_path) {
        Ok(value) => value
        Err(_) => None
      }
      Ok(())
    }) {
    Ok(_) => ()
    Err(error) => return Err(error)
  }
  self.moon_persist_s3_sync_success(settings, snapshot.manifest_hash, etag)
  Ok({ "status": Json::string("uploaded") }.to_json())
}

///|
fn moon_webdav_remote_info_from_body(
  body : String,
  settings : Json,
) -> Result[Json, String] {
  guard settings is Object(obj) else {
    return Err("WebDAV sync is not configured.")
  }
  let remote_root = json_string_from_map(obj, "remoteRoot").unwrap_or(
    moon_sync_default_remote_root(),
  )
  let profile = json_string_from_map(obj, "profile").unwrap_or(
    moon_sync_default_profile(),
  )
  let manifest_json = @json.parse(body) catch {
    _ => return Err("Remote manifest is not valid JSON.")
  }
  let manifest = match moon_sync_manifest_info_from_json(manifest_json) {
    Ok(manifest) => manifest
    Err(error) => return Err(error)
  }
  Ok(moon_sync_remote_info_json(manifest, remote_root, profile))
}

///|
async fn moon_webdav_sync_fetch_remote_info_json(
  settings : Json,
) -> Result[Json, String] {
  guard settings is Object(obj) else {
    return Err("WebDAV sync is not configured.")
  }
  let username = json_string_from_map(obj, "username").unwrap_or("")
  let password = json_string_from_map(obj, "password").unwrap_or("")
  let current_url = match
    moon_webdav_remote_file_url(settings, moon_sync_remote_manifest()) {
    Ok(url) => url
    Err(error) => return Err(error)
  }
  let current = match moon_webdav_get_text(current_url, username, password) {
    Ok(response) => response
    Err(error) => return Err(error)
  }
  if current.code >= 200 && current.code <= 299 {
    return moon_webdav_remote_info_from_body(current.body, settings)
  }
  if current.code != 404 {
    return Err("WebDAV GET failed: HTTP \{current.code}")
  }
  Ok({ "empty": Json::boolean(true) }.to_json())
}

///|
async fn moon_s3_fetch_manifest_snapshot(
  settings : Json,
) -> Result[MoonS3RemoteSnapshot, String]? {
  let manifest_key = match
    moon_s3_remote_file_key(settings, moon_sync_remote_manifest()) {
    Ok(key) => key
    Err(error) => return Some(Err(error))
  }
  let manifest_url = match moon_s3_object_url(settings, manifest_key) {
    Ok(url) => url
    Err(error) => return Some(Err(error))
  }
  match
    @fsx.with_temporary_directory("moongate-s3-remote-info-XXXXXX", async fn(
      tmp_dir,
    ) {
      let manifest_path = @pathx.join(tmp_dir, moon_sync_remote_manifest())
      let header_path = @pathx.join(tmp_dir, "manifest.headers")
      let status = match
        moon_s3_get_file(
          settings,
          manifest_url,
          manifest_path,
          moon_sync_remote_manifest(),
          header_path~,
        ) {
        Ok(status) => status
        Err(error) => return Err(error)
      }
      if status == 404 {
        return Ok(None)
      }
      let body = @fs.read_file(manifest_path).text() catch {
        error => return Err("Failed to read remote manifest: \{error}")
      }
      let manifest_json = @json.parse(body) catch {
        _ => return Err("Remote manifest is not valid JSON.")
      }
      let manifest = match moon_sync_manifest_info_from_json(manifest_json) {
        Ok(manifest) => manifest
        Err(error) => return Err(error)
      }
      Ok(
        Some(MoonS3RemoteSnapshot::{
          manifest,
          manifest_bytes: @encoding/utf8.encode(body),
          etag: moon_s3_etag_from_headers(header_path),
        }),
      )
    }) {
    Ok(Some(snapshot)) => Some(Ok(snapshot))
    Ok(None) => None
    Err(error) => Some(Err(error))
  }
}

///|
async fn moon_s3_sync_fetch_remote_info_json(
  settings : Json,
) -> Result[Json, String] {
  match moon_s3_fetch_manifest_snapshot(settings) {
    None => Ok({ "empty": Json::boolean(true) }.to_json())
    Some(Err(error)) => Err(error)
    Some(Ok(remote)) => {
      let remote_path = match moon_s3_remote_dir_display(settings) {
        Ok(path) => path
        Err(error) => return Err(error)
      }
      let compatible = moon_sync_validate_manifest_compat(remote.manifest)
        is Ok(_)
      let db_compat_version = match remote.manifest.db_compat_version {
        Some(value) => Json::number(value.to_double())
        None => Json::null()
      }
      Ok(
        {
          "deviceName": Json::string(remote.manifest.device_name),
          "createdAt": Json::string(remote.manifest.created_at),
          "snapshotId": Json::string(remote.manifest.snapshot_id),
          "version": Json::number(remote.manifest.version.to_double()),
          "protocolVersion": Json::number(remote.manifest.version.to_double()),
          "dbCompatVersion": db_compat_version,
          "compatible": Json::boolean(compatible),
          "artifacts": Json::array(remote.manifest.artifacts.map(Json::string)),
          "layout": Json::string("current"),
          "remotePath": Json::string(remote_path),
        }.to_json(),
      )
    }
  }
}

///|
async fn moon_webdav_fetch_manifest_snapshot(
  settings : Json,
) -> Result[MoonWebDavRemoteSnapshot, String]? {
  guard settings is Object(obj) else {
    return Some(Err("WebDAV sync is not configured."))
  }
  let username = json_string_from_map(obj, "username").unwrap_or("")
  let password = json_string_from_map(obj, "password").unwrap_or("")
  let manifest_url = match
    moon_webdav_remote_file_url(settings, moon_sync_remote_manifest()) {
    Ok(url) => url
    Err(error) => return Some(Err(error))
  }
  let response = match moon_webdav_get_text(manifest_url, username, password) {
    Ok(response) => response
    Err(error) => return Some(Err(error))
  }
  if response.code == 404 {
    return None
  }
  if !(response.code >= 200 && response.code <= 299) {
    return Some(Err("WebDAV GET failed: HTTP \{response.code}"))
  }
  let manifest_json = @json.parse(response.body) catch {
    _ => return Some(Err("Remote manifest is not valid JSON."))
  }
  let manifest = match moon_sync_manifest_info_from_json(manifest_json) {
    Ok(manifest) => manifest
    Err(error) => return Some(Err(error))
  }
  Some(Ok({ manifest, manifest_bytes: @encoding/utf8.encode(response.body) }))
}

///|
async fn moon_webdav_find_remote_snapshot(
  settings : Json,
) -> Result[MoonWebDavRemoteSnapshot, String]? {
  moon_webdav_fetch_manifest_snapshot(settings)
}

///|
fn moon_sync_validate_artifact(
  bytes : Bytes,
  artifact_name : String,
  manifest : MoonSyncManifestInfo,
) -> Result[Unit, String] {
  guard manifest.artifact_meta.get(artifact_name) is Some(meta) else {
    return Err("Manifest missing artifact: \{artifact_name}")
  }
  if bytes.length() != meta.size {
    return Err(
      "Artifact \{artifact_name} size mismatch (expected: \{meta.size}, got: \{bytes.length()})",
    )
  }
  let actual_hash = moon_sync_sha256_hex(bytes)
  if actual_hash != meta.sha256 {
    return Err("Artifact \{artifact_name} SHA256 verification failed")
  }
  Ok(())
}

///|
async fn moon_webdav_download_artifact(
  settings : Json,
  artifact_name : String,
  output_path : String,
) -> Result[Bytes, String] {
  let url = match moon_webdav_remote_file_url(settings, artifact_name) {
    Ok(url) => url
    Err(error) => return Err(error)
  }
  let status = match
    moon_webdav_get_file(settings, url, output_path, artifact_name) {
    Ok(status) => status
    Err(error) => return Err(error)
  }
  if status == 404 {
    return Err("Remote artifact file missing: \{artifact_name}")
  }
  let content = @fs.read_file(output_path).text() catch {
    error => return Err("Failed to read downloaded \{artifact_name}: \{error}")
  }
  Ok(@encoding/utf8.encode(content))
}

///|
async fn moon_s3_download_artifact(
  settings : Json,
  artifact_name : String,
  output_path : String,
) -> Result[Bytes, String] {
  let key = match moon_s3_remote_file_key(settings, artifact_name) {
    Ok(key) => key
    Err(error) => return Err(error)
  }
  let url = match moon_s3_object_url(settings, key) {
    Ok(url) => url
    Err(error) => return Err(error)
  }
  let status = match
    moon_s3_get_file(settings, url, output_path, artifact_name) {
    Ok(status) => status
    Err(error) => return Err(error)
  }
  if status == 404 {
    return Err("Remote artifact file missing: \{artifact_name}")
  }
  let content = @fs.read_file(output_path).text() catch {
    error => return Err("Failed to read downloaded \{artifact_name}: \{error}")
  }
  Ok(@encoding/utf8.encode(content))
}

///|
async fn ConfigService::moon_apply_webdav_snapshot(
  self : ConfigService,
  proxy : ProxyService,
  db_sql : Bytes,
  skills_zip : Bytes,
) -> Result[Unit, String] {
  let db_text = @encoding/utf8.decode(db_sql) catch {
    _ => return Err("Downloaded db.sql is not valid UTF-8.")
  }
  let snapshot = @json.parse(db_text) catch {
    error =>
      return Err("Downloaded db.sql is not valid MoonGate JSON: \{error}")
  }
  let skills_text = @encoding/utf8.decode(skills_zip) catch {
    _ => return Err("Downloaded skills.zip is not valid UTF-8.")
  }
  let skills = @json.parse(skills_text) catch {
    error =>
      return Err("Downloaded skills.zip is not valid MoonGate JSON: \{error}")
  }
  ignore(moon_skill_state_save(skills))
  self.moon_apply_import_snapshot(proxy, snapshot)
  self.save_settings_store(self.app_settings) catch {
    error => return Err("Failed to persist downloaded settings: \{error}")
  }
  proxy.save_provider_store() catch {
    error => return Err("Failed to persist downloaded providers: \{error}")
  }
  proxy.save_proxy_store() catch {
    error => return Err("Failed to persist downloaded proxy settings: \{error}")
  }
  Ok(())
}

///|
async fn ConfigService::moon_webdav_sync_download_json(
  self : ConfigService,
  proxy : ProxyService,
  settings : Json,
) -> Result[Json, String] {
  let remote = match moon_webdav_find_remote_snapshot(settings) {
    Some(Ok(remote)) => remote
    Some(Err(error)) => return Err(error)
    None => return Err("No downloadable sync data found on the remote.")
  }
  match moon_sync_validate_manifest_compat(remote.manifest) {
    Ok(_) => ()
    Err(error) => return Err(error)
  }
  let manifest_hash = moon_sync_sha256_hex(remote.manifest_bytes)
  match
    @fsx.with_temporary_directory("moongate-webdav-download-XXXXXX", async fn(
      tmp_dir,
    ) {
      let db_path = @pathx.join(tmp_dir, moon_sync_remote_db_sql())
      let skills_path = @pathx.join(tmp_dir, moon_sync_remote_skills_zip())
      let db_sql = match
        moon_webdav_download_artifact(
          settings,
          moon_sync_remote_db_sql(),
          db_path,
        ) {
        Ok(bytes) => bytes
        Err(error) => return Err(error)
      }
      match
        moon_sync_validate_artifact(
          db_sql,
          moon_sync_remote_db_sql(),
          remote.manifest,
        ) {
        Ok(_) => ()
        Err(error) => return Err(error)
      }
      let skills_zip = match
        moon_webdav_download_artifact(
          settings,
          moon_sync_remote_skills_zip(),
          skills_path,
        ) {
        Ok(bytes) => bytes
        Err(error) => return Err(error)
      }
      match
        moon_sync_validate_artifact(
          skills_zip,
          moon_sync_remote_skills_zip(),
          remote.manifest,
        ) {
        Ok(_) => ()
        Err(error) => return Err(error)
      }
      self.moon_apply_webdav_snapshot(proxy, db_sql, skills_zip)
    }) {
    Ok(_) => ()
    Err(error) => return Err(error)
  }
  self.moon_persist_webdav_sync_success(settings, manifest_hash)
  guard settings is Object(obj) else {
    return Ok({ "status": Json::string("downloaded") }.to_json())
  }
  let remote_root = json_string_from_map(obj, "remoteRoot").unwrap_or(
    moon_sync_default_remote_root(),
  )
  let profile = json_string_from_map(obj, "profile").unwrap_or(
    moon_sync_default_profile(),
  )
  Ok(
    {
      "status": Json::string("downloaded"),
      "sourceLayout": Json::string("current"),
      "sourcePath": Json::string(
        moon_sync_remote_dir_display(remote_root, profile),
      ),
    }.to_json(),
  )
}

///|
async fn ConfigService::moon_s3_sync_download_json(
  self : ConfigService,
  proxy : ProxyService,
  settings : Json,
) -> Result[Json, String] {
  let remote = match moon_s3_fetch_manifest_snapshot(settings) {
    Some(Ok(remote)) => remote
    Some(Err(error)) => return Err(error)
    None => return Err("No downloadable sync data found on the remote.")
  }
  match moon_sync_validate_manifest_compat(remote.manifest) {
    Ok(_) => ()
    Err(error) => return Err(error)
  }
  let manifest_hash = moon_sync_sha256_hex(remote.manifest_bytes)
  match
    @fsx.with_temporary_directory("moongate-s3-download-XXXXXX", async fn(
      tmp_dir,
    ) {
      let db_path = @pathx.join(tmp_dir, moon_sync_remote_db_sql())
      let skills_path = @pathx.join(tmp_dir, moon_sync_remote_skills_zip())
      let db_sql = match
        moon_s3_download_artifact(settings, moon_sync_remote_db_sql(), db_path) {
        Ok(bytes) => bytes
        Err(error) => return Err(error)
      }
      match
        moon_sync_validate_artifact(
          db_sql,
          moon_sync_remote_db_sql(),
          remote.manifest,
        ) {
        Ok(_) => ()
        Err(error) => return Err(error)
      }
      let skills_zip = match
        moon_s3_download_artifact(
          settings,
          moon_sync_remote_skills_zip(),
          skills_path,
        ) {
        Ok(bytes) => bytes
        Err(error) => return Err(error)
      }
      match
        moon_sync_validate_artifact(
          skills_zip,
          moon_sync_remote_skills_zip(),
          remote.manifest,
        ) {
        Ok(_) => ()
        Err(error) => return Err(error)
      }
      self.moon_apply_webdav_snapshot(proxy, db_sql, skills_zip)
    }) {
    Ok(_) => ()
    Err(error) => return Err(error)
  }
  self.moon_persist_s3_sync_success(settings, manifest_hash, remote.etag)
  let remote_path = match moon_s3_remote_dir_display(settings) {
    Ok(path) => path
    Err(_) => ""
  }
  Ok(
    {
      "status": Json::string("downloaded"),
      "sourceLayout": Json::string("current"),
      "sourcePath": Json::string(remote_path),
    }.to_json(),
  )
}

///|
fn moon_sync_manifest_info_from_json(
  manifest_json : Json,
) -> Result[MoonSyncManifestInfo, String] {
  guard manifest_json is Object(obj) else {
    return Err("Remote manifest is not a JSON object.")
  }
  guard json_string_from_map(obj, "format") is Some(format) else {
    return Err("Remote manifest is missing format.")
  }
  guard proxy_json_int(obj, "version") is Some(version) else {
    return Err("Remote manifest is missing protocol version.")
  }
  guard json_string_from_map(obj, "deviceName") is Some(device_name) else {
    return Err("Remote manifest is missing device name.")
  }
  guard json_string_from_map(obj, "createdAt") is Some(created_at) else {
    return Err("Remote manifest is missing creation time.")
  }
  guard json_string_from_map(obj, "snapshotId") is Some(snapshot_id) else {
    return Err("Remote manifest is missing snapshot id.")
  }
  guard obj.get("artifacts") is Some(Object(artifact_map)) else {
    return Err("Remote manifest is missing artifacts.")
  }
  let artifacts = artifact_map.keys().collect()
  artifacts.sort()
  let artifact_meta : Map[String, MoonSyncArtifactMeta] = {}
  for name in artifacts {
    guard artifact_map.get(name) is Some(Object(meta_obj)) else {
      return Err("Remote manifest artifact \{name} is not a JSON object.")
    }
    guard json_string_from_map(meta_obj, "sha256") is Some(sha256) else {
      return Err("Remote manifest artifact \{name} is missing sha256.")
    }
    guard proxy_json_int(meta_obj, "size") is Some(size) else {
      return Err("Remote manifest artifact \{name} is missing size.")
    }
    artifact_meta[name] = { sha256, size }
  }
  Ok({
    format,
    version,
    db_compat_version: proxy_json_int(obj, "dbCompatVersion"),
    device_name,
    created_at,
    snapshot_id,
    artifacts,
    artifact_meta,
  })
}

///|
fn moon_sync_validate_manifest_compat(
  manifest : MoonSyncManifestInfo,
) -> Result[Unit, String] {
  if manifest.format != moon_sync_protocol_format() {
    return Err("Remote manifest format is incompatible: \{manifest.format}")
  }
  if manifest.version != moon_sync_protocol_version() {
    return Err(
      "Remote manifest protocol version is incompatible: v\{manifest.version} (local v\{moon_sync_protocol_version()})",
    )
  }
  guard manifest.db_compat_version is Some(db_compat_version) else {
    return Err("Remote manifest is missing the database compatibility version.")
  }
  if db_compat_version != moon_sync_db_compat_version() {
    return Err(
      "Remote database snapshot version is incompatible: db-v\{db_compat_version} (local db-v\{moon_sync_db_compat_version()})",
    )
  }
  Ok(())
}

///|
fn moon_sync_remote_info_json(
  manifest : MoonSyncManifestInfo,
  remote_root : String,
  profile : String,
) -> Json {
  let compatible = moon_sync_validate_manifest_compat(manifest) is Ok(_)
  let db_compat_version = match manifest.db_compat_version {
    Some(value) => Json::number(value.to_double())
    None => Json::null()
  }
  {
    "deviceName": Json::string(manifest.device_name),
    "createdAt": Json::string(manifest.created_at),
    "snapshotId": Json::string(manifest.snapshot_id),
    "version": Json::number(manifest.version.to_double()),
    "protocolVersion": Json::number(manifest.version.to_double()),
    "dbCompatVersion": db_compat_version,
    "compatible": Json::boolean(compatible),
    "artifacts": Json::array(manifest.artifacts.map(Json::string)),
    "layout": Json::string("current"),
    "remotePath": Json::string(
      moon_sync_remote_dir_display(remote_root, profile),
    ),
  }.to_json()
}