///|
/// Stable identifier for a field within a MoonSearch schema.
///
/// M0 constructs field identifiers directly. A schema builder will own their
/// allocation in a later milestone.
pub(all) struct FieldId {
  value : Int
} derive(Eq, @debug.Debug)

///|
pub fn FieldId::new(value : Int) -> FieldId {
  { value, }
}

///|
/// Segment-local document identifier.
pub(all) struct DocId {
  value : Int
} derive(Eq, @debug.Debug)

///|
pub fn DocId::new(value : Int) -> DocId {
  { value, }
}

///|
/// Address of a document in a searcher's segment set.
///
/// M1 searchers contain one segment, so `segment_ord` is always zero. Keeping
/// the segment ordinal in the public result model avoids treating a
/// segment-local `DocId` as a global identifier when multi-segment search is
/// added later.
pub(all) struct DocAddress {
  segment_ord : Int
  doc_id : DocId
} derive(Eq, @debug.Debug)

///|
pub fn DocAddress::new(segment_ord : Int, doc_id : DocId) -> DocAddress {
  { segment_ord, doc_id }
}

///|
/// A document containing zero or more text fields.
pub struct Document {
  field_ids : Array[FieldId]
  texts : Array[String]
}

///|
pub fn Document::new() -> Document {
  { field_ids: [], texts: [] }
}

///|
/// Adds one text value to the document.
pub fn Document::add_text(
  self : Document,
  field_id : FieldId,
  text : String,
) -> Unit {
  self.field_ids.push(field_id)
  self.texts.push(text)
}

///|
pub fn Document::field_count(self : Document) -> Int {
  self.field_ids.length()
}

///|
pub fn Document::field_id_at(self : Document, index : Int) -> FieldId? {
  if index < 0 || index >= self.field_ids.length() {
    None
  } else {
    Some(self.field_ids[index])
  }
}

///|
pub fn Document::text_at(self : Document, index : Int) -> String? {
  if index < 0 || index >= self.texts.length() {
    None
  } else {
    Some(self.texts[index])
  }
}

///|
/// Immutable snapshot of the text values stored for one indexed document.
pub struct StoredDocument {
  field_ids : ReadOnlyArray[FieldId]
  texts : ReadOnlyArray[String]
}

///|
pub fn Document::snapshot(self : Document) -> StoredDocument {
  let field_ids : Array[FieldId] = []
  let texts : Array[String] = []
  for field_id in self.field_ids {
    field_ids.push(field_id)
  }
  for text in self.texts {
    texts.push(text)
  }
  {
    field_ids: ReadOnlyArray::from_array(field_ids),
    texts: ReadOnlyArray::from_array(texts),
  }
}

///|
pub fn StoredDocument::from_fields(
  field_ids : Array[FieldId],
  texts : Array[String],
) -> StoredDocument {
  guard field_ids.length() == texts.length() else {
    abort("stored field identifiers and texts must have equal length")
  }
  {
    field_ids: ReadOnlyArray::from_array(field_ids),
    texts: ReadOnlyArray::from_array(texts),
  }
}

///|
/// Returns every stored text value for a field in insertion order.
pub fn StoredDocument::texts_for(
  self : StoredDocument,
  field_id : FieldId,
) -> ReadOnlyArray[String] {
  let values : Array[String] = []
  for index in 0.. Term {
  { field_id, text }
}