/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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
 *
 *   http://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 <arrow/array/builder_binary.h>
#include <arrow/array/builder_decimal.h>
#include <arrow/array/builder_nested.h>
#include <arrow/array/builder_primitive.h>
#include <arrow/extension_type.h>
#include <arrow/json/from_string.h>
#include <arrow/type.h>
#include <arrow/util/decimal.h>
#include <avro/Generic.hh>
#include <avro/Node.hh>
#include <avro/NodeImpl.hh>
#include <avro/Types.hh>

#include "iceberg/arrow/arrow_status_internal.h"
#include "iceberg/avro/avro_data_util_internal.h"
#include "iceberg/avro/avro_schema_util_internal.h"
#include "iceberg/metadata_columns.h"
#include "iceberg/schema.h"
#include "iceberg/schema_util.h"
#include "iceberg/util/checked_cast.h"
#include "iceberg/util/macros.h"

namespace iceberg::avro {

using ::iceberg::arrow::ToErrorKind;

namespace {

/// \brief Forward declaration for mutual recursion.
Status AppendFieldToBuilder(const ::avro::NodePtr& avro_node,
                            const ::avro::GenericDatum& avro_datum,
                            const FieldProjection& projection,
                            const SchemaField& projected_field,
                            const arrow::MetadataColumnContext& metadata_context,
                            ::arrow::ArrayBuilder* array_builder);

/// \brief Append Avro record data to Arrow struct builder.
Status AppendStructToBuilder(const ::avro::NodePtr& avro_node,
                             const ::avro::GenericDatum& avro_datum,
                             const std::span<const FieldProjection>& projections,
                             const StructType& struct_type,
                             const arrow::MetadataColumnContext& metadata_context,
                             ::arrow::ArrayBuilder* array_builder) {
  if (avro_node->type() != ::avro::AVRO_RECORD) {
    return InvalidArgument("Expected Avro record, got type: {}", ToString(avro_node));
  }
  const auto& avro_record = avro_datum.value<::avro::GenericRecord>();

  auto* struct_builder = internal::checked_cast<::arrow::StructBuilder*>(array_builder);
  ICEBERG_ARROW_RETURN_NOT_OK(struct_builder->Append());

  for (size_t i = 0; i < projections.size(); ++i) {
    const auto& field_projection = projections[i];
    const auto& expected_field = struct_type.fields()[i];
    auto* field_builder = struct_builder->field_builder(static_cast<int>(i));

    if (field_projection.kind == FieldProjection::Kind::kProjected) {
      size_t avro_field_index = std::get<size_t>(field_projection.from);
      if (avro_field_index >= avro_record.fieldCount()) {
        return InvalidArgument("Avro field index {} out of bound {}", avro_field_index,
                               avro_record.fieldCount());
      }

      const auto& avro_field_node = avro_node->leafAt(avro_field_index);
      const auto& avro_field_datum = avro_record.fieldAt(avro_field_index);
      ICEBERG_RETURN_UNEXPECTED(AppendFieldToBuilder(avro_field_node, avro_field_datum,
                                                     field_projection, expected_field,
                                                     metadata_context, field_builder));
    } else if (field_projection.kind == FieldProjection::Kind::kNull) {
      ICEBERG_ARROW_RETURN_NOT_OK(field_builder->AppendNull());
    } else if (field_projection.kind == FieldProjection::Kind::kMetadata) {
      int32_t field_id = expected_field.field_id();
      if (field_id == MetadataColumns::kFilePathColumnId) {
        auto string_builder =
            internal::checked_cast<::arrow::StringBuilder*>(field_builder);
        ICEBERG_ARROW_RETURN_NOT_OK(string_builder->Append(metadata_context.file_path));
      } else if (field_id == MetadataColumns::kFilePositionColumnId) {
        auto int_builder = internal::checked_cast<::arrow::Int64Builder*>(field_builder);
        ICEBERG_ARROW_RETURN_NOT_OK(int_builder->Append(metadata_context.next_file_pos));
      } else {
        return NotSupported("Unsupported metadata column field id: {}", field_id);
      }
    } else {
      return NotImplemented("Unsupported field projection kind: {}",
                            ToString(field_projection.kind));
    }
  }
  return {};
}

/// \brief Append Avro array data to Arrow list builder.
Status AppendListToBuilder(const ::avro::NodePtr& avro_node,
                           const ::avro::GenericDatum& avro_datum,
                           const FieldProjection& element_projection,
                           const ListType& list_type,
                           const arrow::MetadataColumnContext& metadata_context,
                           ::arrow::ArrayBuilder* array_builder) {
  if (avro_node->type() != ::avro::AVRO_ARRAY) {
    return InvalidArgument("Expected Avro array, got type: {}", ToString(avro_node));
  }
  const auto& avro_array = avro_datum.value<::avro::GenericArray>();

  auto* list_builder = internal::checked_cast<::arrow::ListBuilder*>(array_builder);
  ICEBERG_ARROW_RETURN_NOT_OK(list_builder->Append());

  auto* value_builder = list_builder->value_builder();
  const auto& element_node = avro_node->leafAt(0);
  const auto& element_field = list_type.fields().back();

  for (const auto& element : avro_array.value()) {
    ICEBERG_RETURN_UNEXPECTED(AppendFieldToBuilder(element_node, element,
                                                   element_projection, element_field,
                                                   metadata_context, value_builder));
  }
  return {};
}

/// \brief Append Avro map data to Arrow map builder.
Status AppendMapToBuilder(const ::avro::NodePtr& avro_node,
                          const ::avro::GenericDatum& avro_datum,
                          const FieldProjection& key_projection,
                          const FieldProjection& value_projection,
                          const MapType& map_type,
                          const arrow::MetadataColumnContext& metadata_context,
                          ::arrow::ArrayBuilder* array_builder) {
  auto* map_builder = internal::checked_cast<::arrow::MapBuilder*>(array_builder);

  if (avro_node->type() == ::avro::AVRO_MAP) {
    // Handle regular Avro map: map<string, value>
    const auto& avro_map = avro_datum.value<::avro::GenericMap>();
    const auto& map_entries = avro_map.value();

    const auto& key_node = avro_node->leafAt(0);
    const auto& value_node = avro_node->leafAt(1);

    const auto& key_field = map_type.key();
    const auto& value_field = map_type.value();

    ICEBERG_ARROW_RETURN_NOT_OK(map_builder->Append());
    auto* key_builder = map_builder->key_builder();
    auto* item_builder = map_builder->item_builder();

    for (const auto& entry : map_entries) {
      ICEBERG_RETURN_UNEXPECTED(AppendFieldToBuilder(key_node, entry.first,
                                                     key_projection, key_field,
                                                     metadata_context, key_builder));
      ICEBERG_RETURN_UNEXPECTED(AppendFieldToBuilder(value_node, entry.second,
                                                     value_projection, value_field,
                                                     metadata_context, item_builder));
    }

    return {};
  } else if (avro_node->type() == ::avro::AVRO_ARRAY && HasMapLogicalType(avro_node)) {
    // Handle array-based map: list<struct<key, value>>
    const auto& avro_array = avro_datum.value<::avro::GenericArray>();
    const auto& array_entries = avro_array.value();

    const auto& key_field = map_type.key();
    const auto& value_field = map_type.value();

    ICEBERG_ARROW_RETURN_NOT_OK(map_builder->Append());
    auto* key_builder = map_builder->key_builder();
    auto* item_builder = map_builder->item_builder();

    const auto& record_node = avro_node->leafAt(0);
    if (record_node->type() != ::avro::AVRO_RECORD || record_node->leaves() != 2) {
      return InvalidArgument(
          "Array-based map must contain records with exactly 2 fields, got: {}",
          ToString(record_node));
    }
    const auto& key_node = record_node->leafAt(0);
    const auto& value_node = record_node->leafAt(1);

    for (const auto& entry : array_entries) {
      const auto& record = entry.value<::avro::GenericRecord>();
      ICEBERG_RETURN_UNEXPECTED(AppendFieldToBuilder(key_node, record.fieldAt(0),
                                                     key_projection, key_field,
                                                     metadata_context, key_builder));
      ICEBERG_RETURN_UNEXPECTED(AppendFieldToBuilder(value_node, record.fieldAt(1),
                                                     value_projection, value_field,
                                                     metadata_context, item_builder));
    }

    return {};
  } else {
    return InvalidArgument("Expected Avro map or array with map logical type, got: {}",
                           ToString(avro_node));
  }
}

/// \brief Append nested Avro data to Arrow array builder based on type.
Status AppendNestedValueToBuilder(const ::avro::NodePtr& avro_node,
                                  const ::avro::GenericDatum& avro_datum,
                                  const std::span<const FieldProjection>& projections,
                                  const NestedType& projected_type,
                                  const arrow::MetadataColumnContext& metadata_context,
                                  ::arrow::ArrayBuilder* array_builder) {
  switch (projected_type.type_id()) {
    case TypeId::kStruct: {
      const auto& struct_type = internal::checked_cast<const StructType&>(projected_type);
      return AppendStructToBuilder(avro_node, avro_datum, projections, struct_type,
                                   metadata_context, array_builder);
    }

    case TypeId::kList: {
      if (projections.size() != 1) {
        return InvalidArgument("Expected 1 projection for list, got: {}",
                               projections.size());
      }
      const auto& list_type = internal::checked_cast<const ListType&>(projected_type);
      return AppendListToBuilder(avro_node, avro_datum, projections[0], list_type,
                                 metadata_context, array_builder);
    }

    case TypeId::kMap: {
      if (projections.size() != 2) {
        return InvalidArgument("Expected 2 projections for map, got: {}",
                               projections.size());
      }
      const auto& map_type = internal::checked_cast<const MapType&>(projected_type);
      return AppendMapToBuilder(avro_node, avro_datum, projections[0], projections[1],
                                map_type, metadata_context, array_builder);
    }

    default:
      return InvalidArgument("Unsupported nested type: {}", projected_type.ToString());
  }
}

Status AppendPrimitiveValueToBuilder(const ::avro::NodePtr& avro_node,
                                     const ::avro::GenericDatum& avro_datum,
                                     const SchemaField& projected_field,
                                     ::arrow::ArrayBuilder* array_builder) {
  const auto& projected_type = *projected_field.type();
  if (!projected_type.is_primitive()) {
    return InvalidArgument("Expected primitive type, got: {}", projected_type.ToString());
  }

  switch (projected_type.type_id()) {
    case TypeId::kBoolean: {
      if (avro_node->type() != ::avro::AVRO_BOOL) {
        return InvalidArgument("Expected Avro boolean for boolean field, got: {}",
                               ToString(avro_node));
      }
      auto* builder = internal::checked_cast<::arrow::BooleanBuilder*>(array_builder);
      ICEBERG_ARROW_RETURN_NOT_OK(builder->Append(avro_datum.value<bool>()));
      return {};
    }

    case TypeId::kInt: {
      if (avro_node->type() != ::avro::AVRO_INT) {
        return InvalidArgument("Expected Avro int for int field, got: {}",
                               ToString(avro_node));
      }
      auto* builder = internal::checked_cast<::arrow::Int32Builder*>(array_builder);
      ICEBERG_ARROW_RETURN_NOT_OK(builder->Append(avro_datum.value<int32_t>()));
      return {};
    }

    case TypeId::kLong: {
      auto* builder = internal::checked_cast<::arrow::Int64Builder*>(array_builder);
      if (avro_node->type() == ::avro::AVRO_LONG) {
        ICEBERG_ARROW_RETURN_NOT_OK(builder->Append(avro_datum.value<int64_t>()));
      } else if (avro_node->type() == ::avro::AVRO_INT) {
        ICEBERG_ARROW_RETURN_NOT_OK(
            builder->Append(static_cast<int64_t>(avro_datum.value<int32_t>())));
      } else {
        return InvalidArgument("Expected Avro int/long for long field, got: {}",
                               ToString(avro_node));
      }
      return {};
    }

    case TypeId::kFloat: {
      if (avro_node->type() != ::avro::AVRO_FLOAT) {
        return InvalidArgument("Expected Avro float for float field, got: {}",
                               ToString(avro_node));
      }
      auto* builder = internal::checked_cast<::arrow::FloatBuilder*>(array_builder);
      ICEBERG_ARROW_RETURN_NOT_OK(builder->Append(avro_datum.value<float>()));
      return {};
    }

    case TypeId::kDouble: {
      auto* builder = internal::checked_cast<::arrow::DoubleBuilder*>(array_builder);
      if (avro_node->type() == ::avro::AVRO_DOUBLE) {
        ICEBERG_ARROW_RETURN_NOT_OK(builder->Append(avro_datum.value<double>()));
      } else if (avro_node->type() == ::avro::AVRO_FLOAT) {
        ICEBERG_ARROW_RETURN_NOT_OK(
            builder->Append(static_cast<double>(avro_datum.value<float>())));
      } else {
        return InvalidArgument("Expected Avro float/double for double field, got: {}",
                               ToString(avro_node));
      }
      return {};
    }

    case TypeId::kString: {
      if (avro_node->type() != ::avro::AVRO_STRING) {
        return InvalidArgument("Expected Avro string for string field, got: {}",
                               ToString(avro_node));
      }
      auto* builder = internal::checked_cast<::arrow::StringBuilder*>(array_builder);
      ICEBERG_ARROW_RETURN_NOT_OK(builder->Append(avro_datum.value<std::string>()));
      return {};
    }

    case TypeId::kBinary: {
      if (avro_node->type() != ::avro::AVRO_BYTES) {
        return InvalidArgument("Expected Avro bytes for binary field, got: {}",
                               ToString(avro_node));
      }
      auto* builder = internal::checked_cast<::arrow::BinaryBuilder*>(array_builder);
      const auto& bytes = avro_datum.value<std::vector<uint8_t>>();
      ICEBERG_ARROW_RETURN_NOT_OK(
          builder->Append(bytes.data(), static_cast<int32_t>(bytes.size())));
      return {};
    }

    case TypeId::kFixed: {
      if (avro_node->type() != ::avro::AVRO_FIXED) {
        return InvalidArgument("Expected Avro fixed for fixed field, got: {}",
                               ToString(avro_node));
      }
      const auto& fixed = avro_datum.value<::avro::GenericFixed>();
      const auto& fixed_type = internal::checked_cast<const FixedType&>(projected_type);

      if (static_cast<size_t>(fixed.value().size()) != fixed_type.length()) {
        return InvalidArgument("Expected Avro fixed[{}], got: {}", fixed_type.length(),
                               ToString(avro_node));
      }

      auto* builder =
          internal::checked_cast<::arrow::FixedSizeBinaryBuilder*>(array_builder);
      const auto& value = fixed.value();
      ICEBERG_ARROW_RETURN_NOT_OK(
          builder->Append(reinterpret_cast<const uint8_t*>(value.data())));
      return {};
    }

    case TypeId::kUuid: {
      if (avro_node->type() != ::avro::AVRO_FIXED ||
          avro_node->logicalType().type() != ::avro::LogicalType::UUID) {
        return InvalidArgument("Expected Avro fixed for uuid field, got: {}",
                               ToString(avro_node));
      }

      auto* builder =
          internal::checked_cast<::arrow::FixedSizeBinaryBuilder*>(array_builder);
      const auto& fixed = avro_datum.value<::avro::GenericFixed>();
      if (fixed.value().size() != 16) {
        return InvalidArgument("Expected UUID fixed length 16, got: {}",
                               fixed.value().size());
      }
      const auto& value = fixed.value();
      ICEBERG_ARROW_RETURN_NOT_OK(
          builder->Append(reinterpret_cast<const uint8_t*>(value.data())));
      return {};
    }

    case TypeId::kDecimal: {
      if (avro_node->type() != ::avro::AVRO_FIXED ||
          avro_node->logicalType().type() != ::avro::LogicalType::DECIMAL) {
        return InvalidArgument(
            "Expected Avro fixed with decimal logical type for decimal field, got: {}",
            ToString(avro_node));
      }

      const auto& fixed = avro_datum.value<::avro::GenericFixed>();
      const auto& value = fixed.value();
      ICEBERG_ARROW_ASSIGN_OR_RETURN(
          auto decimal, ::arrow::Decimal128::FromBigEndian(value.data(), value.size()));
      auto* builder = internal::checked_cast<::arrow::Decimal128Builder*>(array_builder);
      ICEBERG_ARROW_RETURN_NOT_OK(builder->Append(decimal));
      return {};
    }

    case TypeId::kDate: {
      if (avro_node->type() != ::avro::AVRO_INT ||
          avro_node->logicalType().type() != ::avro::LogicalType::DATE) {
        return InvalidArgument(
            "Expected Avro int with DATE logical type for date field, got: {}",
            ToString(avro_node));
      }
      auto* builder = internal::checked_cast<::arrow::Date32Builder*>(array_builder);
      ICEBERG_ARROW_RETURN_NOT_OK(builder->Append(avro_datum.value<int32_t>()));
      return {};
    }

    case TypeId::kTime: {
      if (avro_node->type() != ::avro::AVRO_LONG ||
          avro_node->logicalType().type() != ::avro::LogicalType::TIME_MICROS) {
        return InvalidArgument(
            "Expected Avro long with TIME_MICROS for time field, got: {}",
            ToString(avro_node));
      }
      auto* builder = internal::checked_cast<::arrow::Time64Builder*>(array_builder);
      ICEBERG_ARROW_RETURN_NOT_OK(builder->Append(avro_datum.value<int64_t>()));
      return {};
    }

    case TypeId::kTimestamp:
    case TypeId::kTimestampTz: {
      if (avro_node->type() != ::avro::AVRO_LONG ||
          avro_node->logicalType().type() != ::avro::LogicalType::TIMESTAMP_MICROS) {
        return InvalidArgument(
            "Expected Avro long with TIMESTAMP_MICROS for timestamp field, got: {}",
            ToString(avro_node));
      }
      auto* builder = internal::checked_cast<::arrow::TimestampBuilder*>(array_builder);
      ICEBERG_ARROW_RETURN_NOT_OK(builder->Append(avro_datum.value<int64_t>()));
      return {};
    }

    case TypeId::kTimestampNs:
    case TypeId::kTimestampTzNs: {
      if (avro_node->type() != ::avro::AVRO_LONG ||
          avro_node->logicalType().type() != ::avro::LogicalType::TIMESTAMP_NANOS) {
        return InvalidArgument(
            "Expected Avro long with TIMESTAMP_NANOS for timestamp field, got: {}",
            ToString(avro_node));
      }
      auto* builder = internal::checked_cast<::arrow::TimestampBuilder*>(array_builder);
      ICEBERG_ARROW_RETURN_NOT_OK(builder->Append(avro_datum.value<int64_t>()));
      return {};
    }

    default:
      return InvalidArgument("Unsupported primitive type {} to append avro node {}",
                             projected_field.type()->ToString(), ToString(avro_node));
  }
}

/// \brief Dispatch to appropriate handlers based on the projection kind.
Status AppendFieldToBuilder(const ::avro::NodePtr& avro_node,
                            const ::avro::GenericDatum& avro_datum,
                            const FieldProjection& projection,
                            const SchemaField& projected_field,
                            const arrow::MetadataColumnContext& metadata_context,
                            ::arrow::ArrayBuilder* array_builder) {
  if (projection.kind == FieldProjection::Kind::kNull) {
    ICEBERG_ARROW_RETURN_NOT_OK(array_builder->AppendNull());
    return {};
  }

  if (avro_node->type() == ::avro::AVRO_UNION) {
    size_t branch = avro_datum.unionBranch();
    if (avro_node->leafAt(branch)->type() == ::avro::AVRO_NULL) {
      ICEBERG_ARROW_RETURN_NOT_OK(array_builder->AppendNull());
      return {};
    } else {
      return AppendFieldToBuilder(avro_node->leafAt(branch), avro_datum, projection,
                                  projected_field, metadata_context, array_builder);
    }
  }

  const auto& projected_type = *projected_field.type();
  if (projected_type.is_primitive()) {
    return AppendPrimitiveValueToBuilder(avro_node, avro_datum, projected_field,
                                         array_builder);
  } else {
    const auto& nested_type = internal::checked_cast<const NestedType&>(projected_type);
    return AppendNestedValueToBuilder(avro_node, avro_datum, projection.children,
                                      nested_type, metadata_context, array_builder);
  }
}

}  // namespace

Status AppendDatumToBuilder(const ::avro::NodePtr& avro_node,
                            const ::avro::GenericDatum& avro_datum,
                            const SchemaProjection& projection,
                            const Schema& projected_schema,
                            const arrow::MetadataColumnContext& metadata_context,
                            ::arrow::ArrayBuilder* array_builder) {
  return AppendNestedValueToBuilder(avro_node, avro_datum, projection.fields,
                                    projected_schema, metadata_context, array_builder);
}

namespace {

// ToAvroNodeVisitor uses 0 for null branch and 1 for value branch.
constexpr int64_t kNullBranch = 0;
constexpr int64_t kValueBranch = 1;

}  // namespace

Status ExtractDatumFromArray(const ::arrow::Array& array, int64_t index,
                             ::avro::GenericDatum* datum) {
  if (index < 0 || index >= array.length()) {
    return InvalidArgument("Cannot extract datum from array at index {} of length {}",
                           index, array.length());
  }

  if (array.IsNull(index)) {
    if (datum->type() == ::avro::AVRO_NULL) {
      return {};
    }
    if (!datum->isUnion()) [[unlikely]] {
      return InvalidSchema("Cannot extract null to non-union type: {}",
                           ::avro::toString(datum->type()));
    }
    datum->selectBranch(kNullBranch);
    return {};
  }

  if (datum->isUnion()) {
    datum->selectBranch(kValueBranch);
  }

  switch (array.type()->id()) {
    case ::arrow::Type::BOOL: {
      const auto& bool_array =
          internal::checked_cast<const ::arrow::BooleanArray&>(array);
      datum->value<bool>() = bool_array.Value(index);
      return {};
    }

    case ::arrow::Type::INT32: {
      const auto& int32_array = internal::checked_cast<const ::arrow::Int32Array&>(array);
      datum->value<int32_t>() = int32_array.Value(index);
      return {};
    }

    case ::arrow::Type::INT64: {
      const auto& int64_array = internal::checked_cast<const ::arrow::Int64Array&>(array);
      datum->value<int64_t>() = int64_array.Value(index);
      return {};
    }

    case ::arrow::Type::FLOAT: {
      const auto& float_array = internal::checked_cast<const ::arrow::FloatArray&>(array);
      datum->value<float>() = float_array.Value(index);
      return {};
    }

    case ::arrow::Type::DOUBLE: {
      const auto& double_array =
          internal::checked_cast<const ::arrow::DoubleArray&>(array);
      datum->value<double>() = double_array.Value(index);
      return {};
    }

    // TODO(gangwu): support LARGE_STRING.
    case ::arrow::Type::STRING: {
      const auto& string_array =
          internal::checked_cast<const ::arrow::StringArray&>(array);
      datum->value<std::string>() = string_array.GetString(index);
      return {};
    }

    // TODO(gangwu): support LARGE_BINARY.
    case ::arrow::Type::BINARY: {
      const auto& binary_array =
          internal::checked_cast<const ::arrow::BinaryArray&>(array);
      std::string_view value = binary_array.GetView(index);
      datum->value<std::vector<uint8_t>>().assign(
          reinterpret_cast<const uint8_t*>(value.data()),
          reinterpret_cast<const uint8_t*>(value.data()) + value.size());
      return {};
    }

    case ::arrow::Type::FIXED_SIZE_BINARY: {
      const auto& fixed_array =
          internal::checked_cast<const ::arrow::FixedSizeBinaryArray&>(array);
      std::string_view value = fixed_array.GetView(index);
      auto& fixed_datum = datum->value<::avro::GenericFixed>();
      fixed_datum.value().assign(value.begin(), value.end());
      return {};
    }

    case ::arrow::Type::DECIMAL128: {
      const auto& decimal_array =
          internal::checked_cast<const ::arrow::Decimal128Array&>(array);
      std::string_view decimal_value = decimal_array.GetView(index);
      auto& fixed_datum = datum->value<::avro::GenericFixed>();
      auto& bytes = fixed_datum.value();
      bytes.assign(decimal_value.begin(), decimal_value.end());
      std::ranges::reverse(bytes);
      return {};
    }

    case ::arrow::Type::DATE32: {
      const auto& date_array = internal::checked_cast<const ::arrow::Date32Array&>(array);
      datum->value<int32_t>() = date_array.Value(index);
      return {};
    }

    case ::arrow::Type::TIME64: {
      const auto& time_array = internal::checked_cast<const ::arrow::Time64Array&>(array);
      datum->value<int64_t>() = time_array.Value(index);
      return {};
    }

    // For both timestamp and timestamp_tz with time unit as microsecond.
    case ::arrow::Type::TIMESTAMP: {
      const auto& timestamp_array =
          internal::checked_cast<const ::arrow::TimestampArray&>(array);
      datum->value<int64_t>() = timestamp_array.Value(index);
      return {};
    }

    case ::arrow::Type::EXTENSION: {
      if (array.type()->name() == "arrow.uuid") {
        const auto& extension_array =
            internal::checked_cast<const ::arrow::ExtensionArray&>(array);
        const auto& fixed_array =
            internal::checked_cast<const ::arrow::FixedSizeBinaryArray&>(
                *extension_array.storage());
        std::string_view value = fixed_array.GetView(index);
        auto& fixed_datum = datum->value<::avro::GenericFixed>();
        fixed_datum.value().assign(value.begin(), value.end());
        return {};
      }

      return NotSupported("Unsupported Arrow extension type: {}", array.type()->name());
    }

    case ::arrow::Type::STRUCT: {
      const auto& struct_array =
          internal::checked_cast<const ::arrow::StructArray&>(array);
      auto& record = datum->value<::avro::GenericRecord>();
      for (int i = 0; i < struct_array.num_fields(); ++i) {
        ICEBERG_RETURN_UNEXPECTED(
            ExtractDatumFromArray(*struct_array.field(i), index, &record.fieldAt(i)));
      }
      return {};
    }

    // TODO(gangwu): support LARGE_LIST.
    case ::arrow::Type::LIST: {
      const auto& list_array = internal::checked_cast<const ::arrow::ListArray&>(array);
      auto& avro_array = datum->value<::avro::GenericArray>();
      auto& elements = avro_array.value();

      auto start = list_array.value_offset(index);
      auto end = list_array.value_offset(index + 1);
      auto length = end - start;

      auto values = list_array.values();
      elements.resize(length, ::avro::GenericDatum(avro_array.schema()->leafAt(0)));

      for (int64_t i = 0; i < length; ++i) {
        ICEBERG_RETURN_UNEXPECTED(
            ExtractDatumFromArray(*values, start + i, &elements[i]));
      }
      return {};
    }

    case ::arrow::Type::MAP: {
      const auto& map_array = internal::checked_cast<const ::arrow::MapArray&>(array);
      auto start = map_array.value_offset(index);
      auto end = map_array.value_offset(index + 1);
      auto length = end - start;

      auto keys = map_array.keys();
      auto items = map_array.items();

      if (datum->type() == ::avro::AVRO_MAP) {
        // Handle regular Avro map
        auto& avro_map = datum->value<::avro::GenericMap>();
        auto value_node = avro_map.schema()->leafAt(1);

        auto& map_entries = avro_map.value();
        map_entries.resize(
            length, std::make_pair(std::string(), ::avro::GenericDatum(value_node)));

        const auto& key_array =
            internal::checked_cast<const ::arrow::StringArray&>(*keys);

        for (int64_t i = 0; i < length; ++i) {
          auto& map_entry = map_entries[i];
          map_entry.first = key_array.GetString(start + i);
          ICEBERG_RETURN_UNEXPECTED(
              ExtractDatumFromArray(*items, start + i, &map_entry.second));
        }
      } else if (datum->type() == ::avro::AVRO_ARRAY) {
        // Handle array-based map (list<struct<key, value>>)
        auto& avro_array = datum->value<::avro::GenericArray>();
        auto record_node = avro_array.schema()->leafAt(0);
        if (record_node->type() != ::avro::AVRO_RECORD || record_node->leaves() != 2) {
          return InvalidArgument(
              "Expected Avro record with 2 fields for map value, got: {}",
              ToString(record_node));
        }

        auto& elements = avro_array.value();
        elements.resize(length, ::avro::GenericDatum(record_node));

        for (int64_t i = 0; i < length; ++i) {
          auto& record = elements[i].value<::avro::GenericRecord>();
          ICEBERG_RETURN_UNEXPECTED(
              ExtractDatumFromArray(*keys, start + i, &record.fieldAt(0)));
          ICEBERG_RETURN_UNEXPECTED(
              ExtractDatumFromArray(*items, start + i, &record.fieldAt(1)));
        }
      } else {
        return InvalidArgument("Unsupported Avro type for map: {}",
                               static_cast<int>(datum->type()));
      }
      return {};
    }

    default:
      return InvalidArgument("Unsupported Arrow array type: {}",
                             array.type()->ToString());
  }
}

}  // namespace iceberg::avro
