/*
 * 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.
 */

/* This file is based on source code from the Spark Project (http://spark.apache.org/), licensed
 * by the Apache Software Foundation (ASF) under the Apache License, Version 2.0. See the NOTICE
 * file distributed with this work for additional information regarding copyright ownership. */

#include "paimon/common/data/variant/variant_builder.h"

#include <algorithm>
#include <charconv>
#include <cstdlib>
#include <cstring>

#include "fmt/format.h"
#include "paimon/common/data/variant/variant_defs.h"
#include "rapidjson/error/en.h"
#include "rapidjson/memorystream.h"
#include "rapidjson/reader.h"

namespace paimon {

namespace {

// A rapidjson SAX handler that feeds parsed JSON events into a `VariantBuilder`, mirroring
// `GenericVariantBuilder.buildJson` in the Java implementation. Numbers are delivered as raw
// text (`kParseNumbersAsStringsFlag`) so that exact decimal semantics match Jackson's.
class JsonToVariantHandler
    : public rapidjson::BaseReaderHandler<rapidjson::UTF8<>, JsonToVariantHandler> {
 public:
    explicit JsonToVariantHandler(VariantBuilder* builder) : builder_(builder) {}

    bool Null() {
        BeforeValue();
        return Ok(builder_->AppendNull());
    }

    bool Bool(bool b) {
        BeforeValue();
        return Ok(builder_->AppendBoolean(b));
    }

    bool RawNumber(const char* str, rapidjson::SizeType length, bool /*copy*/) {
        BeforeValue();
        return Ok(AppendNumber(std::string_view(str, length)));
    }

    bool String(const char* str, rapidjson::SizeType length, bool /*copy*/) {
        BeforeValue();
        return Ok(builder_->AppendString(std::string_view(str, length)));
    }

    bool StartObject() {
        BeforeValue();
        contexts_.emplace_back(Context{true, builder_->GetWritePos(), {}, {}, {}});
        return true;
    }

    bool Key(const char* str, rapidjson::SizeType length, bool /*copy*/) {
        contexts_.back().pending_key.assign(str, length);
        return true;
    }

    bool EndObject(rapidjson::SizeType /*member_count*/) {
        Context context = std::move(contexts_.back());
        contexts_.pop_back();
        return Ok(builder_->FinishWritingObject(context.start, &context.fields));
    }

    bool StartArray() {
        BeforeValue();
        contexts_.emplace_back(Context{false, builder_->GetWritePos(), {}, {}, {}});
        return true;
    }

    bool EndArray(rapidjson::SizeType /*element_count*/) {
        Context context = std::move(contexts_.back());
        contexts_.pop_back();
        return Ok(builder_->FinishWritingArray(context.start, context.offsets));
    }

    const Status& status() const {
        return status_;
    }

 private:
    struct Context {
        bool is_object;
        int32_t start;
        std::vector<VariantBuilder::FieldEntry> fields;
        std::vector<int32_t> offsets;
        std::string pending_key;
    };

    bool Ok(const Status& status) {
        if (!status.ok()) {
            status_ = status;
            return false;
        }
        return true;
    }

    // Records the offset of the value that is about to be appended in the enclosing container.
    void BeforeValue() {
        if (contexts_.empty()) {
            return;
        }
        Context& top = contexts_.back();
        int32_t offset = builder_->GetWritePos() - top.start;
        if (top.is_object) {
            int32_t id = builder_->AddKey(top.pending_key);
            top.fields.emplace_back(top.pending_key, id, offset);
        } else {
            top.offsets.push_back(offset);
        }
    }

    // Mirrors the Java number handling: integers that fit in a long are appended as long;
    // everything else is first tried as an exact decimal and falls back to double.
    Status AppendNumber(std::string_view text) {
        bool integral = true;
        for (char c : text) {
            if (c != '-' && !(c >= '0' && c <= '9')) {
                integral = false;
                break;
            }
        }
        if (integral) {
            int64_t long_value = 0;
            auto [ptr, ec] = std::from_chars(text.data(), text.data() + text.size(), long_value);
            if (ec == std::errc() && ptr == text.data() + text.size()) {
                return builder_->AppendLong(long_value);
            }
        }
        PAIMON_ASSIGN_OR_RAISE(bool appended, TryAppendDecimal(text));
        if (appended) {
            return Status::OK();
        }
        char* end = nullptr;
        std::string text_copy(text);
        double double_value = std::strtod(text_copy.c_str(), &end);
        if (end != text_copy.c_str() + text_copy.size()) {
            return Status::Invalid(fmt::format("Invalid JSON number: {}", text));
        }
        return builder_->AppendDouble(double_value);
    }

    // Tries to append a JSON number as an exact decimal. Returns whether it succeeded. The input
    // must only use the decimal format (an integer value with an optional '.' in it) and must not
    // use scientific notation. It also must fit into the precision limitation of decimal types.
    Result<bool> TryAppendDecimal(std::string_view text) {
        for (char c : text) {
            if (c != '-' && c != '.' && !(c >= '0' && c <= '9')) {
                return false;
            }
        }
        bool negative = false;
        size_t i = 0;
        if (i < text.size() && text[i] == '-') {
            negative = true;
            ++i;
        }
        __int128_t unscaled = 0;
        int32_t scale = 0;
        int32_t significant_digits = 0;
        bool seen_point = false;
        bool seen_nonzero = false;
        for (; i < text.size(); ++i) {
            char c = text[i];
            if (c == '.') {
                seen_point = true;
                continue;
            }
            if (seen_point) {
                ++scale;
            }
            if (c != '0' || seen_nonzero) {
                seen_nonzero = true;
                ++significant_digits;
            }
            if (significant_digits > VariantDefs::kMaxDecimal16Precision) {
                return false;
            }
            unscaled = unscaled * 10 + (c - '0');
        }
        if (scale > VariantDefs::kMaxDecimal16Precision) {
            return false;
        }
        VariantDecimal decimal;
        decimal.unscaled = negative ? -unscaled : unscaled;
        decimal.scale = scale;
        PAIMON_RETURN_NOT_OK(builder_->AppendDecimal(decimal));
        return true;
    }

    VariantBuilder* builder_;
    std::vector<Context> contexts_;
    Status status_;
};

}  // namespace

Result<std::shared_ptr<GenericVariant>> VariantBuilder::ParseJson(
    std::string_view json, bool allow_duplicate_keys, const std::shared_ptr<MemoryPool>& pool) {
    VariantBuilder builder(allow_duplicate_keys);
    JsonToVariantHandler handler(&builder);
    rapidjson::Reader reader;
    rapidjson::MemoryStream stream(json.data(), json.size());
    rapidjson::ParseResult result =
        reader.Parse<rapidjson::kParseNumbersAsStringsFlag>(stream, handler);
    if (!result) {
        if (!handler.status().ok()) {
            return handler.status();
        }
        return Status::Invalid(fmt::format("Failed to parse JSON: {} (at offset {})",
                                           rapidjson::GetParseError_En(result.Code()),
                                           result.Offset()));
    }
    return builder.Build(pool);
}

Result<std::shared_ptr<GenericVariant>> VariantBuilder::Build(
    const std::shared_ptr<MemoryPool>& pool) {
    auto num_keys = static_cast<int32_t>(dictionary_keys_.size());
    // Use int64 to avoid overflow in accumulating lengths.
    int64_t dictionary_string_size = 0;
    for (const std::string& key : dictionary_keys_) {
        dictionary_string_size += static_cast<int64_t>(key.size());
    }
    // Determine the number of bytes required per offset entry. The largest offset is the
    // one-past-the-end value, which is the total string size. It's very unlikely that the number
    // of keys could be larger, but incorporate that into the calculation in case of pathological
    // data.
    int64_t max_size = std::max(dictionary_string_size, static_cast<int64_t>(num_keys));
    if (max_size > VariantDefs::kSizeLimit) {
        return Status::Invalid("VARIANT_SIZE_LIMIT");
    }
    int32_t offset_size = GetIntegerSize(static_cast<int32_t>(max_size));

    int32_t offset_start = 1 + offset_size;
    int32_t string_start = offset_start + (num_keys + 1) * offset_size;
    int64_t metadata_size = string_start + dictionary_string_size;
    if (metadata_size > VariantDefs::kSizeLimit) {
        return Status::Invalid("VARIANT_SIZE_LIMIT");
    }

    std::shared_ptr<Bytes> metadata =
        Bytes::AllocateBytes(static_cast<size_t>(metadata_size), pool.get());
    auto* metadata_data = reinterpret_cast<uint8_t*>(metadata->data());
    int32_t header_byte = VariantDefs::kVersion | ((offset_size - 1) << 6);
    VariantBinaryUtil::WriteLong(header_byte, 1, metadata_data, 0);
    VariantBinaryUtil::WriteLong(num_keys, offset_size, metadata_data, 1);
    int32_t current_offset = 0;
    for (int32_t i = 0; i < num_keys; ++i) {
        VariantBinaryUtil::WriteLong(current_offset, offset_size, metadata_data,
                                     offset_start + i * offset_size);
        const std::string& key = dictionary_keys_[i];
        memcpy(metadata_data + string_start + current_offset, key.data(), key.size());
        current_offset += static_cast<int32_t>(key.size());
    }
    VariantBinaryUtil::WriteLong(current_offset, offset_size, metadata_data,
                                 offset_start + num_keys * offset_size);

    std::shared_ptr<Bytes> value =
        Bytes::AllocateBytes(static_cast<size_t>(write_pos_), pool.get());
    memcpy(value->data(), write_buffer_.data(), static_cast<size_t>(write_pos_));
    return GenericVariant::Create(std::move(value), std::move(metadata));
}

Status VariantBuilder::AppendString(std::string_view str) {
    bool long_str = static_cast<int32_t>(str.size()) > VariantDefs::kMaxShortStrSize;
    PAIMON_RETURN_NOT_OK(CheckCapacity((long_str ? 1 + VariantDefs::kU32Size : 1) +
                                       static_cast<int32_t>(str.size())));
    if (long_str) {
        write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kLongStr);
        VariantBinaryUtil::WriteLong(static_cast<int64_t>(str.size()), VariantDefs::kU32Size,
                                     write_buffer_.data(), write_pos_);
        write_pos_ += VariantDefs::kU32Size;
    } else {
        write_buffer_[write_pos_++] =
            VariantBinaryUtil::ShortStrHeader(static_cast<int32_t>(str.size()));
    }
    memcpy(write_buffer_.data() + write_pos_, str.data(), str.size());
    write_pos_ += static_cast<int32_t>(str.size());
    return Status::OK();
}

