// Glaze Library
// For the license information refer to glaze.hpp

#include <concepts>

#include "glaze/glaze.hpp"
#include "ut/ut.hpp"

using namespace ut;

// Structs for deserialization tests - must be at namespace scope for reflection
namespace lazy_beve_test
{
   struct User
   {
      std::string name{};
      int age{};
      bool active{};
   };

   struct Address
   {
      std::string city{};
      std::string country{};
   };

   struct Person
   {
      std::string name{};
      Address address{};
   };

   struct Item
   {
      int id{};
      std::string value{};
   };

   struct Numbers
   {
      int32_t int_val{42};
      double float_val{3.14};
      int64_t negative{-100};
      uint64_t big{9007199254740993ULL};
   };

   struct StringData
   {
      std::string simple{"hello"};
      std::string with_special{"hello\nworld"};
   };

   struct BoolData
   {
      bool exists{true};
   };

   struct MixedData
   {
      std::string str{"hello"};
      int num{42};
   };

   struct Container
   {
      User user;
      int version{1};
   };

   struct PeopleContainer
   {
      std::vector<Person> people;
   };

   struct PrimitiveData
   {
      int count{42};
      double ratio{3.14};
      std::string name{"test"};
      bool active{true};
   };

   struct ArrayData
   {
      std::vector<int> values{1, 2, 3, 4, 5};
   };

   struct SimpleData
   {
      std::string name{"test"};
      int age{30};
   };

   struct NumMapContainer
   {
      std::map<int, std::string> num_map{{1, "hello"}, {2, "world"}};
      std::string after{"after_map"};
   };

   struct DoubleField
   {
      double value{3.5};
   };

   struct FloatField
   {
      float value{};
   };
}

// A user options struct that derives from glz::opts to carry an extra field, which is the
// pattern glz::opts documents ("Add these fields to a custom options struct if you want to
// use them"). The lazy API must instantiate on the exact type so the derived field survives.
struct custom_lazy_beve_opts : glz::opts
{
   bool allow_conversions = false;
};

inline constexpr custom_lazy_beve_opts derived_beve_opts{glz::opts{.format = glz::BEVE}};

