package ru.dbotthepony.mc.otm.matter

import com.mojang.datafixers.util.Either
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.resources.ResourceLocation
import net.minecraft.tags.TagKey
import net.minecraft.world.item.Item
import ru.dbotthepony.mc.otm.core.math.Decimal
import ru.dbotthepony.mc.otm.data.DecimalCodec
import java.util.Optional

class InsertAction(
	id: Either<ResourceLocation, TagKey<Item>>,
	val matter: Decimal,
	val complexity: Double,
	val replaceIfExists: Boolean = false,
	errorOnFailure: Boolean = false,
	priority: Optional<Int> = Optional.empty(),
) : AbstractRegistryAction(id, errorOnFailure, priority) {
	override fun doUpdate(registry: MutableCollection<IMutableRegistryEntry>, source: ResourceLocation) {
		if (replaceIfExists) {
			// search & replace in parallel, if possible
			val replaced = registry.stream().anyMatch {
				if (it.name == id) {
					if (priority.isEmpty || it.priority < priority.get()) {
						it.matter = matter
						it.complexity = complexity
						it.priority = priority.orElse(0)
					}

					return@anyMatch true
				} else {
					return@anyMatch false
				}
			}

			if (!replaced) {
				registry.add(MutableRegistryEntry(id, matter, complexity, priority.orElse(0)))
			}
		} else {
			if (registry.stream().anyMatch { it.name == id }) {
				fail { "Value with name $id already exists (source: $source)" }
				return
			}

			registry.add(MutableRegistryEntry(id, matter, complexity, priority.orElse(0)))
		}
	}

	override val type: Type<*>
		get() = Companion

	companion object : Type<InsertAction> {
		override val codec: Codec<InsertAction> by lazy {
			RecordCodecBuilder.create {
				it.group(
					TargetCodec.fieldOf("id").forGetter(InsertAction::id),
					DecimalCodec.fieldOf("matter").forGetter(InsertAction::matter),
					Codec.doubleRange(0.0, Double.MAX_VALUE).fieldOf("complexity").forGetter(InsertAction::complexity),
					Codec.BOOL.optionalFieldOf("replace", false).forGetter(InsertAction::replaceIfExists),
					Codec.BOOL.optionalFieldOf("errorOnFailure", false).forGetter(InsertAction::errorOnFailure),
					Codec.INT.optionalFieldOf("priority").forGetter(InsertAction::priority),
				).apply(it, ::InsertAction)
			}
		}
	}
}
