///|
/// 原始存储记录;index B-tree 没有独立 rowid,字段按磁盘顺序保留。
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}

///|
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}

///|
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
}

///|
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(
  self : Database,
  number : Int,
  depth : Int,
  lower : Int64?,
  upper : Int64?,
  table : Bool,
  occupied : Map[Int, PageUse],
) -> WalkFrame raise SqliteError {
  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("扫描总页数超过限制")
  }
  occupied[number] = Btree
  let bytes = self.read_page(number)
  let page = self.page_from_bytes(number, bytes)
  let page_is_table = page.kind == TableLeaf || page.kind == TableInterior
  if page_is_table != table {
    raise Invalid("B-tree 中混入不同类型的页面")
  }
  {
    page,
    bytes,
    depth,
    lower,
    upper,
    cursor: 0,
    previous_key: lower,
    emit_pending: false,
    right_visited: false,
  }
}

///|
/// 按 B-tree 顺序逐条回调,返回 false 可停止;无需在库中保存所有结果。
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 {
  if limit < 0 || limit > self.limits.max_rows {
    raise LimitExceeded("记录数请求超出资源限制")
  }
  let root_page = self.page(root)
  let table = root_page.kind == TableLeaf || root_page.kind == TableInterior
  let occupied : Map[Int, PageUse] = Map([])
  let stack = [self.walk_frame(root, 1, None, None, table, occupied)]
  let mut records_read = 0
  let mut payload_bytes = 0UL
  let mut last_rowid : Int64? = None
  let mut leaf_depth : Int? = None
  while !stack.is_empty() {
    let frame = stack[stack.length() - 1]
    let page = frame.page
    let bytes = frame.bytes
    let usable = self.header.usable_size
    let interior = page.kind == TableInterior || page.kind == IndexInterior
    if frame.cursor == page.cell_count {
      if interior && !frame.right_visited {
        frame.right_visited = true
        stack.push(
          self.walk_frame(
            page.right_child.unwrap(),
            frame.depth + 1,
            frame.previous_key,
            frame.upper,
            table,
            occupied,
          ),
        )
      } else {
        let _ = stack.pop()
      }
      continue
    }
    let offset = page.cell_offsets[frame.cursor]
    if interior && !frame.emit_pending {
      if offset > usable - 4 {
        raise Invalid("interior cell 子页指针越界")
      }
      let child = page_number(read_u32(bytes, offset))
      let mut upper = frame.upper
      if table {
        let (bits, _) = page_varint(bytes, offset + 4, usable)
        let key = bits.reinterpret_as_int64()
        if frame.previous_key is Some(previous) && key <= previous {
          raise Invalid("interior rowid 未严格递增")
        }
        if frame.upper is Some(bound) && key > bound {
          raise Invalid("interior rowid 越过父页键范围")
        }
        upper = Some(key)
      }
      let lower = frame.previous_key
      if table {
        frame.previous_key = upper
        frame.cursor = frame.cursor + 1
      } else {
        // 索引内部 cell 本身就是记录,必须先访问左子树再返回该记录。
        frame.emit_pending = true
      }
      stack.push(
        self.walk_frame(child, frame.depth + 1, lower, upper, table, occupied),
      )
      continue
    }
    if !interior {
      if leaf_depth is Some(expected) && expected != frame.depth {
        raise Invalid("B-tree 叶页深度不一致")
      }
      leaf_depth = Some(frame.depth)
    }
    if records_read == limit {
      return {
        records_read,
        pages_read: occupied.length(),
        payload_bytes,
        completion: RecordLimit,
      }
    }
    let payload_offset = if page.kind == IndexInterior {
      offset + 4
    } else {
      offset
    }
    let (size_bits, size_bytes) = page_varint(bytes, payload_offset, usable)
    let payload_size = bounded_int(size_bits, "payload size")
    if payload_size.to_uint64() > max_total_payload_bytes - payload_bytes {
      raise LimitExceeded("扫描累计 payload 超过字节预算")
    }
    let (rowid, rowid_bytes) = if table {
      let (bits, size) = page_varint(bytes, payload_offset + size_bytes, usable)
      let rowid = bits.reinterpret_as_int64()
      if frame.lower is Some(bound) && rowid <= bound {
        raise Invalid("叶页 rowid 低于父页键范围")
      }
      if frame.upper is Some(bound) && rowid > bound {
        raise Invalid("叶页 rowid 高于父页键范围")
      }
      if last_rowid is Some(previous) && rowid <= previous {
        raise Invalid("叶页 rowid 未严格递增")
      }
      last_rowid = Some(rowid)
      (Some(rowid), size)
    } else {
      (None, 0)
    }
    let payload = self.cell_payload_tracked(
      bytes,
      payload_offset + size_bytes + rowid_bytes,
      payload_size,
      table,
      occupied,
    )
    let record = {
      rowid,
      values: decode_record(payload, self.header.text_encoding),
      page_number: page.number,
      cell_offset: offset,
    }
    payload_bytes = payload_bytes + payload_size.to_uint64()
    records_read = records_read + 1
    frame.cursor = frame.cursor + 1
    frame.emit_pending = false
    if !visit(record) {
      return {
        records_read,
        pages_read: occupied.length(),
        payload_bytes,
        completion: VisitorStopped,
      }
    }
  }
  {
    records_read,
    pages_read: occupied.length(),
    payload_bytes,
    completion: Complete,
  }
}

///|
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
}

///|
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~)
}