package ru.dbotthepony.mc.otm.capability.matter

import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.NbtOps
import net.minecraft.nbt.Tag
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraftforge.registries.ForgeRegistries
import ru.dbotthepony.mc.otm.core.util.readBinaryJsonWithCodec
import ru.dbotthepony.mc.otm.core.util.writeBinaryJsonWithCodec
import ru.dbotthepony.mc.otm.data.UUIDCodec
import java.util.Optional
import java.util.UUID

data class ReplicationTask(
	val id: UUID,
	val patternId: Optional<UUID>,
	val item: Item,
	val inProgress: Int,
	val finished: Int,
	val required: Int
) {
	init {
		require(inProgress >= 0) { "Invalid inProgress value: $inProgress"}
		require(finished >= 0) { "Invalid finished value: $finished"}
		require(required >= 0) { "Invalid required value: $required"}
	}

	val total get() = inProgress + finished + required

	fun matchId(other: ReplicationTask): Boolean {
		return other.id == id
	}

	fun stack(count: Int = 1): ItemStack {
		return ItemStack(item, count)
	}

	fun allocate(amount: Int = 1): ReplicationTask {
		return ReplicationTask(id, patternId, item, inProgress + amount, finished, required - amount)
	}

	fun finish(amount: Int = 1): ReplicationTask {
		return ReplicationTask(id, patternId, item, inProgress - amount, finished + amount, required)
	}

	fun serializeNBT(): CompoundTag {
		return CODEC.encode(this, NbtOps.INSTANCE, NbtOps.INSTANCE.empty()).get().map({ it as CompoundTag }, { throw RuntimeException("Failed to serialize ReplicationTask: ${it.message()}") })
	}

	fun write(buff: FriendlyByteBuf) {
		buff.writeBinaryJsonWithCodec(CODEC, this)
	}

	companion object {
		fun deserializeNBT(nbt: Tag?): ReplicationTask? {
			return CODEC.decode(NbtOps.INSTANCE, nbt ?: return null).result().orElse(null)?.first
		}

		fun read(buff: FriendlyByteBuf): ReplicationTask {
			return buff.readBinaryJsonWithCodec(CODEC)
		}

		val CODEC: Codec<ReplicationTask> by lazy {
			RecordCodecBuilder.create {
				it.group(
					UUIDCodec.fieldOf("id").forGetter(ReplicationTask::id),
					UUIDCodec.optionalFieldOf("patternId").forGetter(ReplicationTask::patternId),
					ForgeRegistries.ITEMS.codec.fieldOf("item").forGetter(ReplicationTask::item),
					Codec.intRange(0, Int.MAX_VALUE).fieldOf("inProgress").forGetter(ReplicationTask::inProgress),
					Codec.intRange(0, Int.MAX_VALUE).fieldOf("finished").forGetter(ReplicationTask::finished),
					Codec.intRange(0, Int.MAX_VALUE).fieldOf("required").forGetter(ReplicationTask::required),
				).apply(it, ::ReplicationTask)
			}
		}
	}
}
