///|
/// 归档、分片上传、直链与任务相关的模型。
///
/// 和别的模型一样手写容错解码:Go 会内嵌结构体(归档内容项里内嵌 `ObjResp`)、
/// 把 `nil` 切片序列化成 `null`,还有 `int64` 与 JSON 数字的差异。

///|
/// 归档条目的内容项(`POST /api/fs/archive/meta` 的文件树节点)。
///
/// 与 `FsGetResponse` 一样,Go 内嵌了 `ObjResp`;这里放在 `obj` 字段里。
pub struct ArchiveContent {
  /// 该条目对应的文件对象。
  obj : ObjResp
  /// 子条目(只有目录才有)。
  children : Array[ArchiveContent]
}

///|
/// 解码归档内容项。
pub impl @json.FromJson for ArchiveContent with fn from_json(json, path) {
  let obj : ObjResp = @json.from_json(json, path~)
  guard json is Object(fields) else {
    raise @json.JsonDecodeError((path, "ArchiveContent: expected object"))
  }
  { obj, children: array_field(fields, "children"), }
}

///|
/// 显式声明手写的 `FromJson` 实现以普通方法暴露。
pub extend ArchiveContent with @json.FromJson::{from_json}

///|
/// `POST /api/fs/archive/meta` 的响应。
pub struct ArchiveMetaResponse {
  /// 归档文件自身的注释。
  comment : String
  /// 是否加密(需要 `archive_pass`)。
  encrypted : Bool
  /// 顶层条目。
  content : Array[ArchiveContent]
  /// 服务端记录的排序设置;未提供时是 `None`。
  sort : ListSort?
  /// 归档文件的带签名直链。
  raw_url : String
  /// 签名。
  sign : String
}

///|
/// 解码归档元数据响应。
pub impl @json.FromJson for ArchiveMetaResponse with fn from_json(json, path) {
  guard json is Object(fields) else {
    raise @json.JsonDecodeError((path, "ArchiveMetaResponse: expected object"))
  }
  {
    comment: string_field(fields, "comment"),
    encrypted: bool_field(fields, "encrypted"),
    content: array_field(fields, "content"),
    sort: json_field(fields, "sort"),
    raw_url: string_field(fields, "raw_url"),
    sign: string_field(fields, "sign"),
  }
}

///|
/// 显式声明手写的 `FromJson` 实现以普通方法暴露。
pub extend ArchiveMetaResponse with @json.FromJson::{from_json}

///|
/// 分片上传会话的快照(`/api/fs/multipart/*` 的响应载荷)。
pub struct SessionSnapshot {
  /// 会话 ID,后续分片请求放在 `X-Upload-Id` 头里。
  upload_id : String
  /// 会话状态。
  state : String
  /// 第几次尝试(服务端重试过会增加)。
  attempt : Int
  /// 目标路径。
  path : String
  /// 文件总大小(字节)。
  size : Int64
  /// 每片大小(字节)。
  chunk_size : Int64
  /// 分片总数。
  total_chunks : Int
  /// 已收到的分片区间,每项是 `[起始, 结束]`(闭区间,单位是分片序号)。
  received : Array[Array[Int]]
  /// 已收到的字节数。
  received_bytes : Int64
  /// 已连续收到的分片前沿。
  frontier : Int
  /// 写入存储的进度百分比。
  storage_progress : Double
  /// 失败原因;没有错误时是 `None`。
  error : String?
}

///|
/// 解码分片上传会话快照。
pub impl @json.FromJson for SessionSnapshot with fn from_json(json, path) {
  guard json is Object(fields) else {
    raise @json.JsonDecodeError((path, "SessionSnapshot: expected object"))
  }
  {
    upload_id: string_field(fields, "upload_id"),
    state: string_field(fields, "state"),
    attempt: int_field(fields, "attempt"),
    path: string_field(fields, "path"),
    size: int64_field(fields, "size"),
    chunk_size: int64_field(fields, "chunk_size"),
    total_chunks: int_field(fields, "total_chunks"),
    received: array_field(fields, "received"),
    received_bytes: int64_field(fields, "received_bytes"),
    frontier: int_field(fields, "frontier"),
    storage_progress: double_field(fields, "storage_progress"),
    error: json_field(fields, "error"),
  }
}

