///|
/// Represents a single key-value record stored on disk.
pub struct Record {
  checksum : Int
  timestamp : Int64
  expires_at : Int64
  key : Bytes
  value : Bytes
  is_tombstone : Bool
}

///|
/// Creates a new active Record.
pub fn Record::new(key : Bytes, value : Bytes, timestamp : Int64) -> Record {
  { checksum: 0, timestamp, expires_at: 0L, key, value, is_tombstone: false }
}

///|
/// Creates a new active Record with a Time-To-Live (expires at timestamp + ttl).
pub fn Record::new_ttl(
  key : Bytes,
  value : Bytes,
  timestamp : Int64,
  ttl : Int64,
) -> Record {
  {
    checksum: 0,
    timestamp,
    expires_at: timestamp + ttl,
    key,
    value,
    is_tombstone: false,
  }
}

///|
/// Creates a new tombstone (deleted) Record.
pub fn Record::new_tombstone(key : Bytes, timestamp : Int64) -> Record {
  {
    checksum: 0,
    timestamp,
    expires_at: 0L,
    key,
    value: Bytes::make(0, (0).to_byte()),
    is_tombstone: true,
  }
}

///|
/// Serializes the Record to its on-disk binary representation.
pub fn Record::serialize(self : Record) -> Bytes {
  let key_sz = self.key.length()
  let val_sz = if self.is_tombstone { -1 } else { self.value.length() }
  let val_len = if self.is_tombstone { 0 } else { self.value.length() }
  let total_sz = 28 + key_sz + val_len
  let arr = Array::make(total_sz, (0).to_byte())

  // Write timestamp (offset 4)
  set_int64(arr, 4, self.timestamp)
  // Write expires_at (offset 12)
  set_int64(arr, 12, self.expires_at)
  // Write key_sz (offset 20)
  set_int32(arr, 20, key_sz)
  // Write value_sz (offset 24)
  set_int32(arr, 24, val_sz)
  // Write key (offset 28)
  for i = 0; i < key_sz; i = i + 1 {
    arr[28 + i] = self.key[i]
  }
  // Write value (offset 28 + key_sz)
  for i = 0; i < val_len; i = i + 1 {
    arr[28 + key_sz + i] = self.value[i]
  }

  // Compute checksum of the rest of the array (from offset 4 to end)
  let checksum = fnv1a(arr, 4, total_sz - 4)
  set_int32(arr, 0, checksum)

  Bytes::from_array(arr)
}

///| Decodes record metadata from a 28-byte header.

///|
/// Returns (checksum, timestamp, expires_at, key_sz, val_sz).
pub fn decode_header(
  header : Bytes,
) -> (Int, Int64, Int64, Int, Int) raise MoonKVError {
  if header.length() < 28 {
    raise MoonKVError::InvalidRecord(
      "Header too short: \{header.length()} bytes",
    )
  }
  let checksum = get_int32(header, 0)
  let timestamp = get_int64(header, 4)
  let expires_at = get_int64(header, 12)
  let key_sz = get_int32(header, 20)
  let val_sz = get_int32(header, 24)
  (checksum, timestamp, expires_at, key_sz, val_sz)
}

///|
/// Deserializes a complete Record from raw bytes, verifying its checksum.
pub fn deserialize_record(bytes : Bytes) -> Record raise MoonKVError {
  let total_sz = bytes.length()
  if total_sz < 28 {
    raise MoonKVError::InvalidRecord("Record too short: \{total_sz} bytes")
  }

  let checksum = get_int32(bytes, 0)
  let timestamp = get_int64(bytes, 4)
  let expires_at = get_int64(bytes, 12)
  let key_sz = get_int32(bytes, 20)
  let val_sz = get_int32(bytes, 24)

  if key_sz < 0 {
    raise MoonKVError::InvalidRecord("Negative key size: \{key_sz}")
  }

  let is_tombstone = val_sz < 0
  if val_sz < -1 {
    raise MoonKVError::InvalidRecord("Invalid negative value size: \{val_sz}")
  }
  let val_len = if is_tombstone { 0 } else { val_sz }

  if key_sz > total_sz - 28 {
    raise MoonKVError::InvalidRecord(
      "Key size exceeds record bounds: \{key_sz}",
    )
  }
  if !is_tombstone && val_len > total_sz - 28 - key_sz {
    raise MoonKVError::InvalidRecord(
      "Value size exceeds record bounds: \{val_len}",
    )
  }

  let expected_sz = 28 + key_sz + val_len
  if total_sz != expected_sz {
    raise MoonKVError::InvalidRecord(
      "Record size mismatch: got \{total_sz}, expected \{expected_sz}",
    )
  }

  // Verify checksum
  let computed_checksum = fnv1a_bytes(bytes, 4, expected_sz - 4)
  if checksum != computed_checksum {
    raise MoonKVError::CorruptedDatabase(
      "Checksum mismatch: got \{checksum}, computed \{computed_checksum}",
    )
  }

  // Extract key
  let key_arr = Array::make(key_sz, (0).to_byte())
  for i = 0; i < key_sz; i = i + 1 {
    key_arr[i] = bytes[28 + i]
  }
  let key = Bytes::from_array(key_arr)

  // Extract value
  let value = if is_tombstone {
    Bytes::make(0, (0).to_byte())
  } else {
    let val_arr = Array::make(val_len, (0).to_byte())
    for i = 0; i < val_len; i = i + 1 {
      val_arr[i] = bytes[28 + key_sz + i]
    }
    Bytes::from_array(val_arr)
  }

  { checksum, timestamp, expires_at, key, value, is_tombstone }
}