// Copyright 2021 The Pigweed Authors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy of
// the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations under
// the License.

#include "pw_protobuf/message.h"

#include "pw_stream/memory_stream.h"
#include "pw_unit_test/framework.h"

#define ASSERT_OK(status) ASSERT_EQ(OkStatus(), status)

namespace pw::protobuf {

TEST(ProtoHelper, IterateMessage) {
  // clang-format off
  constexpr uint8_t encoded_proto[] = {
    // type=uint32, k=1, v=1
    0x08, 0x01,
    // type=uint32, k=2, v=2
    0x10, 0x02,
    // type=uint32, k=3, v=3
    0x18, 0x03,
  };
  // clang-format on

  stream::MemoryReader reader(as_bytes(span(encoded_proto)));
  Message parser = Message(reader, sizeof(encoded_proto));

  uint32_t count = 0;
  for (Message::Field field : parser) {
    ++count;
    EXPECT_EQ(field.field_number(), count);
    Uint32 value = field.As<Uint32>();
    ASSERT_OK(value.status());
    EXPECT_EQ(value.value(), count);
  }

  EXPECT_EQ(count, static_cast<uint32_t>(3));
}

TEST(ProtoHelper, MessageIterator) {
  // clang-format off
  std::uint8_t encoded_proto[] = {
    // key = 1, str = "foo 1"
    0x0a, 0x05, 'f', 'o', 'o', ' ', '1',
    // type=uint32, k=2, v=2
    0x10, 0x02,
  };
  // clang-format on

  stream::MemoryReader reader(as_bytes(span(encoded_proto)));
  Message parser = Message(reader, sizeof(encoded_proto));

  Message::iterator iter = parser.begin();

  Message::iterator first = iter++;
  ASSERT_EQ(first, first);
  ASSERT_EQ(first->field_number(), static_cast<uint32_t>(1));
  String str = first->As<String>();
  ASSERT_OK(str.status());
  Result<bool> cmp = str.Equal("foo 1");
  ASSERT_OK(cmp.status());
  ASSERT_TRUE(cmp.value());

  Message::iterator second = iter++;
  ASSERT_EQ(second, second);
  ASSERT_EQ(second->field_number(), static_cast<uint32_t>(2));
  Uint32 uint32_val = second->As<Uint32>();
  ASSERT_OK(uint32_val.status());
  ASSERT_EQ(uint32_val.value(), static_cast<uint32_t>(2));

  ASSERT_NE(first, second);
  ASSERT_NE(first, iter);
  ASSERT_NE(second, iter);
  ASSERT_EQ(iter, parser.end());
}

TEST(ProtoHelper, MessageIteratorMalformedProto) {
  // clang-format off
  std::uint8_t encoded_proto[] = {
    // key = 1, str = "foo 1"
    0x0a,0x05,'f','o','o',' ','1',
    // key = 0, str = "foo 2" (invalid)
    0x02,0x05,'f','o','o',' ','2',
    // key = 3, str = "bar 1"
    0x1a,0x05,'b','a','r',' ','1',
  };
  // clang-format on

  stream::MemoryReader reader(as_bytes(span(encoded_proto)));
  Message parser = Message(reader, sizeof(encoded_proto));

  Message::iterator iter = parser.begin();
  ASSERT_OK(iter.status());

  // Second field has invalid field number
  ASSERT_FALSE((++iter).ok());

  // Attempting to increment an invalid iterator result in it being end()
  ASSERT_EQ((++iter), parser.end());

  // Test the c++ std loop behavior.
  bool expected_ok_status[] = {true, false};
  size_t count = 0;
  for (Message::Field field : parser) {
    ASSERT_EQ(field.ok(), expected_ok_status[count++]);
  }
  // First element ok. Second element invalid. Iteration ends in the next
  // iteration.
  ASSERT_EQ(count, 2ULL);
}

TEST(ProtoHelper, InvalidMessageBeginIterator) {
  Message parser(Status::Internal());
  ASSERT_FALSE(parser.begin().ok());
  ASSERT_EQ(parser.begin(), parser.end());
}

TEST(ProtoHelper, AsProtoInteger) {
  // clang-format off
  std::uint8_t encoded_proto[] = {
      // type: int32, k = 1, val = -123
      0x08, 0x85, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01,
      // type: uint32, k = 2, val = 123
      0x10, 0x7b,
      // type: sint32, k = 3, val = -456
      0x18, 0x8f, 0x07,
      // type: fixed32, k = 4, val = 268435457
      0x25, 0x01, 0x00, 0x00, 0x10,
      // type: sfixed32, k = 5, val = -268435457
      0x2d, 0xff, 0xff, 0xff, 0xef,
      // type: int64, k = 6, val = -1099511627776
      0x30, 0x80, 0x80, 0x80, 0x80, 0x80, 0xe0, 0xff, 0xff, 0xff, 0x01,
      // type: uint64, k = 7, val = 1099511627776
      0x38, 0x80, 0x80, 0x80, 0x80, 0x80, 0x20,
      // type: sint64, k = 8, val = -2199023255552
      0x40, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
      // type: fixed64, k = 9, val = 72057594037927937
      0x49, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
      // type: sfixed64, k = 10, val = -72057594037927937
      0x51, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
      // type: float, k = 11, val = 123456.00
      0x5d, 0x00, 0x20, 0xf1, 0x47,
      // type: double, k = 12, val = -123456.789
      0x61, 0xc9, 0x76, 0xbe, 0x9f, 0x0c, 0x24, 0xfe, 0xc0,
      // type: bool, k = 13, val = true
      0x68, 0x01,
      // type: bool, k = 14, val = false
      0x70, 0x00
  };
  // clang-format on

  stream::MemoryReader reader(as_bytes(span(encoded_proto)));
  Message parser = Message(reader, sizeof(encoded_proto));

  {
    Int32 value = parser.AsInt32(1);
    ASSERT_OK(value.status());
    ASSERT_EQ(value.value(), static_cast<int32_t>(-123));
  }

  {
    Uint32 value = parser.AsUint32(2);
    ASSERT_OK(value.status());
    ASSERT_EQ(value.value(), static_cast<uint32_t>(123));
  }

  {
    Sint32 value = parser.AsSint32(3);
    ASSERT_OK(value.status());
    ASSERT_EQ(value.value(), static_cast<int32_t>(-456));
  }

  {
    Fixed32 value = parser.AsFixed32(4);
    ASSERT_OK(value.status());
    ASSERT_EQ(value.value(), static_cast<uint32_t>(268435457));
  }

  {
    Sfixed32 value = parser.AsSfixed32(5);
    ASSERT_OK(value.status());
    ASSERT_EQ(value.value(), static_cast<int32_t>(-268435457));
  }

  {
    Int64 value = parser.AsInt64(6);
    ASSERT_OK(value.status());
    ASSERT_EQ(value.value(), static_cast<int64_t>(-1099511627776));
  }

  {
    Uint64 value = parser.AsUint64(7);
    ASSERT_OK(value.status());
    ASSERT_EQ(value.value(), static_cast<uint64_t>(1099511627776));
  }

  {
    Sint64 value = parser.AsSint64(8);
    ASSERT_OK(value.status());
    ASSERT_EQ(value.value(), static_cast<int64_t>(-2199023255552));
  }

  {
    Fixed64 value = parser.AsFixed64(9);
    ASSERT_OK(value.status());
    ASSERT_EQ(value.value(), static_cast<uint64_t>(72057594037927937));
  }

  {
    Sfixed64 value = parser.AsSfixed64(10);
    ASSERT_OK(value.status());
    ASSERT_EQ(value.value(), static_cast<int64_t>(-72057594037927937));
  }

  {
    Float value = parser.AsFloat(11);
    ASSERT_OK(value.status());
    ASSERT_EQ(value.value(), static_cast<float>(123456.00));
  }

  {
    Double value = parser.AsDouble(12);
    ASSERT_OK(value.status());
    ASSERT_EQ(value.value(), static_cast<double>(-123456.789));
  }

  {
    Bool value = parser.AsBool(13);
    ASSERT_OK(value.status());
    ASSERT_EQ(value.value(), static_cast<bool>(true));
  }

  {
    Bool value = parser.AsBool(14);
    ASSERT_OK(value.status());
    ASSERT_EQ(value.value(), static_cast<bool>(false));
  }
}

TEST(ProtoHelper, AsString) {
  // message {
  //   string str = 1;
  // }
  // clang-format off
  std::uint8_t encoded_proto[] = {
    // `str`, k = 1, "string"
    0x0a, 0x06, 's', 't', 'r', 'i', 'n', 'g',
  };
  // clang-format on

  stream::MemoryReader reader(as_bytes(span(encoded_proto)));
  Message parser = Message(reader, sizeof(encoded_proto));

  constexpr uint32_t kFieldNumber = 1;
  String value = parser.AsString(kFieldNumber);
  ASSERT_OK(value.status());
  Result<bool> cmp = value.Equal("string");
  ASSERT_OK(cmp.status());
  ASSERT_TRUE(cmp.value());

  cmp = value.Equal("other");
  ASSERT_OK(cmp.status());
  ASSERT_FALSE(cmp.value());

  // The string is a prefix of the target string to compare.
  cmp = value.Equal("string and more");
  ASSERT_OK(cmp.status());
  ASSERT_FALSE(cmp.value());

  // The target string to compare is a sub prefix of this string
  cmp = value.Equal("str");
  ASSERT_OK(cmp.status());
  ASSERT_FALSE(cmp.value());
}

TEST(ProtoHelper, AsRepeatedStrings) {
  // Repeated field of string i.e.
  //
  // message RepeatedString {
  //   repeated string msg_a = 1;
  //   repeated string msg_b = 2;
  // }
  // clang-format off
  std::uint8_t encoded_proto[] = {
    // key = 1, str = "foo 1"
    0x0a, 0x05, 'f', 'o', 'o', ' ', '1',
    // key = 2, str = "foo 2"
    0x12, 0x05, 'f', 'o', 'o', ' ', '2',
    // key = 1, str = "bar 1"
    0x0a, 0x05, 'b', 'a', 'r', ' ', '1',
    // key = 2, str = "bar 2"
    0x12, 0x05, 'b', 'a', 'r', ' ', '2',
  };
  // clang-format on

  constexpr uint32_t kMsgAFieldNumber = 1;
  constexpr uint32_t kMsgBFieldNumber = 2;
  constexpr uint32_t kNonExistFieldNumber = 3;

  stream::MemoryReader reader(as_bytes(span(encoded_proto)));
  Message parser = Message(reader, sizeof(encoded_proto));

  // Field 'msg_a'
  {
    RepeatedStrings msg = parser.AsRepeatedStrings(kMsgAFieldNumber);
    std::string_view expected[] = {
        "foo 1",
        "bar 1",
    };

    size_t count = 0;
    for (String ele : msg) {
      ASSERT_OK(ele.status());
      Result<bool> res = ele.Equal(expected[count++]);
      ASSERT_OK(res.status());
      ASSERT_TRUE(res.value());
    }

    ASSERT_EQ(count, static_cast<size_t>(2));
  }

  // Field `msg_b`
  {
    RepeatedStrings msg = parser.AsRepeatedStrings(kMsgBFieldNumber);
    std::string_view expected[] = {
        "foo 2",
        "bar 2",
    };

    size_t count = 0;
    for (String ele : msg) {
      ASSERT_OK(ele.status());
      Result<bool> res = ele.Equal(expected[count++]);
      ASSERT_OK(res.status());
      ASSERT_TRUE(res.value());
    }

    ASSERT_EQ(count, static_cast<size_t>(2));
  }

  // non-existing field
  {
    RepeatedStrings msg = parser.AsRepeatedStrings(kNonExistFieldNumber);
    size_t count = 0;
    for ([[maybe_unused]] String ele : msg) {
      count++;
    }

    ASSERT_EQ(count, static_cast<size_t>(0));
  }
}

TEST(ProtoHelper, RepeatedFieldIterator) {
  // Repeated field of string i.e.
  //
  // message RepeatedString {
  //   repeated string msg = 1;
  // }
  // clang-format off
  std::uint8_t encoded_proto[] = {
    // key = 1, str = "foo 1"
    0x0a, 0x05, 'f', 'o', 'o', ' ', '1',
    // key = 1, str = "bar 1"
    0x0a, 0x05, 'b', 'a', 'r', ' ', '1',
  };
  // clang-format on

  constexpr uint32_t kFieldNumber = 1;
  stream::MemoryReader reader(as_bytes(span(encoded_proto)));
  Message parser = Message(reader, sizeof(encoded_proto));
  RepeatedStrings repeated_str = parser.AsRepeatedStrings(kFieldNumber);

  RepeatedStrings::iterator iter = repeated_str.begin();

  RepeatedStrings::iterator first = iter++;
  ASSERT_EQ(first, first);
  Result<bool> cmp = first->Equal("foo 1");
  ASSERT_OK(cmp.status());
  ASSERT_TRUE(cmp.value());

  RepeatedStrings::iterator second = iter++;
  ASSERT_EQ(second, second);
  cmp = second->Equal("bar 1");
  ASSERT_OK(cmp.status());
  ASSERT_TRUE(cmp.value());

  ASSERT_NE(first, second);
  ASSERT_NE(first, iter);
  ASSERT_NE(second, iter);
  ASSERT_EQ(iter, repeated_str.end());
}

TEST(ProtoHelper, RepeatedFieldIteratorMalformedFieldID) {
  // Repeated field of string i.e.
  //
  // message RepeatedString {
  //   repeated string msg = 1;
  // }
  // clang-format off
  std::uint8_t encoded_proto[] = {
    // key = 1, str = "foo 1"
    0x0a, 0x05, 'f', 'o', 'o', ' ', '1',
    // key = 0, str = "foo 1" (invalid)
    0x02, 0x05, 'f', 'o', 'o', ' ', '1',
    // key = 1, str = "foo 1"
    0x0a, 0x05, 'f', 'o', 'o', ' ', '1',
  };
  // clang-format on

  stream::MemoryReader reader(as_bytes(span(encoded_proto)));
  Message parser = Message(reader, sizeof(encoded_proto));
  RepeatedStrings repeated_str = parser.AsRepeatedStrings(1);

  bool expected_ok[] = {true, false};
  size_t count = 0;
  for (String s : repeated_str) {
    ASSERT_EQ(s.ok(), expected_ok[count++]);
  }
  // Iterator becomes invalid in the second iteration. Attempting to increment
  // causes it to become end(); Therefore, count should be incremented twice.
  ASSERT_EQ(count, 2ULL);
}

TEST(ProtoHelper, RepeatedFieldIteratorMalformedFieldIDBeginning) {
  // Repeated field of string i.e.
  //
  // message RepeatedString {
  //   repeated string msg = 1;
  // }
  // clang-format off
  std::uint8_t encoded_proto[] = {
    // key = 0, str = "foo 1" (invalid)
    0x02, 0x05, 'f', 'o', 'o', ' ', '1',
    // key = 1, str = "foo 1"
    0x0a, 0x05, 'f', 'o', 'o', ' ', '1',
    // key = 1, str = "foo 1"
    0x0a, 0x05, 'f', 'o', 'o', ' ', '1',
  };
  // clang-format on

  stream::MemoryReader reader(as_bytes(span(encoded_proto)));
  Message parser = Message(reader, sizeof(encoded_proto));
  RepeatedStrings repeated_str = parser.AsRepeatedStrings(1);

  bool expected_ok[] = {false};
  size_t count = 0;
  for (String s : repeated_str) {
    ASSERT_EQ(s.ok(), expected_ok[count++]);
  }
  // Iterator becomes invalid in the second iteration. Attempting to increment
  // causes it to become end(); Therefore, count should be incremented twice.
  ASSERT_EQ(count, 1ULL);
}

TEST(ProtoHelper, RepeatedFieldIteratorMalformedDataLoss) {
  // Repeated field of string i.e.
  //
  // message RepeatedString {
  //   repeated string msg = 1;
  // }
  // clang-format off
  std::uint8_t encoded_proto[] = {
    // key = 1, str = "foo 1"
    0x0a, 0x05, 'f', 'o', 'o', ' ', '1',
    // key = 0, str = "foo 1" (invalid)
    0x0a, 0x10, 'f', 'o', 'o', ' ', '1',
  };
  // clang-format on

  stream::MemoryReader reader(as_bytes(span(encoded_proto)));
  Message parser = Message(reader, sizeof(encoded_proto));
  RepeatedStrings repeated_str = parser.AsRepeatedStrings(1);

  bool expected_ok[] = {true, false};
  size_t count = 0;
  for (String s : repeated_str) {
    ASSERT_EQ(s.ok(), expected_ok[count++]);
  }
  ASSERT_EQ(count, 2ULL);
}

TEST(ProtoHelper, AsMessage) {
  // A nested message:
  //
  // message Contact {
  //   string number = 1;
  //   string email = 2;
  // }
  //
  // message Person {
  //  Contact info = 2;
  // }
  // clang-format off
  std::uint8_t encoded_proto[] = {
    // Person.info.number = "123456", .email = "foo@email.com"
    0x12, 0x17,
    0x0a, 0x06, '1', '2', '3', '4', '5', '6',
    0x12, 0x0d, 'f', 'o', 'o', '@', 'e', 'm', 'a', 'i', 'l', '.', 'c', 'o', 'm',
  };
  // clang-format on

  constexpr uint32_t kInfoFieldNumber = 2;
  constexpr uint32_t kNumberFieldNumber = 1;
  constexpr uint32_t kEmailFieldNumber = 2;

  stream::MemoryReader reader(as_bytes(span(encoded_proto)));
  Message parser = Message(reader, sizeof(encoded_proto));

  Message info = parser.AsMessage(kInfoFieldNumber);
  ASSERT_OK(info.status());

  String number = info.AsString(kNumberFieldNumber);
  ASSERT_OK(number.status());
  Result<bool> cmp = number.Equal("123456");
  ASSERT_OK(cmp.status());
  ASSERT_TRUE(cmp.value());

  String email = info.AsString(kEmailFieldNumber);
  ASSERT_OK(email.status());
  cmp = email.Equal("foo@email.com");
  ASSERT_OK(cmp.status());
  ASSERT_TRUE(cmp.value());
}

TEST(ProtoHelper, AsRepeatedMessages) {
  // message Contact {
  //   string number = 1;
  //   string email = 2;
  // }
  //
  // message Person {
  //  repeated Contact info = 1;
  // }
  // clang-format off
  std::uint8_t encoded_proto[] = {
    // Person.Contact.number = "12345", .email = "foo@email.com"
    0x0a, 0x16,
    0x0a, 0x05, '1', '2', '3', '4', '5',
    0x12, 0x0d, 'f', 'o', 'o', '@', 'e', 'm', 'a', 'i', 'l', '.', 'c', 'o', 'm',

    // Person.Contact.number = "67890", .email = "bar@email.com"
    0x0a, 0x16,
    0x0a, 0x05, '6', '7', '8', '9', '0',
    0x12, 0x0d, 'b', 'a', 'r', '@', 'e', 'm', 'a', 'i', 'l', '.', 'c', 'o', 'm',
  };
  // clang-format on

  constexpr uint32_t kInfoFieldNumber = 1;
  constexpr uint32_t kNumberFieldNumber = 1;
  constexpr uint32_t kEmailFieldNumber = 2;

  stream::MemoryReader reader(as_bytes(span(encoded_proto)));
  Message parser = Message(reader, sizeof(encoded_proto));

  RepeatedMessages messages = parser.AsRepeatedMessages(kInfoFieldNumber);
  ASSERT_OK(messages.status());

  struct {
    std::string_view number;
    std::string_view email;
  } expected[] = {
      {"12345", "foo@email.com"},
      {"67890", "bar@email.com"},
  };

  size_t count = 0;
  for (Message message : messages) {
    String number = message.AsString(kNumberFieldNumber);
    ASSERT_OK(number.status());
    Result<bool> cmp = number.Equal(expected[count].number);
    ASSERT_OK(cmp.status());
    ASSERT_TRUE(cmp.value());

    String email = message.AsString(kEmailFieldNumber);
    ASSERT_OK(email.status());
    cmp = email.Equal(expected[count].email);
    ASSERT_OK(cmp.status());
    ASSERT_TRUE(cmp.value());

    count++;
  }

  ASSERT_EQ(count, static_cast<size_t>(2));
}

TEST(ProtoHelper, AsStringToBytesMap) {
  // message Maps {
  //   map<string, string> map_a = 1;
  //   map<string, string> map_b = 2;
  // }
  // clang-format off
  std::uint8_t encoded_proto[] = {
    // map_a["key_bar"] = "bar_a", key = 1
    0x0a, 0x10,
    0x0a, 0x07, 'k', 'e', 'y', '_', 'b', 'a', 'r', // map key
    0x12, 0x05, 'b', 'a', 'r', '_', 'a', // map value

    // map_a["key_foo"] = "foo_a", key = 1
    0x0a, 0x10,
    0x0a, 0x07, 'k', 'e', 'y', '_', 'f', 'o', 'o',
    0x12, 0x05, 'f', 'o', 'o', '_', 'a',

    // map_b["key_foo"] = "foo_b", key = 2
    0x12, 0x10,
    0x0a, 0x07, 'k', 'e', 'y', '_', 'f', 'o', 'o',
    0x12, 0x05, 'f', 'o', 'o', 0x5f, 0x62,

    // map_b["key_bar"] = "bar_b", key = 2
    0x12, 0x10,
    0x0a, 0x07, 'k', 'e', 'y', '_', 'b', 'a', 'r',
    0x12, 0x05, 'b', 'a', 'r', 0x5f, 0x62,
  };
  // clang-format on

  stream::MemoryReader reader(as_bytes(span(encoded_proto)));
  Message parser = Message(reader, sizeof(encoded_proto));

  {
    // Parse field 'map_a'
    constexpr uint32_t kFieldNumber = 1;
    StringMapParser<String> string_map =
        parser.AsStringToStringMap(kFieldNumber);

    String value = string_map["key_foo"];
    ASSERT_OK(value.status());
    Result<bool> cmp = value.Equal("foo_a");
    ASSERT_OK(cmp.status());
    ASSERT_TRUE(cmp.value());

    value = string_map["key_bar"];
    ASSERT_OK(value.status());
    cmp = value.Equal("bar_a");
    ASSERT_OK(cmp.status());
    ASSERT_TRUE(cmp.value());

    // Non-existing key
    value = string_map["non-existing"];
    ASSERT_EQ(value.status(), Status::NotFound());
  }

  {
    // Parse field 'map_b'
    constexpr uint32_t kFieldNumber = 2;
    StringMapParser<String> string_map =
        parser.AsStringToStringMap(kFieldNumber);

    String value = string_map["key_foo"];
    ASSERT_OK(value.status());
    Result<bool> cmp = value.Equal("foo_b");
    ASSERT_OK(cmp.status());
    ASSERT_TRUE(cmp.value());

    value = string_map["key_bar"];
    ASSERT_OK(value.status());
    cmp = value.Equal("bar_b");
    ASSERT_OK(cmp.status());
    ASSERT_TRUE(cmp.value());

    // Non-existing key
    value = string_map["non-existing"];
    ASSERT_EQ(value.status(), Status::NotFound());
  }
}

TEST(ProtoHelper, AsStringToMessageMap) {
  // message Contact {
  //   string number = 1;
  //   string email = 2;
  // }
  //
  // message Contacts {
  //  map<string, Contact> staffs = 1;
  // }
  // clang-format off
  std::uint8_t encoded_proto[] = {
    // staffs['bar'] = {.number = '456, .email = "bar@email.com"}
    0x0a, 0x1b,
    0x0a, 0x03, 0x62, 0x61, 0x72,
    0x12, 0x14, 0x0a, 0x03, 0x34, 0x35, 0x36, 0x12, 0x0d, 0x62, 0x61, 0x72, 0x40, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d,

    // staffs['foo'] = {.number = '123', .email = "foo@email.com"}
    0x0a, 0x1b,
    0x0a, 0x03, 0x66, 0x6f, 0x6f,
    0x12, 0x14, 0x0a, 0x03, 0x31, 0x32, 0x33, 0x12, 0x0d, 0x66, 0x6f, 0x6f, 0x40, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x2e, 0x63, 0x6f, 0x6d,
  };
  // clang-format on
  constexpr uint32_t kStaffsFieldId = 1;
  constexpr uint32_t kNumberFieldId = 1;
  constexpr uint32_t kEmailFieldId = 2;

  stream::MemoryReader reader(as_bytes(span(encoded_proto)));
  Message parser = Message(reader, sizeof(encoded_proto));

  StringMapParser<Message> staffs = parser.AsStringToMessageMap(kStaffsFieldId);
  ASSERT_OK(staffs.status());

  Message foo_staff = staffs["foo"];
  ASSERT_OK(foo_staff.status());
  String foo_number = foo_staff.AsString(kNumberFieldId);
  ASSERT_OK(foo_number.status());
  Result<bool> foo_number_cmp = foo_number.Equal("123");
  ASSERT_OK(foo_number_cmp.status());
  ASSERT_TRUE(foo_number_cmp.value());
  String foo_email = foo_staff.AsString(kEmailFieldId);
  ASSERT_OK(foo_email.status());
  Result<bool> foo_email_cmp = foo_email.Equal("foo@email.com");
  ASSERT_OK(foo_email_cmp.status());
  ASSERT_TRUE(foo_email_cmp.value());

  Message bar_staff = staffs["bar"];
  ASSERT_OK(bar_staff.status());
  String bar_number = bar_staff.AsString(kNumberFieldId);
  ASSERT_OK(bar_number.status());
  Result<bool> bar_number_cmp = bar_number.Equal("456");
  ASSERT_OK(bar_number_cmp.status());
  ASSERT_TRUE(bar_number_cmp.value());
  String bar_email = bar_staff.AsString(kEmailFieldId);
  ASSERT_OK(bar_email.status());
  Result<bool> bar_email_cmp = bar_email.Equal("bar@email.com");
  ASSERT_OK(bar_email_cmp.status());
  ASSERT_TRUE(bar_email_cmp.value());
}

TEST(ProtoHelper, AsStringToBytesMapMalformed) {
  // message Maps {
  //   map<string, string> map_a = 1;
  //   map<string, string> map_b = 2;
  // }
  // clang-format off
  std::uint8_t encoded_proto[] = {
    // map_a["key_bar"] = "bar_a", key = 1
    0x0a, 0x10,
    0x0a, 0x07, 'k', 'e', 'y', '_', 'b', 'a', 'r', // map key
    0x12, 0x05, 'b', 'a', 'r', '_', 'a', // map value

    // map_a["key_foo"] = "foo_a", key = 0 (invalid)
    0x02, 0x10,
    0x0a, 0x07, 'k', 'e', 'y', '_', 'f', 'o', 'o',
    0x12, 0x05, 'f', 'o', 'o', '_', 'a',
  };
  // clang-format on

  stream::MemoryReader reader(as_bytes(span(encoded_proto)));
  Message parser = Message(reader, sizeof(encoded_proto));

  // Parse field 'map_a'
  constexpr uint32_t kFieldNumber = 1;
  StringMapParser<String> string_map = parser.AsStringToStringMap(kFieldNumber);

  bool expected_ok_status[] = {true, false};
  size_t count = 0;
  for (StringToStringMapEntry entry : string_map) {
    ASSERT_EQ(entry.ok(), expected_ok_status[count]);
    ASSERT_EQ(entry.Key().ok(), expected_ok_status[count]);
    ASSERT_EQ(entry.Value().ok(), expected_ok_status[count]);
    count++;
  }
  ASSERT_EQ(count, 2ULL);
}

}  // namespace pw::protobuf
