package top.r3944realms.lib39.util.storage.vauleio;

import com.mojang.serialization.Codec;
import net.minecraft.util.ProblemReporter;
import net.minecraft.world.item.component.ResolvableProfile;
import net.minecraft.world.level.storage.TagValueOutput;
import net.minecraft.world.level.storage.ValueOutput;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.Nullable;

import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * ValueOutput Helper类
 * 提供链式操作和类型安全的ValueOutput数据写入
 */
@SuppressWarnings({"unused", "OptionalUsedAsFieldOrParameterType", "UnusedReturnValue"})
public class ValueOutputReader {
    private final ValueOutput valueOutput;
    private ValueOutputReader() {
        this.valueOutput = TagValueOutput.createWithoutContext(ProblemReporter.DISCARDING);
    }
    private ValueOutputReader(ValueOutput valueOutput) {
        this.valueOutput = valueOutput;
    }

    /**
     * 创建一个新的NBT构建器
     *
     * @return the nbt writer
     */
    @Contract(value = " -> new", pure = true)
    public static @NotNull ValueOutputReader builder() {
        return new ValueOutputReader();
    }

    /**
     * 从ValueOutput创建写入器
     *
     * @param valueOutput the value output
     * @return the value output writer
     */
    @NotNull
    public static ValueOutputReader of(@NotNull ValueOutput valueOutput) {
        return new ValueOutputReader(valueOutput);
    }

    /**
     * String value output writer.
     *
     * @param key   the key
     * @param value the value
     * @return the value output writer
     */
// 基本写入方法
    public ValueOutputReader string(String key, String value) {
        if (value != null) {
            valueOutput.putString(key, value);
        }
        return this;
    }

    /**
     * String value output writer.
     *
     * @param key          the key
     * @param value        the value
     * @param defaultValue the default value
     * @return the value output writer
     */
    public ValueOutputReader string(String key, @Nullable String value, String defaultValue) {
        valueOutput.putString(key, value != null ? value : defaultValue);
        return this;
    }

    /**
     * String value output writer.
     *
     * @param key   the key
     * @param value the value
     * @return the value output writer
     */
    public ValueOutputReader string(String key, @NonNull Optional<String> value) {
        value.ifPresent(v -> valueOutput.putString(key, v));
        return this;
    }

    /**
     * Boolean value value output writer.
     *
     * @param key   the key
     * @param value the value
     * @return the value output writer
     */
    public ValueOutputReader booleanValue(String key, boolean value) {
        valueOutput.putBoolean(key, value);
        return this;
    }

    /**
     * Boolean value value output writer.
     *
     * @param key   the key
     * @param value the value
     * @return the value output writer
     */
    public ValueOutputReader booleanValue(String key, @NonNull Optional<Boolean> value) {
        value.ifPresent(v -> valueOutput.putBoolean(key, v));
        return this;
    }

    /**
     * Byte value value output writer.
     *
     * @param key   the key
     * @param value the value
     * @return the value output writer
     */
    public ValueOutputReader byteValue(String key, byte value) {
        valueOutput.putByte(key, value);
        return this;
    }

    /**
     * Byte value value output writer.
     *
     * @param key   the key
     * @param value the value
     * @return the value output writer
     */
    public ValueOutputReader byteValue(String key, @NonNull Optional<Byte> value) {
        value.ifPresent(v -> valueOutput.putByte(key, v));
        return this;
    }

    /**
     * Short value value output writer.
     *
     * @param key   the key
     * @param value the value
     * @return the value output writer
     */
    public ValueOutputReader shortValue(String key, short value) {
        valueOutput.putShort(key, value);
        return this;
    }

    /**
     * Short value value output writer.
     *
     * @param key   the key
     * @param value the value
     * @return the value output writer
     */
    public ValueOutputReader shortValue(String key, @NonNull Optional<Short> value) {
        value.ifPresent(v -> valueOutput.putShort(key, v));
        return this;
    }