///|
/// 显式声明手写的 `FromJson` 实现以普通方法暴露。
pub extend SessionSnapshot with @json.FromJson::{from_json}

///|
/// `POST /api/fs/multipart/init` 的响应:会话快照 + 是否是「续传」。
pub struct MultipartInitResponse {
  /// 新建立或续用的会话快照。
  snapshot : SessionSnapshot
  /// 是否是复用了之前的会话(续传)。
  resumed : Bool
}

///|
/// 解码分片上传初始化响应。
pub impl @json.FromJson for MultipartInitResponse with fn from_json(json, path) {
  let snapshot : SessionSnapshot = @json.from_json(json, path~)
  guard json is Object(fields) else {
    raise @json.JsonDecodeError(
      (path, "MultipartInitResponse: expected object"),
    )
  }
  { snapshot, resumed: bool_field(fields, "resumed"), }
}

///|
/// 显式声明手写的 `FromJson` 实现以普通方法暴露。
pub extend MultipartInitResponse with @json.FromJson::{from_json}

///|
/// 直链(`POST /api/fs/link` 的响应)。
///
/// 服务端在驱动不支持直链时只会填 `url`;支持多线程加速的驱动还会给出
/// `concurrency` 与 `part_size`。
pub struct Link {
  /// 直链地址;驱动返回空时表示不能直链,只能走 `/d/` 下载接口。
  url : String
  /// 直链需要的额外请求头。
  header : Map[String, Array[String]]
  /// 建议的并发数。
  concurrency : Int
  /// 建议的分片大小(字节)。
  part_size : Int
  /// 内容长度(字节);未知时是 0。
  content_length : Int64
}

///|
/// 解码直链。
pub impl @json.FromJson for Link with fn from_json(json, path) {
  guard json is Object(fields) else {
    raise @json.JsonDecodeError((path, "Link: expected object"))
  }
  {
    url: string_field(fields, "url"),
    header: map_field(fields, "header"),
    concurrency: int_field(fields, "concurrency"),
    part_size: int_field(fields, "part_size"),
    content_length: int64_field(fields, "content_length"),
  }
}

///|
/// 显式声明手写的 `FromJson` 实现以普通方法暴露。
pub extend Link with @json.FromJson::{from_json}

///|
/// 异步任务的描述(`/api/fs/put` 带 `As-Task: true` 时随响应返回)。
///
/// 本客户端没有封装 `/api/task/*` 的任务管理端点,这个模型只用来读取
/// 「任务已创建」的结果(任务 ID、状态、进度)。
pub struct TaskInfo {
  /// 任务 ID。
  id : String
  /// 任务名(通常是文件名)。
  name : String
  /// 创建者用户名。
  creator : String
  /// 创建者角色:`0` 管理员、`1` 普通用户、`2` 游客。
  creator_role : Int
  /// 状态枚举(`pending`、`running`、`succeeded`、`failed` 等)。
  state : String
  /// 人类可读的状态描述。
  status : String
  /// 进度百分比。
  progress : Double
  /// 开始时间(RFC3339 字符串);未开始时是 `None`。
  start_time : String?
  /// 结束时间(RFC3339 字符串);未结束时是 `None`。
  end_time : String?
  /// 总字节数。
  total_bytes : Int64
  /// 失败原因;没有错误时是空字符串。
  error : String
}

///|
/// 解码任务描述。
pub impl @json.FromJson for TaskInfo with fn from_json(json, path) {
  guard json is Object(fields) else {
    raise @json.JsonDecodeError((path, "TaskInfo: expected object"))
  }
  {
    id: string_field(fields, "id"),
    name: string_field(fields, "name"),
    creator: string_field(fields, "creator"),
    creator_role: int_field(fields, "creator_role"),
    state: string_field(fields, "state"),
    status: string_field(fields, "status"),
    progress: double_field(fields, "progress"),
    start_time: json_field(fields, "start_time"),
    end_time: json_field(fields, "end_time"),
    total_bytes: int64_field(fields, "total_bytes"),
    error: string_field(fields, "error"),
  }
}

///|
/// 显式声明手写的 `FromJson` 实现以普通方法暴露。
pub extend TaskInfo with @json.FromJson::{from_json}