Status VariantBuilder::AppendNull() {
    PAIMON_RETURN_NOT_OK(CheckCapacity(1));
    write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kNull);
    return Status::OK();
}

Status VariantBuilder::AppendBoolean(bool b) {
    PAIMON_RETURN_NOT_OK(CheckCapacity(1));
    write_buffer_[write_pos_++] =
        VariantBinaryUtil::PrimitiveHeader(b ? VariantDefs::kTrue : VariantDefs::kFalse);
    return Status::OK();
}

Status VariantBuilder::AppendLong(int64_t l) {
    PAIMON_RETURN_NOT_OK(CheckCapacity(1 + 8));
    if (l == static_cast<int8_t>(l)) {
        write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kInt1);
        VariantBinaryUtil::WriteLong(l, 1, write_buffer_.data(), write_pos_);
        write_pos_ += 1;
    } else if (l == static_cast<int16_t>(l)) {
        write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kInt2);
        VariantBinaryUtil::WriteLong(l, 2, write_buffer_.data(), write_pos_);
        write_pos_ += 2;
    } else if (l == static_cast<int32_t>(l)) {
        write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kInt4);
        VariantBinaryUtil::WriteLong(l, 4, write_buffer_.data(), write_pos_);
        write_pos_ += 4;
    } else {
        write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kInt8);
        VariantBinaryUtil::WriteLong(l, 8, write_buffer_.data(), write_pos_);
        write_pos_ += 8;
    }
    return Status::OK();
}

