// Typed message example: a Person and AddressBook implemented with the
// Message trait, demonstrating an end-to-end schema-driven workflow.
//
// Wire schema (field numbers are part of the protocol contract):
//   message Person {
//     string name = 1;
//     int32 age  = 2;
//     bool active = 3;
//     repeated string tags = 4;
//   }

///|
/// A typed protobuf message with explicit field numbers.
pub(all) struct Person {
  name : String
  age : Int
  active : Bool
  tags : Array[String]
} derive(Debug, Eq)

///|
/// Create a Person from its fields.
pub fn Person::new(
  name : String,
  age : Int,
  active : Bool,
  tags : Array[String],
) -> Person {
  { name, age, active, tags }
}

///|
/// Encode a Person into an Encoder.
pub fn Person::encode(self : Person, enc : Encoder) -> Unit {
  let _ = enc.encode_string(1U, self.name)
  let _ = enc.encode_int32(2U, self.age)
  let _ = enc.encode_bool(3U, self.active)
  for tag in self.tags {
    let _ = enc.encode_string(4U, tag)
  }
}

///|
/// Decode a Person from a Decoder. Unknown fields are skipped.
pub fn Person::decode(dec : Decoder) -> Person {
  let mut name = ""
  let mut age = 0
  let mut active = false
  let tags : Array[String] = []
  while dec.read_tag() is Some((fnum, wt)) {
    match fnum {
      1U => if dec.read_string() is Some(v) { name = v }
      2U => if dec.read_int32() is Some(v) { age = v }
      3U => if dec.read_bool() is Some(v) { active = v }
      4U => if dec.read_string() is Some(v) { tags.push(v) }
      _ => dec.skip_field(wt) |> ignore
    }
  }
  { name, age, active, tags }
}

///|
/// Serialize a Person to bytes.
pub fn Person::to_bytes(self : Person) -> Bytes {
  let enc = Encoder::new()
  self.encode(enc)
  enc.to_bytes()
}

///|
/// Deserialize a Person from bytes.
pub fn Person::from_bytes(bytes : Bytes) -> Person {
  Person::decode(Decoder::new(bytes))
}

///|
/// A collection of people, encoded as a repeated length-delimited field.
pub(all) struct AddressBook {
  people : Array[Person]
} derive(Debug, Eq)

///|
/// Create an AddressBook from a list of people.
pub fn AddressBook::new(people : Array[Person]) -> AddressBook {
  { people, }
}

///|
/// Encode an AddressBook (field 1 is a repeated Person message).
pub fn AddressBook::encode(self : AddressBook, enc : Encoder) -> Unit {
  for person in self.people {
    let inner = Encoder::new()
    person.encode(inner)
    let _ = enc.encode_message(1U, inner)
  }
}

///|
/// Decode an AddressBook from a Decoder.
pub fn AddressBook::decode(dec : Decoder) -> AddressBook {
  let people : Array[Person] = []
  while dec.read_tag() is Some((fnum, wt)) {
    match fnum {
      1U =>
        if dec.read_message() is Some(inner) {
          people.push(Person::decode(inner))
        }
      _ => dec.skip_field(wt) |> ignore
    }
  }
  { people, }
}

///|
/// Serialize an AddressBook to bytes.
pub fn AddressBook::to_bytes(self : AddressBook) -> Bytes {
  let enc = Encoder::new()
  self.encode(enc)
  enc.to_bytes()
}

///|
/// Deserialize an AddressBook from bytes.
pub fn AddressBook::from_bytes(bytes : Bytes) -> AddressBook {
  AddressBook::decode(Decoder::new(bytes))
}