    /**
     * Int value value output writer.
     *
     * @param key   the key
     * @param value the value
     * @return the value output writer
     */
    public ValueOutputReader intValue(String key, int value) {
        valueOutput.putInt(key, value);
        return this;
    }

    /**
     * Int value value output writer.
     *
     * @param key   the key
     * @param value the value
     * @return the value output writer
     */
    public ValueOutputReader intValue(String key, @NonNull Optional<Integer> value) {
        value.ifPresent(v -> valueOutput.putInt(key, v));
        return this;
    }

    /**
     * Long value value output writer.
     *
     * @param key   the key
     * @param value the value
     * @return the value output writer
     */
    public ValueOutputReader longValue(String key, long value) {
        valueOutput.putLong(key, value);
        return this;
    }

    /**
     * Long value value output writer.
     *
     * @param key   the key
     * @param value the value
     * @return the value output writer
     */
    public ValueOutputReader longValue(String key, @NonNull Optional<Long> value) {
        value.ifPresent(v -> valueOutput.putLong(key, v));
        return this;
    }

    /**
     * Float value value output writer.
     *
     * @param key   the key
     * @param value the value
     * @return the value output writer
     */
    public ValueOutputReader floatValue(String key, float value) {
        valueOutput.putFloat(key, value);
        return this;
    }

    /**
     * Float value value output writer.
     *
     * @param key   the key
     * @param value the value
     * @return the value output writer
     */
    public ValueOutputReader floatValue(String key, @NonNull Optional<Float> value) {
        value.ifPresent(v -> valueOutput.putFloat(key, v));
        return this;
    }

    /**
     * Double value value output writer.
     *
     * @param key   the key
     * @param value the value
     * @return the value output writer
     */
    public ValueOutputReader doubleValue(String key, double value) {
        valueOutput.putDouble(key, value);
        return this;
    }

    /**
     * Double value value output writer.
     *
     * @param key   the key
     * @param value the value
     * @return the value output writer
     */
    public ValueOutputReader doubleValue(String key, @NonNull Optional<Double> value) {
        value.ifPresent(v -> valueOutput.putDouble(key, v));
        return this;
    }

    /**
     * Int array value output writer.
     *
     * @param key   the key
     * @param value the value
     * @return the value output writer
     */
    public ValueOutputReader intArray(String key, int[] value) {
        if (value != null && value.length > 0) {
            valueOutput.putIntArray(key, value);
        }
        return this;
    }

    /**
     * Int array value output writer.
     *
     * @param key   the key
     * @param value the value
     * @return the value output writer
     */
    public ValueOutputReader intArray(String key, @NonNull Optional<int[]> value) {
        value.ifPresent(v -> {
            if (v.length > 0) {
                valueOutput.putIntArray(key, v);
            }
        });
        return this;
    }

    /**
     * Codec value value output writer.
     *
     * @param <T>   the type parameter
     * @param key   the key
     * @param codec the codec
     * @param value the value
     * @return the value output writer
     */
    public <T> ValueOutputReader codecValue(String key, Codec<T> codec, T value) {
        if (value != null) {
            valueOutput.store(key, codec, value);
        }
        return this;
    }

    /**
     * Codec value value output writer.
     *
     * @param <T>   the type parameter
     * @param key   the key
     * @param codec the codec
     * @param value the value
     * @return the value output writer
     */
    public <T> ValueOutputReader codecValue(String key, Codec<T> codec, @NonNull Optional<T> value) {
        value.ifPresent(v -> valueOutput.store(key, codec, v));
        return this;
    }

    /**
     * Codec nullable value value output writer.
     *
     * @param <T>   the type parameter
     * @param key   the key
     * @param codec the codec
     * @param value the value
     * @return the value output writer
     */
    public <T> ValueOutputReader codecNullable(String key, Codec<T> codec, @Nullable T value) {
        valueOutput.storeNullable(key, codec, value);
        return this;
    }