Status VariantBuilder::AppendDouble(double d) {
    PAIMON_RETURN_NOT_OK(CheckCapacity(1 + 8));
    write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kDouble);
    int64_t bits;
    memcpy(&bits, &d, sizeof(bits));
    VariantBinaryUtil::WriteLong(bits, 8, write_buffer_.data(), write_pos_);
    write_pos_ += 8;
    return Status::OK();
}

Status VariantBuilder::AppendDecimal(const VariantDecimal& d) {
    PAIMON_RETURN_NOT_OK(CheckCapacity(2 + 16));
    int32_t precision = d.Precision();
    if (d.scale < 0 || d.scale > VariantDefs::kMaxDecimal16Precision ||
        precision > VariantDefs::kMaxDecimal16Precision) {
        return Status::Invalid(
            fmt::format("Decimal precision {} and scale {} must fit into the variant decimal "
                        "limit {}",
                        precision, d.scale, VariantDefs::kMaxDecimal16Precision));
    }
    if (d.scale <= VariantDefs::kMaxDecimal4Precision &&
        precision <= VariantDefs::kMaxDecimal4Precision) {
        write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kDecimal4);
        write_buffer_[write_pos_++] = static_cast<uint8_t>(d.scale);
        VariantBinaryUtil::WriteLong(static_cast<int64_t>(d.unscaled), 4, write_buffer_.data(),
                                     write_pos_);
        write_pos_ += 4;
    } else if (d.scale <= VariantDefs::kMaxDecimal8Precision &&
               precision <= VariantDefs::kMaxDecimal8Precision) {
        write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kDecimal8);
        write_buffer_[write_pos_++] = static_cast<uint8_t>(d.scale);
        VariantBinaryUtil::WriteLong(static_cast<int64_t>(d.unscaled), 8, write_buffer_.data(),
                                     write_pos_);
        write_pos_ += 8;
    } else {
        write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kDecimal16);
        write_buffer_[write_pos_++] = static_cast<uint8_t>(d.scale);
        auto bits = static_cast<__uint128_t>(d.unscaled);
        for (int32_t i = 0; i < 16; ++i) {
            write_buffer_[write_pos_ + i] = static_cast<uint8_t>((bits >> (8 * i)) & 0xFF);
        }
        write_pos_ += 16;
    }
    return Status::OK();
}

