// 客户端核心:实例构造、URL 拼接、请求发送与状态检查。

///|
/// WebDAV 客户端:全部操作以方法形式挂在本类型上。
/// 通过 `create_webdav_client` 构造(根包重导出)。
pub struct WebdavClient {
  priv http : @moonhttp.Client
  priv base_url : String
  /// 423 自动重试开关(来自 `WebdavConfig.retry_on_423`)
  priv retry_on_423 : Bool
  /// 本客户端加过的锁(进程内、有界),供 423 自动重试找覆盖锁的令牌
  priv mut known_locks : Array[KnownLock]
}

///|
/// 规范化根地址:去掉末尾斜杠("http://h/dav/" → "http://h/dav")。
fn normalize_base(base : String) -> String {
  if base.has_suffix("/") && base.length() > 1 {
    base[:base.length() - 1].to_owned()
  } else {
    base
  }
}

///|
/// 从 base_url 提取服务器上的挂载路径前缀:
/// "http://h:8080/dav" → "/dav","http://h" → ""。
/// 用于把 PROPFIND 返回的 href 与用户路径对齐。
fn base_path_prefix(base : String) -> String {
  let segments : Array[StringView] = base.split("/").collect()
  if segments.length() <= 3 {
    ""
  } else {
    let buf = StringBuilder()
    for i in 3.. @config.Config {
  let cfg = @config.Config::default()
    .with_base_url(normalize_base(config.base_url))
    .with_timeout(config.timeout_ms.unwrap_or(@types.DEFAULT_TIMEOUT_MS))
    .with_validate_status(fn(_) { true })
  match config.username {
    Some(user) => cfg.with_auth(user, config.password.unwrap_or(""))
    None => cfg
  }
}

///|
/// 创建 WebDAV 客户端(moonhttp 自研 HTTP/1.1 传输层)。
pub fn create_webdav_client(config : @types.WebdavConfig) -> WebdavClient {
  let transport : &@transport.Transport = @httpconn.HttpConnTransport::new()
  create_webdav_client_with_transport(config, transport)
}

///|
/// 创建使用指定传输层的客户端:测试注入 MockTransport,
/// 或自定义连接 / 代理行为。
pub fn create_webdav_client_with_transport(
  config : @types.WebdavConfig,
  transport : &@transport.Transport,
) -> WebdavClient {
  let defaults = build_defaults(config)
  {
    http: @moonhttp.Client::new(config=defaults, transport~),
    base_url: normalize_base(config.base_url),
    retry_on_423: config.retry_on_423.unwrap_or(false),
    known_locks: [],
  }
}

///|
/// 拼接请求 URL:规范化后的 base_url + percent 编码后的路径。
fn WebdavClient::url_for(self : WebdavClient, path : String) -> String {
  let normalized = if path.has_prefix("/") { path } else { "/" + path }
  self.base_url + @types.percent_encode_path(normalized)
}

///|
/// 发送文本请求体(或无请求体)的请求并校验状态码。
/// XML 类操作(PROPFIND / PROPPATCH)与轻量探测(OPTIONS / HEAD)走这里。
async fn WebdavClient::send(
  self : WebdavClient,
  operation~ : String,
  http_method~ : @config.Method,
  path~ : String,
  headers? : Array[(String, String)],
  body? : String,
  conditions? : @types.Conditions,
  signal? : @moonhttp.AbortSignal,
  ok~ : Array[Int32],
) -> @moonhttp.Response raise @types.WebdavError {
  let url = self.url_for(path)
  let cfg = build_config(url, http_method, headers, body, conditions, operation)
  let mut response = self.perform(cfg, signal?)
  if response.status == 423 && self.retry_on_423 {
    // 被锁挡住且本客户端自己持有覆盖锁:补上令牌重试一次。
    match self.covering_lock_token(path) {
      Some(token) => {
        let retry_conditions = conditions
          .unwrap_or(@types.Conditions::new())
          .with_lock_token(token)
        let retry_cfg = build_config(
          url,
          http_method,
          headers,
          body,
          Some(retry_conditions),
          operation,
        )
        response = self.perform(retry_cfg, signal?)
      }
      None => ()
    }
  }
  check_status(response, operation~, ok~)
}

///|
/// 组装一次请求的配置(方法 / 头 / 体 / 条件头),供 423 重试时原样重建。
fn build_config(
  url : String,
  http_method : @config.Method,
  headers : Array[(String, String)]?,
  body : String?,
  conditions : @types.Conditions?,
  operation : String,
) -> @config.Config raise @types.WebdavError {
  let mut cfg = @config.Config::new(url).with_method(http_method)
  match headers {
    Some(pairs) =>
      for pair in pairs {
        let (name, value) = pair
        cfg = cfg.with_header(name, value)
      }
    None => ()
  }
  match body {
    Some(xml) => cfg = cfg.with_data_from_str(xml)
    None => ()
  }
  apply_conditions(cfg, conditions, operation~, url~)
}

///|
/// 发一次请求并把 moonhttp 的错误映射成本模块错误。
/// 状态码校验留给调用方(423 自动重试需要先看到响应)。
async fn WebdavClient::perform(
  self : WebdavClient,
  cfg : @config.Config,
  signal? : @moonhttp.AbortSignal,
) -> @moonhttp.Response raise @types.WebdavError {
  self.http.request(cfg, signal?) catch {
    error => raise map_http_error(error, signal_reason(signal))
  }
}

///|
/// 把条件请求对象落到请求头上:`If-Match` / `If-None-Match`,以及锁令牌与
/// 通用条件列表所需的 `If` 头(渲染规则见 if_header.mbt)。
/// 归一化后为空的锁令牌视为未设置;没有任何条件时不动 `cfg`。
fn apply_conditions(
  cfg : @config.Config,
  conditions : @types.Conditions?,
  operation~ : String,
  url~ : String,
) -> @config.Config raise @types.WebdavError {
  match conditions {
    Some(cond) => {
      let mut out = cfg
      match cond.if_match {
        Some(etag) => out = out.with_header("If-Match", etag)
        None => ()
      }
      match cond.if_none_match {
        Some(etag) => out = out.with_header("If-None-Match", etag)
        None => ()
      }
      let if_value = render_if_header(cond, url~, operation~)
      if if_value != "" {
        out = out.with_header("If", if_value)
      }
      out
    }
    None => cfg
  }
}

///|
/// 取消信号给出的原因(未提供原因时为 `None`)。
fn signal_reason(signal : @moonhttp.AbortSignal?) -> String? {
  match signal {
    Some(sig) => sig.reason()
    None => None
  }
}

///|
/// 把 moonhttp 的错误映射成本模块的统一错误:取消单独成一类,
/// 其余归入网络 / HTTP 层。
fn map_http_error(
  error : @moonhttp.HttpError,
  reason : String?,
) -> @types.WebdavError {
  if error.is_cancelled() {
    match reason {
      Some(text) => @types.WebdavError::cancelled(reason=text)
      None => @types.WebdavError::cancelled()
    }
  } else {
    @types.WebdavError::http(error)
  }
}

///|
/// 把对外的 `Progress` 回调适配为 moonhttp 的 `ProgressEvent` 回调。
fn adapt_progress(
  on_progress : ((@types.Progress) -> Unit)?,
) -> ((@config.ProgressEvent) -> Unit)? {
  match on_progress {
    Some(report) =>
      Some(fn(event) { report({ loaded: event.loaded, total: event.total, }) })
    None => None
  }
}