    public ValueOutputReader gameProfile(String key, ResolvableProfile profile) {
        valueOutput.store(key, ResolvableProfile.CODEC, profile);
        return this;
    }

    public ValueOutputReader gameProfile(String key, @NonNull Optional<ResolvableProfile> profile) {
        if (profile.isPresent()) {
            valueOutput.store(key, ResolvableProfile.CODEC, profile.get());
        }
        return this;
    }
    /**
     * Vec 3 value output writer.
     *
     * @param key the key
     * @param vec the vec
     * @return the value output writer
     */
    public ValueOutputReader vec3(String key, Vec3 vec) {
        if (vec != null) {
            ValueOutput child = valueOutput.child(key);
            writeVec3(child, vec);
        }
        return this;
    }

    /**
     * Vec 3 value output writer.
     *
     * @param key the key
     * @param vec the vec
     * @return the value output writer
     */
    public ValueOutputReader vec3(String key, @NonNull Optional<Vec3> vec) {
        vec.ifPresent(v -> vec3(key, v));
        return this;
    }

    /**
     * Enum value value output writer.
     *
     * @param <T>   the type parameter
     * @param key   the key
     * @param value the value
     * @return the value output writer
     */
    public <T extends Enum<T>> ValueOutputReader enumValue(String key, T value) {
        if (value != null) {
            valueOutput.putString(key, value.name().toLowerCase());
        }
        return this;
    }

    /**
     * Enum value value output writer.
     *
     * @param <T>   the type parameter
     * @param key   the key
     * @param value the value
     * @return the value output writer
     */
    public <T extends Enum<T>> ValueOutputReader enumValue(String key, @NonNull Optional<T> value) {
        value.ifPresent(v -> valueOutput.putString(key, v.name().toLowerCase()));
        return this;
    }

    /**
     * Nested value output writer.
     *
     * @param key      the key
     * @param consumer the consumer
     * @return the value output writer
     */
    public ValueOutputReader nested(String key, @NonNull Consumer<ValueOutputReader> consumer) {
        ValueOutput child = valueOutput.child(key);
        consumer.accept(new ValueOutputReader(child));
        return this;
    }

    /**
     * Nested if present value output writer.
     *
     * @param key      the key
     * @param supplier the supplier
     * @param consumer the consumer
     * @return the value output writer
     */
    public ValueOutputReader nestedIfPresent(String key, @NonNull Supplier<Boolean> supplier, Consumer<ValueOutputReader> consumer) {
        if (supplier.get()) {
            ValueOutput child = valueOutput.child(key);
            consumer.accept(new ValueOutputReader(child));
        }
        return this;
    }

    /**
     * List value output writer.
     *
     * @param <T>          the type parameter
     * @param key          the key
     * @param elementCodec the element codec
     * @param elements     the elements
     * @param consumer     the consumer
     * @return the value output writer
     */
    public <T> ValueOutputReader list(String key, Codec<T> elementCodec, Iterable<T> elements, Consumer<TypedListWriter<T>> consumer) {
        ValueOutput.TypedOutputList<T> list = valueOutput.list(key, elementCodec);
        if (!list.isEmpty()) {
            TypedListWriter<T> writer = new TypedListWriter<>(list);
            consumer.accept(writer);
        }
        return this;
    }

    /**
     * List value output writer.
     *
     * @param <T>          the type parameter
     * @param key          the key
     * @param elementCodec the element codec
     * @param elements     the elements
     * @return the value output writer
     */
    public <T> ValueOutputReader list(String key, Codec<T> elementCodec, @NonNull Iterable<T> elements) {
        ValueOutput.TypedOutputList<T> list = valueOutput.list(key, elementCodec);
        for (T element : elements) {
            if (element != null) {
                list.add(element);
            }
        }
        return this;
    }