Status VariantBuilder::AppendDate(int32_t days_since_epoch) {
    PAIMON_RETURN_NOT_OK(CheckCapacity(1 + 4));
    write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kDate);
    VariantBinaryUtil::WriteLong(days_since_epoch, 4, write_buffer_.data(), write_pos_);
    write_pos_ += 4;
    return Status::OK();
}

Status VariantBuilder::AppendTimestamp(int64_t micros_since_epoch) {
    PAIMON_RETURN_NOT_OK(CheckCapacity(1 + 8));
    write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kTimestamp);
    VariantBinaryUtil::WriteLong(micros_since_epoch, 8, write_buffer_.data(), write_pos_);
    write_pos_ += 8;
    return Status::OK();
}

Status VariantBuilder::AppendTimestampNtz(int64_t micros_since_epoch) {
    PAIMON_RETURN_NOT_OK(CheckCapacity(1 + 8));
    write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kTimestampNtz);
    VariantBinaryUtil::WriteLong(micros_since_epoch, 8, write_buffer_.data(), write_pos_);
    write_pos_ += 8;
    return Status::OK();
}

Status VariantBuilder::AppendFloat(float f) {
    PAIMON_RETURN_NOT_OK(CheckCapacity(1 + 4));
    write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kFloat);
    int32_t bits;
    memcpy(&bits, &f, sizeof(bits));
    VariantBinaryUtil::WriteLong(bits, 4, write_buffer_.data(), write_pos_);
    write_pos_ += 4;
    return Status::OK();
}