suite lazy_beve_tests = [] {
   "lazy_beve_read_basic"_test = [] {
      // Create BEVE data from a struct
      lazy_beve_test::User user{"John", 30, true};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(user, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value()) << "Failed to parse BEVE";

      auto& doc = *result;
      expect(doc.is_object());
      expect(doc.root().size() == 3u);

      // Access values lazily
      expect(doc["name"].is_string());
      expect(doc["name"].get<std::string>().value() == "John");

      expect(doc["age"].is_number());
      expect(doc["age"].get<int64_t>().value() == 30);

      expect(doc["active"].is_boolean());
      expect(doc["active"].get<bool>().value() == true);
   };

   "lazy_beve_read_array"_test = [] {
      // Create a generic array
      std::vector<int> arr{1, 2, 3, 4, 5};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(arr, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto& doc = *result;
      expect(doc.is_array());
      expect(doc.root().size() == 5u);
   };

   "lazy_beve_nested"_test = [] {
      lazy_beve_test::Person person{"Alice", {"New York", "USA"}};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(person, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto& doc = *result;
      expect(doc["name"].get<std::string>().value() == "Alice");
      expect(doc["address"]["city"].get<std::string>().value() == "New York");
      expect(doc["address"]["country"].get<std::string>().value() == "USA");
   };

   "lazy_beve_contains"_test = [] {
      std::map<std::string, int> data{{"a", 1}, {"b", 2}};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto& doc = *result;
      expect(doc.root().contains("a"));
      expect(doc.root().contains("b"));
      expect(!doc.root().contains("c"));
   };

   "lazy_beve_empty_object"_test = [] {
      std::map<std::string, int> empty_map{};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(empty_map, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto& doc = *result;
      expect(doc.is_object());
      expect(doc.root().empty());
      expect(doc.root().size() == 0u);
   };

   "lazy_beve_empty_array"_test = [] {
      std::vector<int> empty_arr{};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(empty_arr, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto& doc = *result;
      expect(doc.is_array());
      expect(doc.root().empty());
      expect(doc.root().size() == 0u);
   };

   "lazy_beve_null"_test = [] {
      std::optional<int> null_val = std::nullopt;
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(null_val, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto& doc = *result;
      expect(doc.is_null());
      expect(doc.root().empty());
   };

   "lazy_beve_number_types"_test = [] {
      lazy_beve_test::Numbers nums;
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(nums, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto& doc = *result;
      expect(doc["int_val"].get<int32_t>().value() == 42);
      expect(std::abs(doc["float_val"].get<double>().value() - 3.14) < 0.001);
      expect(doc["negative"].get<int64_t>().value() == -100);
      expect(doc["big"].get<uint64_t>().value() == 9007199254740993ULL);
   };

   "lazy_beve_string_view"_test = [] {
      lazy_beve_test::StringData data;
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto& doc = *result;

      auto simple_sv = doc["simple"].get<std::string_view>();
      expect(simple_sv.has_value());
      expect(simple_sv.value() == "hello");

      auto special_sv = doc["with_special"].get<std::string_view>();
      expect(special_sv.has_value());
      expect(special_sv.value() == "hello\nworld");
   };

   "lazy_beve_explicit_bool"_test = [] {
      lazy_beve_test::BoolData data;
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      // explicit bool conversion - true if not null
      expect(static_cast<bool>(*result));
      expect(static_cast<bool>((*result)["exists"]));

      std::optional<int> null_val = std::nullopt;
      std::vector<std::byte> null_buffer;
      auto null_ec = glz::write_beve(null_val, null_buffer);
      expect(null_ec == glz::error_code::none);
      auto null_result = glz::lazy_beve(null_buffer);
      expect(null_result.has_value());
      expect(!static_cast<bool>(*null_result)); // null is false
   };

   "lazy_beve_wrong_type_error"_test = [] {
      lazy_beve_test::MixedData data;
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto& doc = *result;

      // Trying to get string as number should fail
      auto num_result = doc["str"].get<int64_t>();
      expect(!num_result.has_value());
      expect(num_result.error().ec == glz::error_code::get_wrong_type);

      // Trying to get number as string should fail
      auto str_result = doc["num"].get<std::string>();
      expect(!str_result.has_value());
      expect(str_result.error().ec == glz::error_code::get_wrong_type);
   };

   "lazy_beve_progressive_scanning"_test = [] {
      std::map<std::string, int> data{{"a", 1}, {"b", 2}, {"c", 3}, {"d", 4}, {"e", 5}};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto& doc = *result;

      // Sequential access should use progressive scanning
      expect(doc["a"].get<int64_t>().value() == 1);
      expect(doc["b"].get<int64_t>().value() == 2);
      expect(doc["c"].get<int64_t>().value() == 3);
      expect(doc["d"].get<int64_t>().value() == 4);
      expect(doc["e"].get<int64_t>().value() == 5);

      // Accessing earlier key should still work (wrap-around)
      expect(doc["a"].get<int64_t>().value() == 1);
      expect(doc["c"].get<int64_t>().value() == 3);

      // Non-existent key should return error
      auto missing = doc["z"];
      expect(missing.has_error());
   };

   "lazy_beve_reset_parse_pos"_test = [] {
      std::map<std::string, int> data{{"x", 10}, {"y", 20}, {"z", 30}};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto& doc = *result;

      // Access z first (advances parse_pos to end)
      expect(doc["z"].get<int64_t>().value() == 30);

      // Access x (should wrap around)
      expect(doc["x"].get<int64_t>().value() == 10);

      // Reset and access again
      doc.reset_parse_pos();
      expect(doc["y"].get<int64_t>().value() == 20);
   };

   // ============================================================================
   // indexed_lazy_beve_view tests
   // ============================================================================

   "indexed_lazy_beve_view_array_basic"_test = [] {
      std::vector<int> arr{1, 2, 3, 4, 5};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(arr, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto indexed = result->root().index();
      expect(indexed.is_array());
      expect(!indexed.is_object());
      expect(indexed.size() == 5u);
      expect(!indexed.empty());
   };

   "indexed_lazy_beve_view_object_basic"_test = [] {
      std::map<std::string, int> data{{"a", 1}, {"b", 2}, {"c", 3}};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto indexed = result->root().index();
      expect(indexed.is_object());
      expect(!indexed.is_array());
      expect(indexed.size() == 3u);
      expect(!indexed.empty());

      // Key lookup
      expect(indexed["a"].get<int64_t>().value() == 1);
      expect(indexed["b"].get<int64_t>().value() == 2);
      expect(indexed["c"].get<int64_t>().value() == 3);
      expect(indexed["missing"].has_error());

      // Contains
      expect(indexed.contains("a"));
      expect(indexed.contains("b"));
      expect(!indexed.contains("missing"));
   };

   "indexed_lazy_beve_view_object_iteration"_test = [] {
      std::map<std::string, int> data{{"x", 10}, {"y", 20}, {"z", 30}};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto indexed = result->root().index();
      std::vector<std::pair<std::string_view, int64_t>> items;
      for (auto& item : indexed) {
         auto val = item.get<int64_t>();
         if (val) {
            items.emplace_back(item.key(), *val);
         }
      }

      expect(items.size() == 3u);
   };

   "indexed_lazy_beve_view_empty"_test = [] {
      std::vector<int> empty_arr{};
      std::vector<std::byte> buffer1;
      auto ec1 = glz::write_beve(empty_arr, buffer1);
      expect(ec1 == glz::error_code::none);
      auto result1 = glz::lazy_beve(buffer1);
      expect(result1.has_value());
      auto indexed1 = result1->root().index();
      expect(indexed1.empty());
      expect(indexed1.size() == 0u);

      std::map<std::string, int> empty_map{};
      std::vector<std::byte> buffer2;
      auto ec2 = glz::write_beve(empty_map, buffer2);
      expect(ec2 == glz::error_code::none);
      auto result2 = glz::lazy_beve(buffer2);
      expect(result2.has_value());
      auto indexed2 = result2->root().index();
      expect(indexed2.empty());
      expect(indexed2.size() == 0u);
   };

   // ============================================================================
   // raw_beve() and struct deserialization tests
   // ============================================================================

   "lazy_beve_raw_beve_basic"_test = [] {
      lazy_beve_test::User user{"Alice", 30, true};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(user, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      // raw_beve() returns the entire BEVE for a value
      auto raw = result->root().raw_beve();
      expect(raw.size() == buffer.size());
   };

   "lazy_beve_deserialize_struct"_test = [] {
      lazy_beve_test::Container container{{"Alice", 30, true}, 1};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(container, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      // Navigate lazily to "user", then deserialize into struct
      auto user_view = (*result)["user"];
      expect(!user_view.has_error());

      // Get raw BEVE and deserialize
      auto user_beve = user_view.raw_beve();
      lazy_beve_test::User user{};
      auto read_ec = glz::read_beve(user, user_beve);

      expect(read_ec == glz::error_code::none);
      expect(user.name == "Alice");
      expect(user.age == 30);
      expect(user.active == true);
   };

   // ============================================================================
   // read_into<T>() tests
   // ============================================================================

   "lazy_beve_read_into_basic"_test = [] {
      lazy_beve_test::Container container{{"Alice", 30, true}, 1};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(container, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      // Navigate lazily to "user", then deserialize directly
      lazy_beve_test::User user{};
      auto read_ec = (*result)["user"].read_into(user);

      expect(!read_ec);
      expect(user.name == "Alice");
      expect(user.age == 30);
      expect(user.active == true);
   };

   "lazy_beve_read_into_nested"_test = [] {
      lazy_beve_test::PeopleContainer container{{{"Alice", {"New York", "USA"}}, {"Bob", {"London", "UK"}}}};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(container, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      // Deserialize directly using read_into
      lazy_beve_test::Person alice{};
      auto ec1 = (*result)["people"][0].read_into(alice);
      expect(!ec1);
      expect(alice.name == "Alice");
      expect(alice.address.city == "New York");
      expect(alice.address.country == "USA");

      lazy_beve_test::Person bob{};
      auto ec2 = (*result)["people"][1].read_into(bob);
      expect(!ec2);
      expect(bob.name == "Bob");
      expect(bob.address.city == "London");
      expect(bob.address.country == "UK");
   };

   "lazy_beve_read_into_primitive"_test = [] {
      lazy_beve_test::PrimitiveData data;
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      // read_into works with primitive types too
      int count{};
      auto ec1 = (*result)["count"].read_into(count);
      expect(!ec1);
      expect(count == 42);

      double ratio{};
      auto ec2 = (*result)["ratio"].read_into(ratio);
      expect(!ec2);
      expect(std::abs(ratio - 3.14) < 0.001);

      std::string name{};
      auto ec3 = (*result)["name"].read_into(name);
      expect(!ec3);
      expect(name == "test");

      bool active{};
      auto ec4 = (*result)["active"].read_into(active);
      expect(!ec4);
      expect(active == true);
   };

   "lazy_beve_read_into_array"_test = [] {
      lazy_beve_test::ArrayData data;
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      // read_into can deserialize arrays
      std::vector<int> values{};
      auto read_ec = (*result)["values"].read_into(values);
      expect(!read_ec);
      expect(values.size() == 5u);
      expect(values[0] == 1);
      expect(values[4] == 5);
   };

   "lazy_beve_read_into_error_handling"_test = [] {
      lazy_beve_test::SimpleData data;
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      // read_into on missing key should return error
      lazy_beve_test::User user{};
      auto missing_view = (*result)["missing"];
      expect(missing_view.has_error());

      auto read_ec = missing_view.read_into(user);
      expect(bool(read_ec)); // Should have error
   };

   // ============================================================================
   // glz::read_beve overload for lazy_beve_view tests
   // ============================================================================

   "lazy_beve_read_beve_overload_basic"_test = [] {
      lazy_beve_test::Container container{{"Alice", 30, true}, 1};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(container, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      // Use glz::read_beve directly with lazy_beve_view
      lazy_beve_test::User user{};
      auto read_ec = glz::read_beve(user, (*result)["user"]);

      expect(!read_ec);
      expect(user.name == "Alice");
      expect(user.age == 30);
      expect(user.active == true);
   };

   "lazy_beve_read_beve_overload_primitives"_test = [] {
      lazy_beve_test::PrimitiveData data;
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      // glz::read_beve works with primitive types
      int count{};
      auto ec1 = glz::read_beve(count, (*result)["count"]);
      expect(!ec1);
      expect(count == 42);

      double ratio{};
      auto ec2 = glz::read_beve(ratio, (*result)["ratio"]);
      expect(!ec2);
      expect(std::abs(ratio - 3.14) < 0.001);

      std::string name{};
      auto ec3 = glz::read_beve(name, (*result)["name"]);
      expect(!ec3);
      expect(name == "test");

      bool active{};
      auto ec4 = glz::read_beve(active, (*result)["active"]);
      expect(!ec4);
      expect(active == true);
   };

   "lazy_beve_read_beve_overload_vector"_test = [] {
      lazy_beve_test::ArrayData data;
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      // glz::read_beve works with containers
      std::vector<int> values{};
      auto read_ec = glz::read_beve(values, (*result)["values"]);
      expect(!read_ec);
      expect(values.size() == 5u);
      expect(values[0] == 1);
      expect(values[4] == 5);
   };

   // ============================================================================
   // Iterator tests
   // ============================================================================

   "lazy_beve_object_iteration"_test = [] {
      std::map<std::string, int> data{{"a", 1}, {"b", 2}, {"c", 3}};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      int64_t sum = 0;
      size_t count = 0;
      for (auto& item : result->root()) {
         auto val = item.get<int64_t>();
         if (val) sum += *val;
         ++count;
      }
      expect(sum == 6);
      expect(count == 3u);
   };

   "lazy_beve_generic_array_iteration"_test = [] {
      // Use a tuple to create a generic array with different types
      std::tuple<int, std::string, double> data{42, "hello", 3.14};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      size_t count = 0;
      for ([[maybe_unused]] auto& item : result->root()) {
         ++count;
      }
      expect(count == 3u);
   };

   // ============================================================================
   // Typed array tests
   // ============================================================================

   "lazy_beve_typed_array_size"_test = [] {
      std::vector<int> arr{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(arr, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto& doc = *result;
      expect(doc.is_array());
      expect(doc.is_array());
      expect(doc.root().size() == 10u);
      expect(!doc.root().empty());
   };

   "lazy_beve_string_array"_test = [] {
      std::vector<std::string> arr{"hello", "world", "test"};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(arr, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto& doc = *result;
      expect(doc.is_array());
      expect(doc.root().size() == 3u);
   };

   // ============================================================================
   // Large data tests
   // ============================================================================

   "lazy_beve_large_object"_test = [] {
      std::map<std::string, int> data;
      for (int i = 0; i < 100; ++i) {
         data["key" + std::to_string(i)] = i;
      }
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto& doc = *result;
      expect(doc.root().size() == 100u);

      // Access specific keys
      expect(doc["key0"].get<int64_t>().value() == 0);
      expect(doc["key50"].get<int64_t>().value() == 50);
      expect(doc["key99"].get<int64_t>().value() == 99);
   };

   "lazy_beve_large_array"_test = [] {
      std::vector<int> arr;
      for (int i = 0; i < 1000; ++i) {
         arr.push_back(i);
      }
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(arr, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto& doc = *result;
      expect(doc.root().size() == 1000u);
   };

   // ============================================================================
   // Document copy/move tests
   // ============================================================================

   "lazy_beve_document_copy"_test = [] {
      lazy_beve_test::User user{"Alice", 30, true};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(user, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      // Test copy
      auto doc_copy = *result;
      expect(doc_copy["name"].get<std::string>().value() == "Alice");
      expect(doc_copy["age"].get<int64_t>().value() == 30);
   };

   "lazy_beve_document_move"_test = [] {
      lazy_beve_test::User user{"Bob", 25, false};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(user, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      // Test move
      auto doc_moved = std::move(*result);
      expect(doc_moved["name"].get<std::string>().value() == "Bob");
      expect(doc_moved["age"].get<int64_t>().value() == 25);
   };

   // ============================================================================
   // Number-keyed map tests (critical - verifies skip.hpp fixes)
   // ============================================================================

   "lazy_beve_number_keyed_map_iteration"_test = [] {
      std::map<int, std::string> num_map{{1, "one"}, {2, "two"}, {3, "three"}};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(num_map, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      // Iterate over number-keyed map
      size_t count = 0;
      for (auto& item : result->root()) {
         auto val = item.get<std::string_view>();
         expect(val.has_value());
         ++count;
      }
      expect(count == 3u);
   };

   "lazy_beve_number_keyed_map_skip"_test = [] {
      // Test that skipping over a number-keyed map works correctly
      lazy_beve_test::NumMapContainer data;
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      // Access "after" field - this should skip over num_map correctly
      auto after = (*result)["after"].get<std::string_view>();
      expect(after.has_value());
      expect(after.value() == "after_map");
   };

   "lazy_beve_number_keyed_map_indexed"_test = [] {
      std::map<int, std::string> num_map{{10, "ten"}, {20, "twenty"}, {30, "thirty"}};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(num_map, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto indexed = result->root().index();
      expect(indexed.size() == 3u);

      // Access by position (number keys don't support string lookup)
      expect(indexed[0].get<std::string_view>().value() == "ten");
      expect(indexed[1].get<std::string_view>().value() == "twenty");
      expect(indexed[2].get<std::string_view>().value() == "thirty");
   };

   // ============================================================================
   // Boolean array tests
   // ============================================================================

   "lazy_beve_bool_array"_test = [] {
      std::vector<bool> bools{true, false, true, true, false};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(bools, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto& doc = *result;
      expect(doc.is_array());
      expect(doc.root().size() == 5u);
   };

   // ============================================================================
   // Floating point array tests
   // ============================================================================

   "lazy_beve_float_array"_test = [] {
      std::vector<float> floats{1.5f, 2.5f, 3.5f};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(floats, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto& doc = *result;
      expect(doc.is_array());
      expect(doc.root().size() == 3u);
   };

   "lazy_beve_double_array"_test = [] {
      std::vector<double> doubles{1.1, 2.2, 3.3, 4.4};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(doubles, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto& doc = *result;
      expect(doc.is_array());
      expect(doc.root().size() == 4u);
   };

   // ============================================================================
   // Indexed view random access tests
   // ============================================================================

   "indexed_lazy_beve_view_random_access"_test = [] {
      // Use a generic array (tuple) for indexed access - typed arrays don't have per-element tags
      std::tuple<int, int, int, int, int> arr{10, 20, 30, 40, 50};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(arr, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto indexed = result->root().index();

      // Random access by position
      expect(indexed[0].get<int64_t>().value() == 10);
      expect(indexed[2].get<int64_t>().value() == 30);
      expect(indexed[4].get<int64_t>().value() == 50);

      // Out of order access
      expect(indexed[4].get<int64_t>().value() == 50);
      expect(indexed[1].get<int64_t>().value() == 20);
      expect(indexed[3].get<int64_t>().value() == 40);
   };

   "indexed_lazy_beve_iterator_arithmetic"_test = [] {
      // Use object for iterator arithmetic test - more practical use case
      std::map<std::string, int> data;
      for (int i = 1; i <= 10; ++i) {
         data["k" + std::to_string(i)] = i;
      }
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto indexed = result->root().index();
      expect(indexed.size() == 10u);

      auto it = indexed.begin();

      // Test iterator arithmetic
      it += 3;
      // Values depend on map ordering, just verify no crash
      expect((*it).get<int64_t>().has_value());

      it -= 2;
      expect((*it).get<int64_t>().has_value());

      auto it2 = it + 5;
      expect((*it2).get<int64_t>().has_value());

      auto dist = indexed.end() - indexed.begin();
      expect(dist == 10);
   };

   // ============================================================================
   // Error handling tests
   // ============================================================================

   "lazy_beve_empty_buffer"_test = [] {
      std::vector<std::byte> empty_buffer;
      auto result = glz::lazy_beve(empty_buffer);
      expect(!result.has_value());
   };

   "lazy_beve_truncated_buffer"_test = [] {
      lazy_beve_test::User user{"Alice", 30, true};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(user, buffer);
      expect(ec == glz::error_code::none);

      // Truncate the buffer
      buffer.resize(buffer.size() / 2);

      auto result = glz::lazy_beve(buffer);
      // May or may not parse initially, but accessing fields should fail gracefully
      if (result.has_value()) {
         // Try to access - should handle gracefully
         [[maybe_unused]] auto name = (*result)["name"];
         // Don't crash - that's the important part
      }
   };

   "lazy_beve_missing_key"_test = [] {
      std::map<std::string, int> data{{"a", 1}};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto missing = (*result)["nonexistent"];
      expect(missing.has_error());
      expect(missing.error() == glz::error_code::key_not_found);
   };

   "lazy_beve_object_key_length_past_end"_test = [] {
      // Object with string keys (0x03), one key (compressed count 0x04), a key
      // length prefix of 60 (0xF0), and no key bytes: the declared key length
      // runs past the end of the buffer. The scanner must not build a key view
      // over the missing bytes.
      std::vector<std::byte> buffer{std::byte{0x03}, std::byte{0x04}, std::byte{0xF0}};

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());
      auto& doc = result.value();

      for (auto v : doc.root()) {
         expect(v.key().size() <= buffer.size());
      }

      expect(doc.root().index().size() == 0);

      auto looked_up = doc.root()["anything"];
      expect(looked_up.has_error());
   };

   // ============================================================================
   // Unsigned integer array tests
   // ============================================================================

   "lazy_beve_uint_array"_test = [] {
      std::vector<uint32_t> uints{100u, 200u, 300u};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(uints, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto& doc = *result;
      expect(doc.is_array());
      expect(doc.root().size() == 3u);
   };

   "lazy_beve_uint64_array"_test = [] {
      std::vector<uint64_t> uints{1ULL << 40, 1ULL << 50, 1ULL << 60};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(uints, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      auto& doc = *result;
      expect(doc.is_array());
      expect(doc.root().size() == 3u);
   };

   // ============================================================================
   // Size accessor tests
   // ============================================================================

   "lazy_beve_size_accessor_string"_test = [] {
      std::map<std::string, std::string> data{{"username", "alice_wonderland"}, {"email", "alice@example.com"}};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      // Test the size accessor syntax: doc.size["key"]
      expect(result->size["username"] == 16u); // "alice_wonderland" is 16 chars
      expect(result->size["email"] == 17u); // "alice@example.com" is 17 chars
   };

   "lazy_beve_size_accessor_array"_test = [] {
      std::map<std::string, std::vector<int>> data{{"items", {1, 2, 3, 4, 5}}};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      // Size of array field returns element count
      expect(result->size["items"] == 5u);
   };

   "lazy_beve_size_accessor_number"_test = [] {
      std::map<std::string, int> data{{"count", 42}};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      // Numbers return 0 for value_size
      expect(result->size["count"] == 0u);
   };

   "lazy_beve_size_method_string"_test = [] {
      lazy_beve_test::User user{"Bob", 25, true};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(user, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      // Test size() method directly on string field
      expect(result->root()["name"].size() == 3u); // "Bob"
   };

   "lazy_beve_size_accessor_index"_test = [] {
      std::vector<std::string> strings{"hello", "world", "test"};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(strings, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      // Test size accessor with index: result->size[i]
      expect(result->size[0] == 5u); // "hello"
      expect(result->size[1] == 5u); // "world"
      expect(result->size[2] == 4u); // "test"
   };

   "lazy_beve_size_object_keys"_test = [] {
      std::map<std::string, int> data{{"a", 1}, {"b", 2}, {"c", 3}};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      // size() on object returns key count
      expect(result->root().size() == 3u);
   };

   "lazy_beve_size_nested"_test = [] {
      std::map<std::string, std::map<std::string, std::string>> data{{"outer", {{"inner", "value123"}}}};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      // Nested access
      expect(result->root()["outer"]["inner"].size() == 8u); // "value123"
      expect(result->root()["outer"].size() == 1u); // 1 key in inner object
   };

   "lazy_beve_size_typed_string_array"_test = [] {
      std::vector<std::string> strings{"alpha", "beta", "gamma"};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(strings, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());
      expect(result->root().is_typed_array());

      // Test size via indexed view (uses synthetic tags)
      auto indexed = result->root().index();
      expect(indexed[0].size() == 5u); // "alpha"
      expect(indexed[1].size() == 4u); // "beta"
      expect(indexed[2].size() == 5u); // "gamma"
   };

   "lazy_beve_size_boolean_null"_test = [] {
      std::map<std::string, std::optional<bool>> data{{"flag", true}, {"empty", std::nullopt}};
      std::vector<std::byte> buffer;
      auto ec = glz::write_beve(data, buffer);
      expect(ec == glz::error_code::none);

      auto result = glz::lazy_beve(buffer);
      expect(result.has_value());

      // Booleans and nulls return 0 for size
      expect(result->size["flag"] == 0u);
      expect(result->size["empty"] == 0u);
   };
};

// Regression coverage for the lazy API taking `opts` (a concrete base-class NTTP) rather than
// `auto`, which sliced any derived user options struct down to glz::opts and silently dropped
// every extra field.
suite lazy_beve_derived_opts_tests = [] {
   "lazy_beve_derived_opts_preserve_exact_type"_test = [] {
      // Sliced to glz::opts, these would collapse to the same instantiation.
      expect(not std::same_as<glz::lazy_beve_document<derived_beve_opts>,
                              glz::lazy_beve_document<glz::opts{.format = glz::BEVE}>>);
      expect(
         not std::same_as<glz::lazy_beve_view<derived_beve_opts>, glz::lazy_beve_view<glz::opts{.format = glz::BEVE}>>);
   };

   "lazy_beve_derived_opts_reach_read_into"_test = [] {
      // allow_conversions only exists on the derived struct and defaults to true when absent, so
      // the double -> float read is refused only if the lazy API instantiated on the exact type.
      std::string buffer{};
      expect(not glz::write_beve(lazy_beve_test::DoubleField{}, buffer));

      auto result = glz::lazy_beve<derived_beve_opts>(buffer);
      expect(result.has_value());

      lazy_beve_test::FloatField narrowed{};
      expect(bool((*result).root().read_into(narrowed)));
   };
};

// glz::write_beve on a lazy BEVE view did not compile: the null branch called
// dump_type<tag::null>(b, ix), and dump_type takes its value as a function argument rather than
// a template parameter. Nothing in the suite instantiated the writer, so the whole public path
// stayed broken. These tests exist to instantiate it.
suite lazy_beve_writer_tests = [] {
   "write_beve_round_trips_a_lazy_view"_test = [] {
      std::string buffer{};
      const lazy_beve_test::User user{"Alice", 30, true};
      expect(not glz::write_beve(user, buffer));

      auto doc = glz::lazy_beve<glz::opts{.format = glz::BEVE}>(buffer);
      expect(doc.has_value());
      if (not doc) return;

      std::string out{};
      expect(not glz::write_beve(doc->root(), out));
      expect(out == buffer) << "rewrote " << out.size() << " of " << buffer.size() << " bytes";

      // The rewritten bytes have to be BEVE a reader still accepts, not merely equal-length.
      lazy_beve_test::User restored{};
      expect(not glz::read_beve(restored, out));
      expect(restored.name == user.name);
      expect(restored.age == user.age);
      expect(restored.active == user.active);
   };

   "write_beve_empty_view_writes_the_null_tag"_test = [] {
      // The branch that failed to compile. A view with no data writes BEVE null, which is the
      // single tag byte 0, and a reader has to accept it as such.
      glz::lazy_beve_view<glz::opts{.format = glz::BEVE}> empty{};
      expect(not empty.has_error());
      expect(empty.data() == nullptr);

      std::string out{};
      expect(not glz::write_beve(empty, out));
      expect(out.size() == 1u) << out.size();
      expect(out == std::string(1, '\0'));

      std::optional<int> restored{42};
      expect(not glz::read_beve(restored, out));
      expect(not restored.has_value());
   };

   "write_beve_nested_view_stops_at_its_own_end"_test = [] {
      // A member view's beve_end() is the whole document's end, so writing one must emit that
      // member alone rather than everything that follows it.
      std::string buffer{};
      const lazy_beve_test::Person person{"Bob", {"Paris", "France"}};
      expect(not glz::write_beve(person, buffer));

      auto doc = glz::lazy_beve<glz::opts{.format = glz::BEVE}>(buffer);
      expect(doc.has_value());
      if (not doc) return;

      std::string out{};
      expect(not glz::write_beve((*doc)["address"], out));
      expect(out.size() < buffer.size());

      lazy_beve_test::Address restored{};
      expect(not glz::read_beve(restored, out));
      expect(restored.city == person.address.city);
      expect(restored.country == person.address.country);
   };

   "write_beve_respects_derived_opts"_test = [] {
      // Instantiating the writer on a derived option struct has to compile too; the skip inside
      // it used a default-constructed opts{}, which sliced the pack away.
      std::string buffer{};
      expect(not glz::write_beve(lazy_beve_test::User{"Carol", 41, false}, buffer));

      auto doc = glz::lazy_beve<derived_beve_opts>(buffer);
      expect(doc.has_value());
      if (not doc) return;

      std::string out{};
      expect(not glz::write_beve(doc->root(), out));
      expect(out == buffer);
   };
};

int main() { return 0; }