    /**
     * Children list value output writer.
     *
     * @param key      the key
     * @param consumer the consumer
     * @return the value output writer
     */
    public ValueOutputReader childrenList(String key, Consumer<ChildrenListWriter> consumer) {
        ValueOutput.ValueOutputList list = valueOutput.childrenList(key);
        if (!list.isEmpty()) {
            ChildrenListWriter writer = new ChildrenListWriter(list);
            consumer.accept(writer);
        }
        return this;
    }

    /**
     * If present value output writer.
     *
     * @param condition the condition
     * @param action    the action
     * @return the value output writer
     */
    public ValueOutputReader ifPresent(boolean condition, Consumer<ValueOutputReader> action) {
        if (condition) {
            action.accept(this);
        }
        return this;
    }

    /**
     * If present value output writer.
     *
     * @param condition the condition
     * @param action    the action
     * @return the value output writer
     */
    public ValueOutputReader ifPresent(@NonNull Supplier<Boolean> condition, Consumer<ValueOutputReader> action) {
        if (condition.get()) {
            action.accept(this);
        }
        return this;
    }

    /**
     * Discard value output writer.
     *
     * @param key the key
     * @return the value output writer
     */
    public ValueOutputReader discard(String key) {
        valueOutput.discard(key);
        return this;
    }

    /**
     * Gets raw.
     *
     * @return the raw
     */
    @NotNull
    public ValueOutput getRaw() {
        return valueOutput;
    }

    /**
     * Write vec 3.
     *
     * @param valueOutput the value output
     * @param vec         the vec
     */
    public static void writeVec3(@NotNull ValueOutput valueOutput, @NotNull Vec3 vec) {
        valueOutput.putDouble("X", vec.x);
        valueOutput.putDouble("Y", vec.y);
        valueOutput.putDouble("Z", vec.z);
    }

    /**
     * Write vec 3 safe.
     *
     * @param valueOutput the value output
     * @param vec         the vec
     */
    public static void writeVec3Safe(@NotNull ValueOutput valueOutput, @Nullable Vec3 vec) {
        if (vec != null) {
            writeVec3(valueOutput, vec);
        }
    }

    /**
     * The type Typed list writer.
     *
     * @param <T> the type parameter
     */
    public static class TypedListWriter<T> {
        private final ValueOutput.TypedOutputList<T> list;

        private TypedListWriter(ValueOutput.TypedOutputList<T> list) {
            this.list = list;
        }

        /**
         * Add typed list writer.
         *
         * @param element the element
         * @return the typed list writer
         */
        public TypedListWriter<T> add(T element) {
            if (element != null) {
                list.add(element);
            }
            return this;
        }

        /**
         * Add all typed list writer.
         *
         * @param elements the elements
         * @return the typed list writer
         */
        public TypedListWriter<T> addAll(@NonNull Iterable<T> elements) {
            for (T element : elements) {
                if (element != null) {
                    list.add(element);
                }
            }
            return this;
        }

        /**
         * Is empty boolean.
         *
         * @return the boolean
         */
        public boolean isEmpty() {
            return list.isEmpty();
        }
    }

    /**
     * The type Children list writer.
     */
    public static class ChildrenListWriter {
        private final ValueOutput.ValueOutputList list;

        private ChildrenListWriter(ValueOutput.ValueOutputList list) {
            this.list = list;
        }

        /**
         * Add child value output writer.
         *
         * @param consumer the consumer
         * @return the children list writer
         */
        public ChildrenListWriter addChild(@NonNull Consumer<ValueOutputReader> consumer) {
            ValueOutput child = list.addChild();
            consumer.accept(new ValueOutputReader(child));
            return this;
        }

        /**
         * Discard last children list writer.
         *
         * @return the children list writer
         */
        public ChildrenListWriter discardLast() {
            list.discardLast();
            return this;
        }

        /**
         * Is empty boolean.
         *
         * @return the boolean
         */
        public boolean isEmpty() {
            return list.isEmpty();
        }
    }
}