Status VariantBuilder::AppendBinary(std::string_view binary) {
    PAIMON_RETURN_NOT_OK(
        CheckCapacity(1 + VariantDefs::kU32Size + static_cast<int32_t>(binary.size())));
    write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kBinary);
    VariantBinaryUtil::WriteLong(static_cast<int64_t>(binary.size()), VariantDefs::kU32Size,
                                 write_buffer_.data(), write_pos_);
    write_pos_ += VariantDefs::kU32Size;
    memcpy(write_buffer_.data() + write_pos_, binary.data(), binary.size());
    write_pos_ += static_cast<int32_t>(binary.size());
    return Status::OK();
}

Status VariantBuilder::AppendUuid(std::string_view uuid_bytes) {
    if (uuid_bytes.size() != 16) {
        return Status::Invalid("UUID must be 16 bytes");
    }
    PAIMON_RETURN_NOT_OK(CheckCapacity(1 + 16));
    write_buffer_[write_pos_++] = VariantBinaryUtil::PrimitiveHeader(VariantDefs::kUuid);
    // UUID is stored big-endian, so don't use WriteLong.
    memcpy(write_buffer_.data() + write_pos_, uuid_bytes.data(), 16);
    write_pos_ += 16;
    return Status::OK();
}

int32_t VariantBuilder::AddKey(std::string_view key) {
    auto it = dictionary_.find(std::string(key));
    if (it != dictionary_.end()) {
        return it->second;
    }
    auto id = static_cast<int32_t>(dictionary_keys_.size());
    dictionary_.emplace(std::string(key), id);
    dictionary_keys_.emplace_back(key);
    return id;
}

Status VariantBuilder::FinishWritingObject(int32_t start, std::vector<FieldEntry>* fields) {
    auto size = static_cast<int32_t>(fields->size());
    std::sort(fields->begin(), fields->end(),
              [](const FieldEntry& a, const FieldEntry& b) { return a.key < b.key; });
    int32_t max_id = size == 0 ? 0 : (*fields)[0].id;
    if (allow_duplicate_keys_) {
        int32_t distinct_pos = 0;
        // Maintain a list of distinct keys in-place.
        for (int32_t i = 1; i < size; ++i) {
            max_id = std::max(max_id, (*fields)[i].id);
            if ((*fields)[i].id == (*fields)[i - 1].id) {
                // Found a duplicate key. Keep the field with a greater offset.
                if ((*fields)[distinct_pos].offset < (*fields)[i].offset) {
                    (*fields)[distinct_pos].offset = (*fields)[i].offset;
                }
            } else {
                // Found a distinct key. Add the field to the list.
                ++distinct_pos;
                (*fields)[distinct_pos] = (*fields)[i];
            }
        }
        if (distinct_pos + 1 < size) {
            size = distinct_pos + 1;
            fields->erase(fields->begin() + size, fields->end());
            // Sort the fields by offsets so that we can move the value data of each field to the
            // new offset without overwriting the fields after it.
            std::sort(fields->begin(), fields->end(),
                      [](const FieldEntry& a, const FieldEntry& b) { return a.offset < b.offset; });
            int32_t current_offset = 0;
            for (int32_t i = 0; i < size; ++i) {
                int32_t old_offset = (*fields)[i].offset;
                PAIMON_ASSIGN_OR_RAISE(
                    int32_t field_size,
                    VariantBinaryUtil::ValueSize(
                        std::string_view(reinterpret_cast<const char*>(write_buffer_.data()),
                                         static_cast<size_t>(write_pos_)),
                        start + old_offset));
                memmove(write_buffer_.data() + start + current_offset,
                        write_buffer_.data() + start + old_offset, static_cast<size_t>(field_size));
                (*fields)[i].offset = current_offset;
                current_offset += field_size;
            }
            write_pos_ = start + current_offset;
            // Change back to the sort order by field keys to meet the variant spec.
            std::sort(fields->begin(), fields->end(),
                      [](const FieldEntry& a, const FieldEntry& b) { return a.key < b.key; });
        }
    } else {
        for (int32_t i = 1; i < size; ++i) {
            max_id = std::max(max_id, (*fields)[i].id);
            if ((*fields)[i].key == (*fields)[i - 1].key) {
                return Status::Invalid("VARIANT_DUPLICATE_KEY");
            }
        }
    }
    int32_t data_size = write_pos_ - start;
    bool large_size = size > VariantDefs::kU8Max;
    int32_t size_bytes = large_size ? VariantDefs::kU32Size : 1;
    int32_t id_size = GetIntegerSize(max_id);
    int32_t offset_size = GetIntegerSize(data_size);
    // The space for the header byte, object size, id list, and offset list.
    int32_t header_size = 1 + size_bytes + size * id_size + (size + 1) * offset_size;
    PAIMON_RETURN_NOT_OK(CheckCapacity(header_size));
    // Shift the just-written field data to make room for the object header section.
    memmove(write_buffer_.data() + start + header_size, write_buffer_.data() + start,
            static_cast<size_t>(data_size));
    write_pos_ += header_size;
    write_buffer_[start] = VariantBinaryUtil::ObjectHeader(large_size, id_size, offset_size);
    VariantBinaryUtil::WriteLong(size, size_bytes, write_buffer_.data(), start + 1);
    int32_t id_start = start + 1 + size_bytes;
    int32_t offset_start = id_start + size * id_size;
    for (int32_t i = 0; i < size; ++i) {
        VariantBinaryUtil::WriteLong((*fields)[i].id, id_size, write_buffer_.data(),
                                     id_start + i * id_size);
        VariantBinaryUtil::WriteLong((*fields)[i].offset, offset_size, write_buffer_.data(),
                                     offset_start + i * offset_size);
    }
    VariantBinaryUtil::WriteLong(data_size, offset_size, write_buffer_.data(),
                                 offset_start + size * offset_size);
    return Status::OK();
}

