///|
fn default_protocol() -> Protocol {
  { min_reader_version: 1, min_writer_version: 1, reader_features: [], writer_features: [], }
}

///|
fn empty_schema() -> DeltaSchema {
  { fields: [], raw_json: "", }
}

///|
fn empty_metadata() -> TableInfo {
  {
    id: "",
    name: None,
    schema: empty_schema(),
    partition_columns: [],
    format_provider: "parquet",
    configuration: [],
  }
}

///|
fn upsert_file(files : Array[AddFile], f : AddFile) -> Unit {
  let rebuilt : Array[AddFile] = []
  let mut replaced = false
  for x in files {
    if x.path == f.path {
      rebuilt.push(f)
      replaced = true
    } else {
      rebuilt.push(x)
    }
  }
  if !replaced {
    rebuilt.push(f)
  }
  files.clear()
  for x in rebuilt {
    files.push(x)
  }
}

///|
fn drop_file(files : Array[AddFile], path : String) -> Unit {
  let kept : Array[AddFile] = []
  for x in files {
    if x.path != path {
      kept.push(x)
    }
  }
  files.clear()
  for x in kept {
    files.push(x)
  }
}

///|
/// add.stats 的 numRecords。stats 损坏时显式报错——行数统计不允许静默失真。
fn stats_num_records(stats : String?) -> Int64 raise DeltaError {
  match stats {
    Some(s) => {
      let j = @json.parse(s) catch {
        _ =>
          raise DeltaError::ParseError(
            "add.stats is not valid JSON: '\{s}'",
          )
      }
      match json_field_int64(j, "numRecords") {
        Some(v) => v
        None =>
          raise DeltaError::ParseError(
            "add.stats is missing integer field 'numRecords': '\{s}'",
          )
      }
    }
    None => 0L
  }
}

fn feature_list_text(names : Array[String]) -> String {
  let sb = StringBuilder()
  let mut i = 0
  while i < names.length() {
    if i > 0 {
      sb.write_char(',')
    }
    sb.write_string(names[i])
    i = i + 1
  }
  sb.to_string()
}

///|
/// 协议版本与 feature 校验(读取端):
/// - 只支持 reader protocol version 1;更高的 reader 版本门控 column mapping、
///   删除向量与 v2 checkpoint,必须拒绝并说明原因(Delta 规范行为)。
/// - reader=1 时不得携带 readerFeatures;writer=7 以下不得携带 writerFeatures
///   (解析期已拦截,这里兜底)。
fn validate_protocol(p : Protocol) -> Unit raise DeltaError {
  if p.min_reader_version != 1 {
    raise DeltaError::Unsupported(
      "table requires reader protocol version \{p.min_reader_version}; this kernel implements reader version 1 only (higher reader versions gate column mapping, deletion vectors and v2 checkpoints)",
    )
  }
  if p.reader_features.length() > 0 {
    raise DeltaError::Unsupported(
      "table declares readerFeatures [\{feature_list_text(p.reader_features)}]; reader version 1 has no feature list",
    )
  }
  if p.min_writer_version > 2 {
    raise DeltaError::Unsupported(
      "table requires writer protocol version \{p.min_writer_version}; this kernel implements writer version 2 only",
    )
  }
  if p.writer_features.length() > 0 {
    raise DeltaError::Unsupported(
      "table declares writerFeatures [\{feature_list_text(p.writer_features)}]; writer version 2 has no feature list",
    )
  }
}

///|
pub(all) struct DeltaTable {
  root : String
}

///|
pub fn DeltaTable::open(root : String) -> DeltaTable raise DeltaError {
  if !@fs.path_exists(log_dir(root)) {
    raise DeltaError::NotFound(
      "'\{root}' is not a Delta table (no _delta_log directory)",
    )
  }
  { root, }
}

///|
pub fn DeltaTable::versions(self : DeltaTable) -> Array[Int32] raise DeltaError {
  list_versions(self.root)
}

///|
/// 当前版本 = max(最新 JSON commit, 最新 checkpoint)。checkpoint-only 表
/// 返回 checkpoint 版本。
pub fn DeltaTable::current_version(self : DeltaTable) -> Int32 raise DeltaError {
  let versions = list_versions(self.root)
  let cps = list_checkpoint_versions(self.root)
  if versions.length() == 0 && cps.length() == 0 {
    raise DeltaError::NotFound("table '\{self.root}' has no log entries")
  }
  let latest_json = if versions.length() > 0 {
    versions[versions.length() - 1]
  } else {
    -1
  }
  let latest_cp = if cps.length() > 0 { cps[cps.length() - 1] } else { -1 }
  if latest_json > latest_cp {
    latest_json
  } else {
    latest_cp
  }
}

///|
struct ReplayState {
  mut protocol : Protocol
  mut metadata : TableInfo
  files : Array[AddFile]
  history : Array[HistoryEntry]
  ignored : Array[String]
}

///|
fn apply_action(st : ReplayState, action : Action) -> Unit raise DeltaError {
  match action {
    ProtocolAction(p) => st.protocol = p
    MetaDataAction(m) => st.metadata = m
    AddAction(f) => {
      if f.has_deletion_vector {
        raise DeltaError::Unsupported(
          "file '\{f.path}' carries a deletionVector (deletion vectors require reader protocol version 3; this kernel implements reader version 1)",
        )
      }
      upsert_file(st.files, f)
    }
    RemoveAction(r) => drop_file(st.files, r.path)
    CommitInfoAction(c) =>
      st.history.push({
        version: c.version,
        timestamp: c.timestamp,
        operation: c.operation,
      })
    TxnAction(_) => ()
    IgnoredAction(name) =>
      if !st.ignored.contains(name) {
        st.ignored.push(name)
      }
  }
}

