content: package org.allaymc.api.pdc;
  
  import com.google.common.base.Preconditions;
  import lombok.Getter;
  import lombok.RequiredArgsConstructor;
  
  import java.util.function.BiFunction;
  import java.util.function.BiPredicate;
  
  /**
   * @author IWareQ | Bukkit
   */
  public interface PersistentDataTypeRegistry {
      /**
       * Creates a tag adapter that maps a primitive type to a tag representation with additional type matching validation.
       *
       * @param <T>           the type of the primitive value
       * @param <Z>           the type of the tag representation
       * @param primitiveType the class type of the primitive value
       * @param nbtBaseType   the class type of the tag representation
       * @param builder       a function that converts a primitive value to its tag representation
       * @param extractor     a function that converts a tag representation back to the primitive value
       * @param matcher       a function that validates whether a tag value matches the expected type
       *
       * @return a tag adapter that can perform conversions between the primitive type and tag representation
       * with additional validation through the matcher function
       *
       * @throws IllegalArgumentException if no suitable tag type adapter is found for the specified types or if the matcher fails
       */
      <T, Z> TagAdapter<T, Z> createAdapter(
              Class<T> primitiveType, Class<Z> nbtBaseType,
              BiFunction<PersistentDataType<T, ?>, T, Z> builder,
              BiFunction<PersistentDataType<T, ?>, Z, T> extractor,
              BiPredicate<PersistentDataType<T, ?>, Object> matcher
      );
  
      /**
       * Wraps the given primitive value into a tag instance.
       *
       * @param type  the persistent data type of the value
       * @param value the value to be stored
       * @param <T>   the generic type of the value
       *
       * @return the wrapped tag instance
       *
       * @throws IllegalArgumentException if no suitable tag type adapter is found for this type
       */
      <T> Object wrap(PersistentDataType<T, ?> type, final T value);
  
      /**
       * Checks if the given tag value instance matches the specified primitive type.
       *
       * @param type  the persistent data type of the value
       * @param value the value instance to check
       * @param <T>   the generic type of the persistent data type
       *
       * @return {@code true} if the tag value is an instance of the primitive type; {@code false} otherwise
       *
       * @throws IllegalArgumentException if no suitable tag type adapter is found for this type
       */
      <T> boolean isInstanceOf(PersistentDataType<T, ?> type, Object value);
  
      /**
       * Retrieves the adapter associated with the specified persistent data type.
       *
       * @param type the persistent data type
       * @param <T>  the generic type of the primitive value
       * @param <Z>  the generic type of the tag representation
       *
       * @return the corresponding tag adapter instance
       *
       * @throws IllegalArgumentException if no adapter is found for the specified persistent data type
       */
      <T, Z> TagAdapter<T, Z> getAdapter(PersistentDataType<T, ?> type);
  
      /**
       * Extracts the stored primitive value from the given tag representation.
       *
       * @param type  the persistent data type
       * @param value the tag representation containing the stored value
       * @param <T>   the generic type of the extracted value
       * @param <Z>   the generic type of the tag representation
       *
       * @return the extracted primitive value
       *
       * @throws IllegalArgumentException if the tag representation doesn't match the expected type or if no suitable adapter is found
       */
      <T, Z> T extract(PersistentDataType<T, ?> type, Z value);
  
  
      /**
       * A tag adapter maps a primitive type to a specific tag representation used for persistence.
       *
       * @param <P> the primitive type expected by the persistent data type
       * @param <T> the tag representation type
       */
      @Getter
      @RequiredArgsConstructor
      class TagAdapter<P, T> {
          private final Class<P> primitiveType;
          private final Class<T> nbtBaseType;
          private final BiFunction<PersistentDataType<P, ?>, P, T> builder;
          private final BiFunction<PersistentDataType<P, ?>, T, P> extractor;
          private final BiPredicate<PersistentDataType<P, ?>, Object> matcher;
  
          /**
           * Extracts the primitive value from the provided tag representation.
           *
           * @param dataType the persistent data type
           * @param value    the tag representation
           *
           * @return the extracted primitive value
           *
           * @throws IllegalArgumentException if the provided tag representation is invalid
           */
          public P extract(PersistentDataType<P, ?> dataType, T value) {
              Preconditions.checkArgument(this.isInstance(dataType, value), "Invalid type: " + value.getClass().getSimpleName());
              return extractor.apply(dataType, this.nbtBaseType.cast(value));
          }
  
          /**
           * Builds a tag representation from the provided primitive value.
           *
           * @param dataType the persistent data type
           * @param value    the primitive value to be wrapped
           *
           * @return the constructed tag representation
           *
           * @throws IllegalArgumentException if the provided value is of an invalid type
           */
          public Object build(PersistentDataType<P, ?> dataType, final P value) {
              Preconditions.checkArgument(this.primitiveType.isInstance(value), "Invalid value type: " + value.getClass().getSimpleName());
              return builder.apply(dataType, this.primitiveType.cast(value));
          }
  
          /**
           * Determines whether the provided tag representation is an instance of the expected type.
           *
           * @param dataType the persistent data type
           * @param value    the tag representation to check
           *
           * @return {@code true} if the tag representation matches the expected type; {@code false} otherwise
           */
          public boolean isInstance(PersistentDataType<P, ?> dataType, Object value) {
              return this.matcher.test(dataType, value);
          }
      }
  }
  