Status VariantBuilder::FinishWritingArray(int32_t start, const std::vector<int32_t>& offsets) {
    int32_t data_size = write_pos_ - start;
    auto size = static_cast<int32_t>(offsets.size());
    bool large_size = size > VariantDefs::kU8Max;
    int32_t size_bytes = large_size ? VariantDefs::kU32Size : 1;
    int32_t offset_size = GetIntegerSize(data_size);
    // The space for the header byte, array size, and offset list.
    int32_t header_size = 1 + size_bytes + (size + 1) * offset_size;
    PAIMON_RETURN_NOT_OK(CheckCapacity(header_size));
    // Shift the just-written element data to make room for the header section.
    memmove(write_buffer_.data() + start + header_size, write_buffer_.data() + start,
            static_cast<size_t>(data_size));
    write_pos_ += header_size;
    write_buffer_[start] = VariantBinaryUtil::ArrayHeader(large_size, offset_size);
    VariantBinaryUtil::WriteLong(size, size_bytes, write_buffer_.data(), start + 1);
    int32_t offset_start = start + 1 + size_bytes;
    for (int32_t i = 0; i < size; ++i) {
        VariantBinaryUtil::WriteLong(offsets[i], offset_size, write_buffer_.data(),
                                     offset_start + i * offset_size);
    }
    VariantBinaryUtil::WriteLong(data_size, offset_size, write_buffer_.data(),
                                 offset_start + size * offset_size);
    return Status::OK();
}

Status VariantBuilder::AppendVariant(const GenericVariant& v) {
    return AppendVariantImpl(v.RawValue(), v.Metadata(), v.Pos());
}