///|
fn finalize_snapshot(
  st : ReplayState,
  target_version : Int32,
) -> Snapshot raise DeltaError {
  validate_protocol(st.protocol)
  let mut total_rows = 0L
  let mut total_bytes = 0L
  for f in st.files {
    total_rows = total_rows + stats_num_records(f.stats)
    total_bytes = total_bytes + f.size
  }
  {
    version: target_version,
    protocol: st.protocol,
    metadata: st.metadata,
    files: st.files,
    total_rows,
    total_bytes,
    ignored_actions: st.ignored,
  }
}

///|
/// 回放日志到指定版本(None = 最新),返回 snapshot 与历史。
/// 日志从 0 完整时从头回放;否则从最新的单文件 checkpoint 恢复基底,
/// 再叠加其后的 JSON commit——截断日志与 checkpoint-only 表不再静默读旧版本。
fn replay_until(
  root : String,
  target : Int32?,
) -> (Snapshot, Array[HistoryEntry]) raise DeltaError {
  let versions = list_versions(root)
  let cp_versions = list_checkpoint_versions(root)
  let latest_json = if versions.length() > 0 {
    versions[versions.length() - 1]
  } else {
    -1
  }
  let cp_v = if cp_versions.length() > 0 {
    cp_versions[cp_versions.length() - 1]
  } else {
    -1
  }
  let effective_latest = if latest_json > cp_v { latest_json } else { cp_v }
  if effective_latest < 0 {
    raise DeltaError::NotFound("table '\{root}' has no log entries")
  }
  let full_log = versions.length() > 0 && versions[0] == 0
  let target_version = match target {
    Some(v) => v
    None => effective_latest
  }
  if target_version < 0 || target_version > effective_latest {
    raise DeltaError::NotFound(
      "requested version \{target_version} is out of range (latest = \{effective_latest})",
    )
  }
  let st : ReplayState = {
    protocol: default_protocol(),
    metadata: empty_metadata(),
    files: [],
    history: [],
    ignored: [],
  }
  // 路径一:JSON 从 0 起连续且目标版本被日志覆盖 → 从头回放(历史完整)。
  // 目标超出日志末端(顶端截断,checkpoint 在更前方)时走 checkpoint 恢复。
  if full_log && target_version <= latest_json {
    if !versions.contains(target_version) {
      raise DeltaError::NotFound("log version \{target_version} does not exist")
    }
    let mut v = 0
    while v <= target_version {
      let actions = read_actions(root, v)
      for action in actions {
        apply_action(st, action)
      }
      v = v + 1
    }
    let snapshot = finalize_snapshot(st, target_version)
    (snapshot, st.history)
  } else {
    // 路径二:checkpoint 恢复
    match cp_versions.last() {
      Some(c) => {
        if target_version < c {
          raise DeltaError::NotFound(
            "requested version \{target_version} predates the earliest available checkpoint \{c} and the JSON log no longer starts at version 0",
          )
        }
        let mut v = c + 1
        while v <= target_version {
          if !versions.contains(v) {
            raise DeltaError::NotFound(
              "log version \{v} is missing (gap after checkpoint \{c}; the full JSON log is required to replay past the checkpoint)",
            )
          }
          v = v + 1
        }
        let path = log_dir(root) + "/" + checkpoint_file_name(c)
        let (p_opt, m_opt, files) = parse_checkpoint(path)
        match p_opt {
          Some(p) => st.protocol = p
          None =>
            raise DeltaError::ParseError(
              "checkpoint '\{path}' has no protocol action",
            )
        }
        match m_opt {
          Some(m) => st.metadata = m
          None =>
            raise DeltaError::ParseError(
              "checkpoint '\{path}' has no metaData action",
            )
        }
        for f in files {
          apply_action(st, AddAction(f))
        }
        let mut v = c + 1
        while v <= target_version {
          let actions = read_actions(root, v)
          for action in actions {
            apply_action(st, action)
          }
          v = v + 1
        }
        let snapshot = finalize_snapshot(st, target_version)
        (snapshot, st.history)
      }
      None =>
        raise DeltaError::Unsupported(
          "log starts at version \{versions[0]}; checkpoint-only or truncated tables are not supported (replay requires the initial log entries)",
        )
    }
  }
}

///|
pub fn DeltaTable::snapshot(self : DeltaTable) -> Snapshot raise DeltaError {
  let (snapshot, _) = replay_until(self.root, None)
  snapshot
}

///|
pub fn DeltaTable::snapshot_at(
  self : DeltaTable,
  version : Int32,
) -> Snapshot raise DeltaError {
  let (snapshot, _) = replay_until(self.root, Some(version))
  snapshot
}

///|
pub fn DeltaTable::history(
  self : DeltaTable,
) -> Array[HistoryEntry] raise DeltaError {
  let (_, history) = replay_until(self.root, None)
  history
}

///|
/// 读取表的最新版本号 + snapshot + 是否检测到 checkpoint(用于 CLI 提示)
pub fn DeltaTable::inspect(
  self : DeltaTable,
) -> (Snapshot, CheckpointInfo?, Bool) raise DeltaError {
  let (snapshot, _) = replay_until(self.root, None)
  let cp = checkpoint_info(self.root)
  let has_cp = has_checkpoint_file(self.root)
  (snapshot, cp, has_cp)
}