///|
/// 原始存储记录;index B-tree 没有独立 rowid,字段按磁盘顺序保留。
/// B-tree 原始存储记录。values 按磁盘顺序保留;索引和 WITHOUT ROWID 的 rowid 为 None。page_number 从 1 开始,cell_offset 为页内字节偏移。
pub(all) struct BTreeRecord {
  rowid : Int64?
  values : Array[Value]
  page_number : Int
  cell_offset : Int
} derive(Debug)

///|
pub extend BTreeRecord with @debug.Debug::{to_repr}

///|
/// 扫描终止原因:Complete 为实际遍历结束,RecordLimit 为尚有数据时到达记录前缀上限,VisitorStopped 为消费者停止;不代表 integrity_check。
pub(all) enum ScanCompletion {
  Complete
  RecordLimit
  VisitorStopped
} derive(Debug, Eq)

///|
pub extend ScanCompletion with @debug.Debug::{to_repr}

///|
pub extend ScanCompletion with Eq::{equal, not_equal}

///|
/// 扫描结果及终止原因。pages_read 包含已认领 B-tree/overflow 页,payload_bytes 是已请求 payload 字节数;不是成功记录字节数或精确内存消耗。
pub(all) struct ScanSummary {
  records_read : Int
  pages_read : Int
  payload_bytes : UInt64
  completion : ScanCompletion
} derive(Debug)

///|
pub extend ScanSummary with @debug.Debug::{to_repr}

///|
priv enum PageUse {
  Btree
  Overflow
}

///|
priv struct ScanObserver {
  claim : (Int, PageKind, Int?) -> Unit raise SqliteError
  charge_payload : (UInt64) -> Unit
  trace : InspectionTrace
}

///|
fn quiet_observer() -> ScanObserver {
  {
    claim: (_, _, _) => (),
    charge_payload: _ => (),
    trace: InspectionTrace::new(),
  }
}

///|
fn page_use_name(page_use : PageUse) -> String {
  match page_use {
    Btree => "B-tree"
    Overflow => "overflow"
  }
}

///|
priv struct WalkFrame {
  page : Page
  bytes : Bytes
  depth : Int
  lower : Int64?
  upper : Int64?
  mut cursor : Int
  mut previous_key : Int64?
  mut emit_pending : Bool
  mut right_visited : Bool
}

///|
fn Database::walk_frame_loaded(
  self : Database,
  number : Int,
  depth : Int,
  lower : Int64?,
  upper : Int64?,
  table : Bool,
  occupied : Map[Int, PageUse],
  parent : Int?,
  observer : ScanObserver,
  bytes : Bytes,
) -> WalkFrame raise SqliteError {
  observer.trace.at(TreeTraversal, Some(number))
  if depth > self.limits.max_depth {
    raise LimitExceeded("B-tree 深度超过限制")
  }
  if occupied.get(number) is Some(page_use) {
    raise Invalid(
      "页 \{number} 已用于 \{page_use_name(page_use)},不能重复用于 B-tree",
    )
  }
  if occupied.length() >= self.limits.max_pages {
    raise LimitExceeded("扫描总页数超过限制")
  }
  observer.trace.at(ReadPage, Some(number))
  observer.trace.at(PageOwnershipCheck, Some(number))
  (observer.claim)(
    number,
    if parent is None {
      BtreeRoot
    } else {
      BtreeChild
    },
    parent,
  )
  occupied[number] = Btree
  let page = self.page_from_bytes(number, bytes, trace=Some(observer.trace))
  observer.trace.at(TreeTraversal, Some(number))
  let page_is_table = page.kind == TableLeaf || page.kind == TableInterior
  if page_is_table != table {
    raise Invalid("B-tree 中混入不同类型的页面")
  }
  // 非根空页会丢失记录;仅页 1 允许零 cell 的内部虚拟根。
  if page.cell_count == 0 &&
    (
      parent is Some(_) ||
      (
        (page.kind == TableInterior || page.kind == IndexInterior) &&
        number != 1
      )
    ) {
    raise Invalid("B-tree 非根页面或非页 1 内部根页不能为空")
  }
  {
    page,
    bytes,
    depth,
    lower,
    upper,
    cursor: 0,
    previous_key: lower,
    emit_pending: false,
    right_visited: false,
  }
}

///|
/// 按 B-tree 顺序逐条回调,返回 false 可停止;无需在库中保存所有结果。
/// 逐条调用 visit,返回 false 停止;不收集结果数组。默认累计 payload 64 MiB,失败请求也计费;报告区分遍历结束、记录前缀上限和消费者停止。
pub fn Database::scan_btree(
  self : Database,
  root : Int,
  visit : (BTreeRecord) -> Bool raise SqliteError,
  limit? : Int = self.limits.max_rows,
  max_total_payload_bytes? : UInt64 = 67108864UL,
) -> ScanSummary raise SqliteError {
  self.scan_btree_observed(
    root,
    visit,
    quiet_observer(),
    limit~,
    max_total_payload_bytes~,
  )
}

///|
fn Database::scan_btree_observed(
  self : Database,
  root : Int,
  visit : (BTreeRecord) -> Bool raise SqliteError,
  observer : ScanObserver,
  limit~ : Int,
  max_total_payload_bytes~ : UInt64,
) -> ScanSummary raise SqliteError {
  let cursor = self.scan_cursor_observed(
    root,
    observer,
    limit~,
    max_total_payload_bytes~,
  )
  while true {
    match cursor.next() {
      NeedPage(number) => cursor.provide_page(number, self.read_page(number))
      RecordReady(record) => if !visit(record) { return cursor.stop() }
      ScanFinished(summary) => return summary
    }
  } nobreak {
    abort("同步扫描游标未结束")
  }
}

///|
/// 收集任意 B-tree 的原始记录,索引内部 cell 也交付;显式 limit 可返回前缀。单条仍完整解码,集合本身可能占用大量内存。
pub fn Database::read_btree(
  self : Database,
  root : Int,
  limit? : Int = self.limits.max_rows,
  max_total_payload_bytes? : UInt64 = 67108864UL,
) -> Array[BTreeRecord] raise SqliteError {
  let records : Array[BTreeRecord] = []
  let summary = self.scan_btree(
    root,
    record => {
      records.push(record)
      true
    },
    limit~,
    max_total_payload_bytes~,
  )
  if limit == self.limits.max_rows && summary.completion != Complete {
    raise LimitExceeded(
      "记录数超过资源限制;请指定更小的 limit 读取前缀",
    )
  }
  records
}

///|
/// 收集 index B-tree/WITHOUT ROWID 的原始记录;普通 rowid 表类型不符合时抛 Invalid,不检查索引排序或 collation。
pub fn Database::read_index(
  self : Database,
  root : Int,
  limit? : Int = self.limits.max_rows,
) -> Array[BTreeRecord] raise SqliteError {
  let page = self.page(root)
  if page.kind != IndexLeaf && page.kind != IndexInterior {
    raise Invalid("根页不是 index B-tree")
  }
  self.read_btree(root, limit~)
}