Status VariantBuilder::AppendVariantImpl(std::string_view value, std::string_view metadata,
                                         int32_t pos) {
    PAIMON_RETURN_NOT_OK(VariantBinaryUtil::CheckIndex(pos, static_cast<int32_t>(value.size())));
    int32_t basic_type = static_cast<uint8_t>(value[pos]) & VariantDefs::kBasicTypeMask;
    switch (basic_type) {
        case VariantDefs::kObject: {
            PAIMON_ASSIGN_OR_RAISE(VariantBinaryUtil::ObjectInfo info,
                                   VariantBinaryUtil::GetObjectInfo(value, pos));
            std::vector<FieldEntry> fields;
            fields.reserve(info.num_elements);
            int32_t start = write_pos_;
            for (int32_t i = 0; i < info.num_elements; ++i) {
                PAIMON_ASSIGN_OR_RAISE(int32_t id,
                                       VariantBinaryUtil::ReadUnsigned(
                                           value, info.id_start + info.id_size * i, info.id_size));
                PAIMON_ASSIGN_OR_RAISE(
                    int32_t offset,
                    VariantBinaryUtil::ReadUnsigned(value, info.offset_start + info.offset_size * i,
                                                    info.offset_size));
                int32_t element_pos = info.data_start + offset;
                PAIMON_ASSIGN_OR_RAISE(std::string_view key,
                                       VariantBinaryUtil::GetMetadataKey(metadata, id));
                int32_t new_id = AddKey(key);
                fields.emplace_back(std::string(key), new_id, write_pos_ - start);
                PAIMON_RETURN_NOT_OK(AppendVariantImpl(value, metadata, element_pos));
            }
            return FinishWritingObject(start, &fields);
        }
        case VariantDefs::kArray: {
            PAIMON_ASSIGN_OR_RAISE(VariantBinaryUtil::ArrayInfo info,
                                   VariantBinaryUtil::GetArrayInfo(value, pos));
            std::vector<int32_t> offsets;
            offsets.reserve(info.num_elements);
            int32_t start = write_pos_;
            for (int32_t i = 0; i < info.num_elements; ++i) {
                PAIMON_ASSIGN_OR_RAISE(
                    int32_t offset,
                    VariantBinaryUtil::ReadUnsigned(value, info.offset_start + info.offset_size * i,
                                                    info.offset_size));
                int32_t element_pos = info.data_start + offset;
                offsets.push_back(write_pos_ - start);
                PAIMON_RETURN_NOT_OK(AppendVariantImpl(value, metadata, element_pos));
            }
            return FinishWritingArray(start, offsets);
        }
        default:
            return ShallowAppendVariant(value, pos);
    }
}

Status VariantBuilder::ShallowAppendVariant(std::string_view value, int32_t pos) {
    PAIMON_ASSIGN_OR_RAISE(int32_t size, VariantBinaryUtil::ValueSize(value, pos));
    PAIMON_RETURN_NOT_OK(
        VariantBinaryUtil::CheckIndex(pos + size - 1, static_cast<int32_t>(value.size())));
    PAIMON_RETURN_NOT_OK(CheckCapacity(size));
    memcpy(write_buffer_.data() + write_pos_, value.data() + pos, static_cast<size_t>(size));
    write_pos_ += size;
    return Status::OK();
}

Status VariantBuilder::CheckCapacity(int32_t additional) {
    int32_t required = write_pos_ + additional;
    if (required > static_cast<int32_t>(write_buffer_.size())) {
        // Allocate a new buffer with a capacity of the next power of 2 of `required`.
        auto new_capacity = static_cast<int32_t>(write_buffer_.size());
        while (new_capacity < required) {
            new_capacity *= 2;
            if (new_capacity > VariantDefs::kSizeLimit) {
                return Status::Invalid("VARIANT_SIZE_LIMIT");
            }
        }
        write_buffer_.resize(static_cast<size_t>(new_capacity));
    }
    return Status::OK();
}

int32_t VariantBuilder::GetIntegerSize(int32_t value) {
    if (value <= VariantDefs::kU8Max) {
        return 1;
    }
    if (value <= VariantDefs::kU16Max) {
        return 2;
    }
    if (value <= VariantDefs::kU24Max) {
        return 3;
    }
    return 4;
}

}  // namespace paimon
