///|
/// Values that may participate in a portable ordering key.
pub(all) enum PageValue {
  NullValue
  IntValue(Int64)
  TextValue(String)
  BoolValue(Bool)
} derive(Debug, Eq)

///|
/// Sort direction for one key component.
pub(all) enum SortDirection {
  Ascending
  Descending
} derive(Debug, Eq)

///|
/// Explicit null placement avoids database-dependent default ordering.
pub(all) enum NullPlacement {
  NullsFirst
  NullsLast
} derive(Debug, Eq)

///|
/// Direction in which a page is requested from a cursor boundary.
pub(all) enum PageMode {
  ForwardPage
  BackwardPage
} derive(Debug, Eq)

///|
/// One named sort key. Record id is always appended as the final unique
/// tie-breaker and therefore must not appear here.
pub struct SortField {
  name : String
  direction : SortDirection
  nulls : NullPlacement
} derive(Debug, Eq)

///|
/// Format-neutral record used by the reference paginator. Database adapters
/// may use the same ordering and cursor APIs without materializing all rows.
pub struct PageRow {
  id : String
  values : Array[(String, PageValue)]
} derive(Debug, Eq)

///|
/// A typed key component captured inside a cursor.
pub struct KeyPart {
  field : String
  value : PageValue
  direction : SortDirection
  nulls : NullPlacement
} derive(Debug, Eq)

///|
/// Stable position consists of declared sort values and a unique record id.
pub struct PagePosition {
  parts : Array[KeyPart]
  tie_breaker : String
} derive(Debug, Eq)

///|
/// Decoded cursor data. Snapshot is supplied by the host, for example a
/// database revision, event offset, or content hash.
pub struct PageCursor {
  version : Int
  position : PagePosition
  snapshot : String?
} derive(Debug, Eq)

///|
/// One result edge with a cursor that resumes after or before this row.
pub struct PageEdge {
  row : PageRow
  cursor : String
} derive(Debug, Eq)

///|
/// Navigation metadata follows the common connection model while remaining
/// independent of GraphQL or any web framework.
pub struct PageInfo {
  has_previous_page : Bool
  has_next_page : Bool
  start_cursor : String?
  end_cursor : String?
  snapshot : String?
} derive(Debug, Eq)

///|
/// Complete page returned by the in-memory reference engine.
pub struct PageResult {
  edges : Array[PageEdge]
  info : PageInfo
} derive(Debug, Eq)

///|
pub(all) enum PageErrorKind {
  EmptyRowId
  DuplicateRowId
  EmptySortField
  DuplicateSortField
  MissingSortValue
  InvalidPageSize
  TooManySortFields
  TooManyRows
  InvalidCursor
  UnsupportedCursorVersion
  CursorChecksumMismatch
  CursorSortMismatch
  CursorTooLong
  SnapshotMismatch
  RepeatedCursor
  DuplicateTraversalItem
  InvalidOffset
} derive(Debug, Eq)

///|
/// Structured failure suitable for API error mapping and test assertions.
pub struct PageError {
  kind : PageErrorKind
  field : String
  message : String
} derive(Debug, Eq)

///|
pub fn int_value(value : Int64) -> PageValue {
  IntValue(value)
}

///|
pub fn text_value(value : String) -> PageValue {
  TextValue(value)
}

///|
pub fn bool_value(value : Bool) -> PageValue {
  BoolValue(value)
}

///|
pub fn null_value() -> PageValue {
  NullValue
}

///|
pub fn sort_field(
  name : String,
  direction? : SortDirection = Ascending,
  nulls? : NullPlacement = NullsLast,
) -> SortField {
  { name, direction, nulls }
}

///|
pub fn page_row(
  id : String,
  values : Array[(String, PageValue)],
) -> Result[PageRow, PageError] {
  if id.is_empty() {
    return Err(page_error(EmptyRowId, "id", "row id must not be empty"))
  }
  for index = 0; index < values.length(); index = index + 1 {
    if values[index].0.is_empty() {
      return Err(
        page_error(EmptySortField, "values", "row field name must not be empty"),
      )
    }
    for previous = 0; previous < index; previous = previous + 1 {
      if values[previous].0 == values[index].0 {
        return Err(
          page_error(
            DuplicateSortField,
            values[index].0,
            "row contains a duplicate field name",
          ),
        )
      }
    }
  }
  Ok({ id, values })
}

///|
pub fn PageRow::id(self : PageRow) -> String {
  self.id
}

///|
pub fn PageRow::values(self : PageRow) -> Array[(String, PageValue)] {
  self.values.copy()
}

///|
pub fn PageRow::value(self : PageRow, name : String) -> PageValue? {
  for item in self.values {
    if item.0 == name {
      return Some(item.1)
    }
  }
  None
}

///|
pub fn SortField::name(self : SortField) -> String {
  self.name
}

///|
pub fn SortField::direction(self : SortField) -> SortDirection {
  self.direction
}

///|
pub fn SortField::nulls(self : SortField) -> NullPlacement {
  self.nulls
}

///|
pub fn KeyPart::field(self : KeyPart) -> String {
  self.field
}

///|
pub fn KeyPart::value(self : KeyPart) -> PageValue {
  self.value
}

///|
pub fn KeyPart::direction(self : KeyPart) -> SortDirection {
  self.direction
}

///|
pub fn KeyPart::nulls(self : KeyPart) -> NullPlacement {
  self.nulls
}

///|
pub fn PagePosition::parts(self : PagePosition) -> Array[KeyPart] {
  self.parts.copy()
}

///|
pub fn PagePosition::tie_breaker(self : PagePosition) -> String {
  self.tie_breaker
}

///|
pub fn PageCursor::version(self : PageCursor) -> Int {
  self.version
}

///|
pub fn PageCursor::position(self : PageCursor) -> PagePosition {
  self.position
}

///|
pub fn PageCursor::snapshot(self : PageCursor) -> String? {
  self.snapshot
}

///|
pub fn PageEdge::row(self : PageEdge) -> PageRow {
  self.row
}

///|
pub fn PageEdge::cursor(self : PageEdge) -> String {
  self.cursor
}

///|
pub fn PageInfo::has_previous_page(self : PageInfo) -> Bool {
  self.has_previous_page
}

///|
pub fn PageInfo::has_next_page(self : PageInfo) -> Bool {
  self.has_next_page
}

///|
pub fn PageInfo::start_cursor(self : PageInfo) -> String? {
  self.start_cursor
}

///|
pub fn PageInfo::end_cursor(self : PageInfo) -> String? {
  self.end_cursor
}

///|
pub fn PageInfo::snapshot(self : PageInfo) -> String? {
  self.snapshot
}

///|
pub fn PageResult::edges(self : PageResult) -> Array[PageEdge] {
  self.edges.copy()
}

///|
pub fn PageResult::info(self : PageResult) -> PageInfo {
  self.info
}

///|
pub fn PageError::kind(self : PageError) -> PageErrorKind {
  self.kind
}

///|
pub fn PageError::field(self : PageError) -> String {
  self.field
}

///|
pub fn PageError::message(self : PageError) -> String {
  self.message
}

///|
fn page_error(
  kind : PageErrorKind,
  field : String,
  message : String,
) -> PageError {
  { kind, field, message }
}