///|
pub(all) enum TxtValue {
  Missing
  Flag
  Value(Bytes)
} derive(Eq, Debug)

///|
pub struct TxtRecord {
  priv entries : Array[Bytes]
} derive(Debug)

///|
pub fn TxtRecord::from_entries(
  entries : Array[Bytes],
) -> TxtRecord raise InputError {
  let mut size = 0
  for entry in entries {
    guard entry.length() <= 255 else {
      raise InvalidInput("TXT entry exceeds 255 octets")
    }
    size += 1 + entry.length()
    guard size <= 65535 else {
      raise InvalidInput("TXT RDATA exceeds 65535 octets")
    }
  }
  { entries: entries.copy(), }
}

///|
pub fn TxtRecord::from_wire(data : Bytes) -> TxtRecord raise InputError {
  guard data.length() <= 65535 else {
    raise InvalidInput("TXT RDATA exceeds 65535 octets")
  }
  let entries : Array[Bytes] = []
  let mut offset = 0
  while offset < data.length() {
    let size = data[offset].to_int()
    offset += 1
    guard size <= data.length() - offset else {
      raise InvalidInput("truncated TXT entry")
    }
    entries.push(data[offset:offset + size].to_owned())
    offset += size
  }
  TxtRecord::from_entries(entries)
}

///|
pub fn TxtRecord::entries(self : TxtRecord) -> Array[Bytes] {
  self.entries.copy()
}

///|
pub fn TxtRecord::to_wire(self : TxtRecord) -> Bytes {
  let out : Array[Byte] = []
  for entry in self.entries {
    out.push(entry.length().to_byte())
    for b in entry {
      out.push(b)
    }
  }
  Bytes::makei(out.length(), i => out[i])
}

///|
fn folded(b : Byte) -> Byte {
  let n = b.to_int()
  (if n >= 65 && n <= 90 { n + 32 } else { n }).to_byte()
}

///|
/// First matching key wins (RFC 6763 section 6.4). Values stay binary.
/// Invalid/empty keys are preserved in entries(), ignored during lookup.
pub fn TxtRecord::lookup(
  self : TxtRecord,
  key : String,
) -> TxtValue raise InputError {
  let wanted = @utf8.encode(key)
  guard wanted.length() > 0 else { raise InvalidInput("empty TXT key") }
  for b in wanted {
    let n = b.to_int()
    guard n >= 32 && n <= 126 && n != 61 else {
      raise InvalidInput("TXT key must be printable ASCII without '='")
    }
  }
  for entry in self.entries {
    let mut end = 0
    let mut valid = true
    while end < entry.length() && entry[end] != b'=' {
      let n = entry[end].to_int()
      if n < 32 || n > 126 {
        valid = false
      }
      end += 1
    }
    if valid && end == wanted.length() {
      let mut equal = true
      for i in 0..