#ifndef THIRD_PARTY_UPB_WASM_ABI_TEST_UPB_COMPONENT_OPERATORS_H_
#define THIRD_PARTY_UPB_WASM_ABI_TEST_UPB_COMPONENT_OPERATORS_H_

#include <cstddef>
#include <tuple>
#include <utility>
#include <variant>

#include "third_party/absl/base/nullability.h"
#include "third_party/absl/base/optimization.h"
#include "third_party/absl/log/absl_log.h"
#include "third_party/absl/status/status.h"
#include "wasm_abi/component.h"
#include "wasm_abi/status_util.h"

namespace upb::wasm {

/**
 * The following classes are used to fuzz test UPB Component methods.
 *
 * UpbOperation defines the generic shape of a Component method invocation, with
 * a template specialization for each method in upb::wasm::Component. `fn`
 * performs the operation using the parameters stored in the associated `args`.
 *
 * The fuzz test (component_fuzz_test.cc) generates a vector of variants of
 * these classes. For each class in the generated vector, the `func` method is
 * called.
 */

template <auto f, typename Result, typename... Args>
struct UpbOperation;

template <auto f, typename Result>
struct UpbOperation<f, Result (upb::wasm::Component::*)(
                           MaybeErrorStatus* absl_nonnull)> {
  Result fn(upb::wasm::Component* component) const {
    MaybeErrorStatus status = nullptr;
    Result result = std::invoke(f, component, &status);
    if (ABSL_PREDICT_FALSE(status != nullptr)) {
      ABSL_LOG(ERROR) << status->message();
      delete status;
      return Result();
    }
    return result;
  }
};

// Overload for function with args.
template <auto f, typename Result, typename... Args>
struct UpbOperation<f, Result (upb::wasm::Component::*)(
                           MaybeErrorStatus* absl_nonnull, Args...)> {
  Result fn(upb::wasm::Component* component) const {
    return fn_helper(component, std::make_index_sequence<sizeof...(Args)>());
  }
  template <size_t... Is>
  Result fn_helper(upb::wasm::Component* component,
                   std::integer_sequence<size_t, Is...>) const {
    MaybeErrorStatus status = nullptr;
    Result result = std::invoke(f, component, &status, std::get<Is>(args)...);
    if (ABSL_PREDICT_FALSE(status != nullptr)) {
      ABSL_LOG(ERROR) << status->message();
      delete status;
      return Result();
    }
    return result;
  }

  std::tuple<Args...> args;
};

// Overload for void return.
template <auto f, typename... Args>
struct UpbOperation<f, void (upb::wasm::Component::*)(
                           MaybeErrorStatus* absl_nonnull, Args...)> {
  void fn(upb::wasm::Component* component) const {
    return fn_helper(component, std::make_index_sequence<sizeof...(Args)>());
  }
  template <size_t... Is>
  void fn_helper(upb::wasm::Component* component,
                 std::integer_sequence<size_t, Is...>) const {
    MaybeErrorStatus status = nullptr;
    std::invoke(f, component, &status, std::get<Is>(args)...);
    if (ABSL_PREDICT_FALSE(status != nullptr)) {
      ABSL_LOG(ERROR) << status->message();
      delete status;
    }
  }

  std::tuple<Args...> args;
};

#define DECLARE_UPB_OPERATION(NAME)                      \
  using NAME = UpbOperation<&upb::wasm::Component::NAME, \
                            decltype(&upb::wasm::Component::NAME)>;

DECLARE_UPB_OPERATION(StringView_Constructor)
DECLARE_UPB_OPERATION(StringView_FromFloat)
DECLARE_UPB_OPERATION(StringView_FromDouble)
DECLARE_UPB_OPERATION(StringView_FromInt32)
DECLARE_UPB_OPERATION(StringView_FromUInt32)
DECLARE_UPB_OPERATION(StringView_FromInt64)
DECLARE_UPB_OPERATION(StringView_FromUInt64)
DECLARE_UPB_OPERATION(StringView_Size)
DECLARE_UPB_OPERATION(StringView_Alias)
DECLARE_UPB_OPERATION(StringView_IsEqual)
DECLARE_UPB_OPERATION(StringView_Compare)
DECLARE_UPB_OPERATION(Message_Constructor)
DECLARE_UPB_OPERATION(Message_DeepCopy)
DECLARE_UPB_OPERATION(Message_ShallowCopy)
DECLARE_UPB_OPERATION(Message_GetInt32)
DECLARE_UPB_OPERATION(Message_SetInt32)
DECLARE_UPB_OPERATION(Message_GetUInt32)
DECLARE_UPB_OPERATION(Message_SetUInt32)
DECLARE_UPB_OPERATION(Message_GetInt64)
DECLARE_UPB_OPERATION(Message_SetInt64)
DECLARE_UPB_OPERATION(Message_GetUInt64)
DECLARE_UPB_OPERATION(Message_SetUInt64)
DECLARE_UPB_OPERATION(Message_HasBaseField)
DECLARE_UPB_OPERATION(Message_HasExtension)
DECLARE_UPB_OPERATION(Message_GetArray)
DECLARE_UPB_OPERATION(Message_SetNullableArray)
DECLARE_UPB_OPERATION(Message_GetBool)
DECLARE_UPB_OPERATION(Message_SetBool)
DECLARE_UPB_OPERATION(Message_GetFloat)
DECLARE_UPB_OPERATION(Message_SetFloat)
DECLARE_UPB_OPERATION(Message_GetDouble)
DECLARE_UPB_OPERATION(Message_SetDouble)
DECLARE_UPB_OPERATION(Message_GetString)
DECLARE_UPB_OPERATION(Message_SetString)
DECLARE_UPB_OPERATION(Message_GetMessage)
DECLARE_UPB_OPERATION(Message_GetMessageOrEmpty)
DECLARE_UPB_OPERATION(Message_SetNullableMessage)
DECLARE_UPB_OPERATION(Message_GetOrCreateMutableMessage)
DECLARE_UPB_OPERATION(Message_GetMap)
DECLARE_UPB_OPERATION(Message_SetNullableMap)
DECLARE_UPB_OPERATION(Message_ClearBaseField)
DECLARE_UPB_OPERATION(Message_GetBoolExtension)
DECLARE_UPB_OPERATION(Message_GetDoubleExtension)
DECLARE_UPB_OPERATION(Message_GetFloatExtension)
DECLARE_UPB_OPERATION(Message_GetInt32Extension)
DECLARE_UPB_OPERATION(Message_GetInt64Extension)
DECLARE_UPB_OPERATION(Message_GetUInt32Extension)
DECLARE_UPB_OPERATION(Message_GetUInt64Extension)
DECLARE_UPB_OPERATION(Message_GetStringExtension)
DECLARE_UPB_OPERATION(Message_GetMessageExtension)
DECLARE_UPB_OPERATION(Message_GetMessageExtensionOrEmpty)
DECLARE_UPB_OPERATION(Message_GetArrayExtension)
DECLARE_UPB_OPERATION(Message_GetMutableArrayExtension)
DECLARE_UPB_OPERATION(Message_SetBoolExtension)
DECLARE_UPB_OPERATION(Message_SetDoubleExtension)
DECLARE_UPB_OPERATION(Message_SetFloatExtension)
DECLARE_UPB_OPERATION(Message_SetInt32Extension)
DECLARE_UPB_OPERATION(Message_SetUInt32Extension)
DECLARE_UPB_OPERATION(Message_SetInt64Extension)
DECLARE_UPB_OPERATION(Message_SetUInt64Extension)
DECLARE_UPB_OPERATION(Message_SetStringExtension)
DECLARE_UPB_OPERATION(Message_SetNullableMessageExtension)
DECLARE_UPB_OPERATION(Message_SetNullableArrayExtension)
DECLARE_UPB_OPERATION(Message_ClearExtension)
DECLARE_UPB_OPERATION(Array_Constructor)
DECLARE_UPB_OPERATION(Array_ConstructorForMessages)
DECLARE_UPB_OPERATION(Array_Size)
DECLARE_UPB_OPERATION(Array_GetBool)
DECLARE_UPB_OPERATION(Array_GetInt32)
DECLARE_UPB_OPERATION(Array_GetUInt32)
DECLARE_UPB_OPERATION(Array_GetFloat)
DECLARE_UPB_OPERATION(Array_GetMessage)
DECLARE_UPB_OPERATION(Array_GetString)
DECLARE_UPB_OPERATION(Array_AppendBool)
DECLARE_UPB_OPERATION(Array_AppendInt32)
DECLARE_UPB_OPERATION(Array_AppendUInt32)
DECLARE_UPB_OPERATION(Array_AppendFloat)
DECLARE_UPB_OPERATION(Array_AppendMessage)
DECLARE_UPB_OPERATION(Array_AppendString)
DECLARE_UPB_OPERATION(Array_Append)
DECLARE_UPB_OPERATION(MapBool_Contains)
DECLARE_UPB_OPERATION(MapBool_GetInt32)
DECLARE_UPB_OPERATION(MapBool_GetUInt32)
DECLARE_UPB_OPERATION(MapBool_GetInt64)
DECLARE_UPB_OPERATION(MapBool_GetUInt64)
DECLARE_UPB_OPERATION(MapBool_GetFloat)
DECLARE_UPB_OPERATION(MapBool_GetDouble)
DECLARE_UPB_OPERATION(MapBool_GetBool)
DECLARE_UPB_OPERATION(MapBool_GetMessage)
DECLARE_UPB_OPERATION(MapBool_GetString)
DECLARE_UPB_OPERATION(MapInt32_Contains)
DECLARE_UPB_OPERATION(MapInt32_GetBool)
DECLARE_UPB_OPERATION(MapInt32_GetDouble)
DECLARE_UPB_OPERATION(MapInt32_GetFloat)
DECLARE_UPB_OPERATION(MapInt32_GetInt32)
DECLARE_UPB_OPERATION(MapInt32_GetInt64)
DECLARE_UPB_OPERATION(MapInt32_GetMessage)
DECLARE_UPB_OPERATION(MapInt32_GetString)
DECLARE_UPB_OPERATION(MapInt32_GetUInt32)
DECLARE_UPB_OPERATION(MapInt32_GetUInt64)
DECLARE_UPB_OPERATION(MapInt64_Contains)
DECLARE_UPB_OPERATION(MapInt64_GetBool)
DECLARE_UPB_OPERATION(MapInt64_GetDouble)
DECLARE_UPB_OPERATION(MapInt64_GetFloat)
DECLARE_UPB_OPERATION(MapInt64_GetInt32)
DECLARE_UPB_OPERATION(MapInt64_GetInt64)
DECLARE_UPB_OPERATION(MapInt64_GetMessage)
DECLARE_UPB_OPERATION(MapInt64_GetString)
DECLARE_UPB_OPERATION(MapInt64_GetUInt32)
DECLARE_UPB_OPERATION(MapInt64_GetUInt64)
DECLARE_UPB_OPERATION(MapString_Contains)
DECLARE_UPB_OPERATION(MapString_GetInt32)
DECLARE_UPB_OPERATION(MapString_GetUInt32)
DECLARE_UPB_OPERATION(MapString_GetInt64)
DECLARE_UPB_OPERATION(MapString_GetUInt64)
DECLARE_UPB_OPERATION(MapString_GetFloat)
DECLARE_UPB_OPERATION(MapString_GetDouble)
DECLARE_UPB_OPERATION(MapString_GetBool)
DECLARE_UPB_OPERATION(MapString_GetMessage)
DECLARE_UPB_OPERATION(MapString_GetString)
DECLARE_UPB_OPERATION(MapUInt32_Contains)
DECLARE_UPB_OPERATION(MapUInt32_GetBool)
DECLARE_UPB_OPERATION(MapUInt32_GetDouble)
DECLARE_UPB_OPERATION(MapUInt32_GetFloat)
DECLARE_UPB_OPERATION(MapUInt32_GetInt32)
DECLARE_UPB_OPERATION(MapUInt32_GetInt64)
DECLARE_UPB_OPERATION(MapUInt32_GetMessage)
DECLARE_UPB_OPERATION(MapUInt32_GetString)
DECLARE_UPB_OPERATION(MapUInt32_GetUInt32)
DECLARE_UPB_OPERATION(MapUInt32_GetUInt64)
DECLARE_UPB_OPERATION(MapUInt64_Contains)
DECLARE_UPB_OPERATION(MapUInt64_GetBool)
DECLARE_UPB_OPERATION(MapUInt64_GetDouble)
DECLARE_UPB_OPERATION(MapUInt64_GetFloat)
DECLARE_UPB_OPERATION(MapUInt64_GetInt32)
DECLARE_UPB_OPERATION(MapUInt64_GetInt64)
DECLARE_UPB_OPERATION(MapUInt64_GetMessage)
DECLARE_UPB_OPERATION(MapUInt64_GetString)
DECLARE_UPB_OPERATION(MapUInt64_GetUInt32)
DECLARE_UPB_OPERATION(MapUInt64_GetUInt64)
DECLARE_UPB_OPERATION(Map_New)
DECLARE_UPB_OPERATION(Map_NewForMessages)
DECLARE_UPB_OPERATION(MapInt32_InsertBool)
DECLARE_UPB_OPERATION(MapInt32_InsertInt32)
DECLARE_UPB_OPERATION(MapInt32_InsertUInt32)
DECLARE_UPB_OPERATION(MapInt32_InsertFloat)
DECLARE_UPB_OPERATION(MapInt32_InsertMessage)
DECLARE_UPB_OPERATION(MapInt32_InsertString)
DECLARE_UPB_OPERATION(MapInt32_InsertInt64)
DECLARE_UPB_OPERATION(MapInt32_InsertUInt64)
DECLARE_UPB_OPERATION(MapInt32_InsertDouble)
DECLARE_UPB_OPERATION(MapUInt32_InsertBool)
DECLARE_UPB_OPERATION(MapUInt32_InsertInt32)
DECLARE_UPB_OPERATION(MapUInt32_InsertUInt32)
DECLARE_UPB_OPERATION(MapUInt32_InsertFloat)
DECLARE_UPB_OPERATION(MapUInt32_InsertMessage)
DECLARE_UPB_OPERATION(MapUInt32_InsertString)
DECLARE_UPB_OPERATION(MapUInt32_InsertInt64)
DECLARE_UPB_OPERATION(MapUInt32_InsertUInt64)
DECLARE_UPB_OPERATION(MapUInt32_InsertDouble)
DECLARE_UPB_OPERATION(MapInt64_InsertBool)
DECLARE_UPB_OPERATION(MapInt64_InsertInt32)
DECLARE_UPB_OPERATION(MapInt64_InsertUInt32)
DECLARE_UPB_OPERATION(MapInt64_InsertFloat)
DECLARE_UPB_OPERATION(MapInt64_InsertMessage)
DECLARE_UPB_OPERATION(MapInt64_InsertString)
DECLARE_UPB_OPERATION(MapInt64_InsertInt64)
DECLARE_UPB_OPERATION(MapInt64_InsertUInt64)
DECLARE_UPB_OPERATION(MapInt64_InsertDouble)
DECLARE_UPB_OPERATION(MapUInt64_InsertBool)
DECLARE_UPB_OPERATION(MapUInt64_InsertInt32)
DECLARE_UPB_OPERATION(MapUInt64_InsertUInt32)
DECLARE_UPB_OPERATION(MapUInt64_InsertFloat)
DECLARE_UPB_OPERATION(MapUInt64_InsertMessage)
DECLARE_UPB_OPERATION(MapUInt64_InsertString)
DECLARE_UPB_OPERATION(MapUInt64_InsertInt64)
DECLARE_UPB_OPERATION(MapUInt64_InsertUInt64)
DECLARE_UPB_OPERATION(MapUInt64_InsertDouble)
DECLARE_UPB_OPERATION(MapString_InsertBool)
DECLARE_UPB_OPERATION(MapString_InsertInt32)
DECLARE_UPB_OPERATION(MapString_InsertUInt32)
DECLARE_UPB_OPERATION(MapString_InsertFloat)
DECLARE_UPB_OPERATION(MapString_InsertMessage)
DECLARE_UPB_OPERATION(MapString_InsertString)
DECLARE_UPB_OPERATION(MapString_InsertInt64)
DECLARE_UPB_OPERATION(MapString_InsertUInt64)
DECLARE_UPB_OPERATION(MapString_InsertDouble)
DECLARE_UPB_OPERATION(Map_Size)
DECLARE_UPB_OPERATION(Decode)
DECLARE_UPB_OPERATION(Encode)
DECLARE_UPB_OPERATION(ByteSize)

using AnyUpbOperation = std::variant<
    StringView_Constructor, StringView_FromFloat, StringView_FromDouble,
    StringView_FromInt32, StringView_FromUInt32, StringView_Size,
    StringView_Alias, StringView_IsEqual, StringView_Compare,
    Message_Constructor, Message_DeepCopy, Message_ShallowCopy,
    Message_GetInt32, Message_SetInt32, Message_GetUInt32, Message_SetUInt32,
    Message_GetInt64, Message_SetInt64, Message_GetUInt64, Message_SetUInt64,
    Message_HasBaseField, Message_HasExtension, Message_GetArray,
    Message_SetNullableArray, Message_GetBool, Message_SetBool,
    Message_GetFloat, Message_SetFloat, Message_GetDouble, Message_SetDouble,
    Message_GetString, Message_SetString, Message_GetMessage,
    Message_GetMessageOrEmpty, Message_SetNullableMessage,
    Message_GetOrCreateMutableMessage, Message_GetMap, Message_SetNullableMap,
    Message_ClearBaseField, Message_GetBoolExtension,
    Message_GetDoubleExtension, Message_GetFloatExtension,
    Message_GetInt32Extension, Message_GetInt64Extension,
    Message_GetUInt32Extension, Message_GetUInt64Extension,
    Message_GetStringExtension, Message_GetMessageExtension,
    Message_GetMessageExtensionOrEmpty, Message_GetArrayExtension,
    Message_GetMutableArrayExtension, Message_SetBoolExtension,
    Message_SetDoubleExtension, Message_SetFloatExtension,
    Message_SetInt32Extension, Message_SetUInt32Extension,
    Message_SetInt64Extension, Message_SetUInt64Extension,
    Message_SetStringExtension, Message_SetNullableMessageExtension,
    Message_SetNullableArrayExtension, Message_ClearExtension,
    Array_Constructor, Array_ConstructorForMessages, Array_Size, Array_GetBool,
    Array_GetInt32, Array_GetUInt32, Array_GetFloat, Array_GetMessage,
    Array_GetString, Array_AppendBool, Array_AppendInt32, Array_AppendUInt32,
    Array_AppendFloat, Array_AppendMessage, Array_AppendString, Array_Append,
    MapBool_Contains, MapBool_GetInt32, MapBool_GetUInt32, MapBool_GetInt64,
    MapBool_GetUInt64, MapBool_GetFloat, MapBool_GetDouble, MapBool_GetBool,
    MapBool_GetMessage, MapBool_GetString, MapInt32_Contains, MapInt32_GetBool,
    MapInt32_GetDouble, MapInt32_GetFloat, MapInt32_GetInt32, MapInt32_GetInt64,
    MapInt32_GetMessage, MapInt32_GetString, MapInt32_GetUInt32,
    MapInt32_GetUInt64, MapInt64_Contains, MapInt64_GetBool, MapInt64_GetDouble,
    MapInt64_GetFloat, MapInt64_GetInt32, MapInt64_GetInt64,
    MapInt64_GetMessage, MapInt64_GetString, MapInt64_GetUInt32,
    MapInt64_GetUInt64, MapString_Contains, MapString_GetInt32,
    MapString_GetUInt32, MapString_GetInt64, MapString_GetUInt64,
    MapString_GetFloat, MapString_GetDouble, MapString_GetBool,
    MapString_GetMessage, MapString_GetString, MapUInt32_Contains,
    MapUInt32_GetBool, MapUInt32_GetDouble, MapUInt32_GetFloat,
    MapUInt32_GetInt32, MapUInt32_GetInt64, MapUInt32_GetMessage,
    MapUInt32_GetString, MapUInt32_GetUInt32, MapUInt32_GetUInt64,
    MapUInt64_Contains, MapUInt64_GetBool, MapUInt64_GetDouble,
    MapUInt64_GetFloat, MapUInt64_GetInt32, MapUInt64_GetInt64,
    MapUInt64_GetMessage, MapUInt64_GetString, MapUInt64_GetUInt32,
    MapUInt64_GetUInt64, Map_New, Map_NewForMessages, MapInt32_InsertFloat,
    MapInt32_InsertMessage, MapInt32_InsertString, MapInt32_InsertInt64,
    MapString_InsertFloat, MapString_InsertMessage, MapString_InsertString,
    MapString_InsertInt64, Map_Size, Decode, Encode, ByteSize>;
// Note: adding more operations to this variant list might cause OOM when
// running with `--config=asan` (which seems to be used for UPB's TAP projects).

}  // namespace upb::wasm

#endif  // THIRD_PARTY_UPB_WASM_ABI_TEST_UPB_COMPONENT_OPERATORS_H_
